Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cp_model_loader.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <cmath>
18#include <cstdint>
19#include <limits>
20#include <memory>
21#include <numeric>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/btree_map.h"
27#include "absl/container/btree_set.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/log/check.h"
31#include "absl/meta/type_traits.h"
32#include "absl/strings/str_cat.h"
33#include "absl/types/span.h"
39#include "ortools/sat/circuit.h"
41#include "ortools/sat/cp_model.pb.h"
45#include "ortools/sat/diffn.h"
48#include "ortools/sat/integer.h"
52#include "ortools/sat/model.h"
56#include "ortools/sat/sat_parameters.pb.h"
60#include "ortools/sat/util.h"
64
65namespace operations_research {
66namespace sat {
67
68namespace {
69
70template <typename Values>
71std::vector<int64_t> ValuesFromProto(const Values& values) {
72 return std::vector<int64_t>(values.begin(), values.end());
73}
74
75void ComputeLinearBounds(const LinearConstraintProto& proto,
76 CpModelMapping* mapping, IntegerTrail* integer_trail,
77 int64_t* sum_min, int64_t* sum_max) {
78 *sum_min = 0;
79 *sum_max = 0;
80
81 for (int i = 0; i < proto.vars_size(); ++i) {
82 const int64_t coeff = proto.coeffs(i);
83 const IntegerVariable var = mapping->Integer(proto.vars(i));
84 const int64_t lb = integer_trail->LowerBound(var).value();
85 const int64_t ub = integer_trail->UpperBound(var).value();
86 if (coeff >= 0) {
87 (*sum_min) += coeff * lb;
88 (*sum_max) += coeff * ub;
89 } else {
90 (*sum_min) += coeff * ub;
91 (*sum_max) += coeff * lb;
92 }
93 }
94}
95
96// We check if the constraint is a sum(ax * xi) == value.
97bool ConstraintIsEq(const LinearConstraintProto& proto) {
98 return proto.domain_size() == 2 && proto.domain(0) == proto.domain(1);
99}
100
101// We check if the constraint is a sum(ax * xi) != value.
102bool ConstraintIsNEq(const LinearConstraintProto& proto,
103 CpModelMapping* mapping, IntegerTrail* integer_trail,
104 int64_t* single_value) {
105 int64_t sum_min = 0;
106 int64_t sum_max = 0;
107 ComputeLinearBounds(proto, mapping, integer_trail, &sum_min, &sum_max);
108
109 const Domain complement =
110 Domain(sum_min, sum_max)
111 .IntersectionWith(ReadDomainFromProto(proto).Complement());
112 if (complement.IsEmpty()) return false;
113 const int64_t value = complement.Min();
114
115 if (complement.Size() == 1) {
116 if (single_value != nullptr) {
117 *single_value = value;
118 }
119 return true;
120 }
121 return false;
122}
123
124} // namespace
125
126void LoadVariables(const CpModelProto& model_proto,
127 bool view_all_booleans_as_integers, Model* m) {
128 auto* mapping = m->GetOrCreate<CpModelMapping>();
129 const int num_proto_variables = model_proto.variables_size();
130
131 // All [0, 1] variables always have a corresponding Boolean, even if it is
132 // fixed to 0 (domain == [0,0]) or fixed to 1 (domain == [1,1]).
133 {
134 auto* sat_solver = m->GetOrCreate<SatSolver>();
135 CHECK_EQ(sat_solver->NumVariables(), 0);
136
137 BooleanVariable new_var(0);
138 std::vector<BooleanVariable> false_variables;
139 std::vector<BooleanVariable> true_variables;
140
141 mapping->booleans_.resize(num_proto_variables, kNoBooleanVariable);
142 mapping->reverse_boolean_map_.resize(num_proto_variables, -1);
143 for (int i = 0; i < num_proto_variables; ++i) {
144 const auto& domain = model_proto.variables(i).domain();
145 if (domain.size() != 2) continue;
146 if (domain[0] >= 0 && domain[1] <= 1) {
147 mapping->booleans_[i] = new_var;
148 mapping->reverse_boolean_map_[new_var] = i;
149 if (domain[1] == 0) {
150 false_variables.push_back(new_var);
151 } else if (domain[0] == 1) {
152 true_variables.push_back(new_var);
153 }
154 ++new_var;
155 }
156 }
157
158 sat_solver->SetNumVariables(new_var.value());
159 for (const BooleanVariable var : true_variables) {
160 m->Add(ClauseConstraint({sat::Literal(var, true)}));
161 }
162 for (const BooleanVariable var : false_variables) {
163 m->Add(ClauseConstraint({sat::Literal(var, false)}));
164 }
165 }
166
167 // Compute the list of positive variable reference for which we need to
168 // create an IntegerVariable.
169 std::vector<int> var_to_instantiate_as_integer;
170 if (view_all_booleans_as_integers) {
171 var_to_instantiate_as_integer.resize(num_proto_variables);
172 for (int i = 0; i < num_proto_variables; ++i) {
173 var_to_instantiate_as_integer[i] = i;
174 }
175 } else {
176 // Compute the integer variable references used by the model.
177 absl::flat_hash_set<int> used_variables;
178
179 const bool some_linerization =
180 m->GetOrCreate<SatParameters>()->linearization_level() > 0;
181
182 IndexReferences refs;
183 for (int c = 0; c < model_proto.constraints_size(); ++c) {
184 const ConstraintProto& ct = model_proto.constraints(c);
186 for (const int ref : refs.variables) {
187 used_variables.insert(PositiveRef(ref));
188 }
189
190 // We always add a linear relaxation for circuit/route except for
191 // linearization level zero.
192 if (some_linerization) {
193 if (ct.constraint_case() == ConstraintProto::kCircuit) {
194 for (const int ref : ct.circuit().literals()) {
195 used_variables.insert(PositiveRef(ref));
196 }
197 } else if (ct.constraint_case() == ConstraintProto::kRoutes) {
198 for (const int ref : ct.routes().literals()) {
199 used_variables.insert(PositiveRef(ref));
200 }
201 }
202 }
203 }
204
205 // Add the objectives variables that needs to be referenceable as integer
206 // even if they are only used as Booleans.
207 if (model_proto.has_objective()) {
208 for (const int obj_var : model_proto.objective().vars()) {
209 used_variables.insert(PositiveRef(obj_var));
210 }
211 }
212
213 // Make sure any unused variable, that is not already a Boolean is
214 // considered "used".
215 for (int i = 0; i < num_proto_variables; ++i) {
216 if (mapping->booleans_[i] == kNoBooleanVariable) {
217 used_variables.insert(i);
218 }
219 }
220
221 // We want the variable in the problem order.
222 var_to_instantiate_as_integer.assign(used_variables.begin(),
223 used_variables.end());
224 gtl::STLSortAndRemoveDuplicates(&var_to_instantiate_as_integer);
225 }
226 mapping->integers_.resize(num_proto_variables, kNoIntegerVariable);
227
228 // It is important for memory usage to reserve tight vector has we have many
229 // indexed by IntegerVariable. Unfortunately, we create intermediate
230 // IntegerVariable while loading large linear constraint, or when we have
231 // disjoint LP component. So this is a best effort at a tight upper bound.
232 int reservation_size = var_to_instantiate_as_integer.size();
233 for (const ConstraintProto& ct : model_proto.constraints()) {
234 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
235 const int ct_size = ct.linear().vars().size();
236 if (ct_size > 100) {
237 reservation_size += static_cast<int>(std::round(std::sqrt(ct_size)));
238 }
239 }
240 if (model_proto.has_objective()) {
241 reservation_size += 1; // Objective var.
242 const int ct_size = model_proto.objective().vars().size() + 1;
243 if (ct_size > 100) {
244 reservation_size += static_cast<int>(std::round(std::sqrt(ct_size)));
245 }
246 }
247
248 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
249 integer_trail->ReserveSpaceForNumVariables(reservation_size);
250 m->GetOrCreate<GenericLiteralWatcher>()->ReserveSpaceForNumVariables(
251 reservation_size);
252 mapping->reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(),
253 -1);
254 for (const int i : var_to_instantiate_as_integer) {
255 const auto& var_proto = model_proto.variables(i);
256 mapping->integers_[i] =
257 integer_trail->AddIntegerVariable(ReadDomainFromProto(var_proto));
258 DCHECK_LT(mapping->integers_[i], mapping->reverse_integer_map_.size());
259 mapping->reverse_integer_map_[mapping->integers_[i]] = i;
260 }
261
262 auto* encoder = m->GetOrCreate<IntegerEncoder>();
263 auto* intervals_repository = m->GetOrCreate<IntervalsRepository>();
264
265 // Link any variable that has both views.
266 for (int i = 0; i < num_proto_variables; ++i) {
267 if (mapping->integers_[i] == kNoIntegerVariable) continue;
268 if (mapping->booleans_[i] == kNoBooleanVariable) continue;
269
270 // Associate with corresponding integer variable.
271 encoder->AssociateToIntegerEqualValue(
272 sat::Literal(mapping->booleans_[i], true), mapping->integers_[i],
273 IntegerValue(1));
274 }
275
276 // Create the interval variables.
277 mapping->intervals_.resize(model_proto.constraints_size(),
279 for (int c = 0; c < model_proto.constraints_size(); ++c) {
280 const ConstraintProto& ct = model_proto.constraints(c);
281 if (ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
282 continue;
283 }
285 const sat::Literal enforcement_literal =
286 mapping->Literal(ct.enforcement_literal(0));
287 // TODO(user): Fix the constant variable situation. An optional interval
288 // with constant start/end or size cannot share the same constant
289 // variable if it is used in non-optional situation.
290 mapping->intervals_[c] = intervals_repository->CreateInterval(
291 mapping->Affine(ct.interval().start()),
292 mapping->Affine(ct.interval().end()),
293 mapping->Affine(ct.interval().size()), enforcement_literal.Index(),
294 /*add_linear_relation=*/false);
295 } else {
296 mapping->intervals_[c] = intervals_repository->CreateInterval(
297 mapping->Affine(ct.interval().start()),
298 mapping->Affine(ct.interval().end()),
299 mapping->Affine(ct.interval().size()), kNoLiteralIndex,
300 /*add_linear_relation=*/false);
301 }
302 mapping->already_loaded_ct_.insert(&ct);
303 }
304}
305
306void LoadBooleanSymmetries(const CpModelProto& model_proto, Model* m) {
307 auto* mapping = m->GetOrCreate<CpModelMapping>();
308 const SymmetryProto& symmetry = model_proto.symmetry();
309 if (symmetry.permutations().empty()) return;
310
311 // We currently can only use symmetry that touch a subset of variables.
312 const int num_vars = model_proto.variables().size();
313 std::vector<bool> can_be_used_in_symmetry(num_vars, true);
314
315 // First, we currently only support loading symmetry between Booleans.
316 for (int v = 0; v < num_vars; ++v) {
317 if (!mapping->IsBoolean(v)) can_be_used_in_symmetry[v] = false;
318 }
319
320 // Tricky: Moreover, some constraint will causes extra Boolean to be created
321 // and linked with the Boolean in the constraints. We can't use any of the
322 // symmetry that touch these since we potentially miss the component that will
323 // map these extra Booleans between each other.
324 //
325 // TODO(user): We could add these extra Boolean during expansion/presolve so
326 // that we have the symmetry involing them. Or maybe comes up with a different
327 // solution.
328 const int num_constraints = model_proto.constraints().size();
329 for (int c = 0; c < num_constraints; ++c) {
330 const ConstraintProto& ct = model_proto.constraints(c);
331 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
332 if (ct.linear().domain().size() <= 2) continue;
333
334 // A linear with a complex domain might need extra Booleans to be loaded.
335 // Note that it should be fine for the Boolean(s) in enforcement_literal
336 // though.
337 for (const int ref : ct.linear().vars()) {
338 can_be_used_in_symmetry[PositiveRef(ref)] = false;
339 }
340 }
341
342 auto* sat_solver = m->GetOrCreate<SatSolver>();
343 auto* symmetry_handler = m->GetOrCreate<SymmetryPropagator>();
344 sat_solver->AddPropagator(symmetry_handler);
345 const int num_literals = 2 * sat_solver->NumVariables();
346
347 for (const SparsePermutationProto& perm : symmetry.permutations()) {
348 bool can_be_used = true;
349 for (const int var : perm.support()) {
350 if (!can_be_used_in_symmetry[var]) {
351 can_be_used = false;
352 break;
353 }
354 }
355 if (!can_be_used) continue;
356
357 // Convert the variable symmetry to a "literal" one.
358 auto literal_permutation =
359 std::make_unique<SparsePermutation>(num_literals);
360 int support_index = 0;
361 const int num_cycle = perm.cycle_sizes().size();
362 for (int i = 0; i < num_cycle; ++i) {
363 const int size = perm.cycle_sizes(i);
364 const int saved_support_index = support_index;
365 for (int j = 0; j < size; ++j) {
366 const int var = perm.support(support_index++);
367 literal_permutation->AddToCurrentCycle(
368 mapping->Literal(var).Index().value());
369 }
370 literal_permutation->CloseCurrentCycle();
371
372 // Note that we also need to add the corresponding cycle for the negated
373 // literals.
374 support_index = saved_support_index;
375 for (int j = 0; j < size; ++j) {
376 const int var = perm.support(support_index++);
377 literal_permutation->AddToCurrentCycle(
378 mapping->Literal(var).NegatedIndex().value());
379 }
380 literal_permutation->CloseCurrentCycle();
381 }
382 symmetry_handler->AddSymmetry(std::move(literal_permutation));
383 }
384
385 SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Added ",
386 symmetry_handler->num_permutations(),
387 " symmetry to the SAT solver.");
388}
389
390// The logic assumes that the linear constraints have been presolved, so that
391// equality with a domain bound have been converted to <= or >= and so that we
392// never have any trivial inequalities.
393//
394// TODO(user): Regroup/presolve two encoding like b => x > 2 and the same
395// Boolean b => x > 5. These shouldn't happen if we merge linear constraints.
396void ExtractEncoding(const CpModelProto& model_proto, Model* m) {
397 auto* mapping = m->GetOrCreate<CpModelMapping>();
398 auto* encoder = m->GetOrCreate<IntegerEncoder>();
399 auto* logger = m->GetOrCreate<SolverLogger>();
400 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
401 auto* sat_solver = m->GetOrCreate<SatSolver>();
402
403 // TODO(user): Debug what makes it unsat at this point.
404 if (sat_solver->ModelIsUnsat()) return;
405
406 // Detection of literal equivalent to (i_var == value). We collect all the
407 // half-reified constraint lit => equality or lit => inequality for a given
408 // variable, and we will later sort them to detect equivalence.
409 struct EqualityDetectionHelper {
410 const ConstraintProto* ct;
412 int64_t value;
413 bool is_equality; // false if != instead.
414
415 bool operator<(const EqualityDetectionHelper& o) const {
416 if (literal.Variable() == o.literal.Variable()) {
417 if (value == o.value) return is_equality && !o.is_equality;
418 return value < o.value;
419 }
420 return literal.Variable() < o.literal.Variable();
421 }
422 };
423 std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
424 model_proto.variables_size());
425
426 // TODO(user): We will re-add the same implied bounds during probing, so
427 // it might not be necessary to do that here. Also, it might be too early
428 // if some of the literal view used in the LP are created later, but that
429 // should be fixable via calls to implied_bounds->NotifyNewIntegerView().
430 auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
431 auto* detector = m->GetOrCreate<ProductDetector>();
432
433 // Detection of literal equivalent to (i_var >= bound). We also collect
434 // all the half-refied part and we will sort the vector for detection of the
435 // equivalence.
436 struct InequalityDetectionHelper {
437 const ConstraintProto* ct;
439 IntegerLiteral i_lit;
440
441 bool operator<(const InequalityDetectionHelper& o) const {
442 if (literal.Variable() == o.literal.Variable()) {
443 return i_lit.var < o.i_lit.var;
444 }
445 return literal.Variable() < o.literal.Variable();
446 }
447 };
448 std::vector<InequalityDetectionHelper> inequalities;
449
450 // Loop over all constraints and fill var_to_equalities and inequalities.
451 for (const ConstraintProto& ct : model_proto.constraints()) {
452 if (ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
453 continue;
454 }
455 if (ct.enforcement_literal().size() != 1) continue;
456 if (ct.linear().vars_size() != 1) continue;
457
458 // ct is a linear constraint with one term and one enforcement literal.
459 const sat::Literal enforcement_literal =
460 mapping->Literal(ct.enforcement_literal(0));
461 if (sat_solver->Assignment().LiteralIsFalse(enforcement_literal)) continue;
462
463 const int ref = ct.linear().vars(0);
464 const int var = PositiveRef(ref);
465
466 const Domain domain = ReadDomainFromProto(model_proto.variables(var));
467 const Domain domain_if_enforced =
468 ReadDomainFromProto(ct.linear())
469 .InverseMultiplicationBy(ct.linear().coeffs(0) *
470 (RefIsPositive(ref) ? 1 : -1));
471
472 if (domain_if_enforced.IsEmpty()) {
473 if (!sat_solver->AddUnitClause(enforcement_literal.Negated())) return;
474 continue;
475 }
476
477 // Detect enforcement_literal => (var >= value or var <= value).
478 if (domain_if_enforced.NumIntervals() == 1) {
479 if (domain_if_enforced.Max() >= domain.Max() &&
480 domain_if_enforced.Min() > domain.Min()) {
481 inequalities.push_back({&ct, enforcement_literal,
483 mapping->Integer(var),
484 IntegerValue(domain_if_enforced.Min()))});
485 } else if (domain_if_enforced.Min() <= domain.Min() &&
486 domain_if_enforced.Max() < domain.Max()) {
487 inequalities.push_back({&ct, enforcement_literal,
489 mapping->Integer(var),
490 IntegerValue(domain_if_enforced.Max()))});
491 }
492 }
493
494 // Detect implied bounds. The test is less strict than the above
495 // test.
496 if (domain_if_enforced.Min() > domain.Min()) {
497 implied_bounds->Add(
498 enforcement_literal,
500 mapping->Integer(var), IntegerValue(domain_if_enforced.Min())));
501 }
502 if (domain_if_enforced.Max() < domain.Max()) {
503 implied_bounds->Add(
504 enforcement_literal,
505 IntegerLiteral::LowerOrEqual(mapping->Integer(var),
506 IntegerValue(domain_if_enforced.Max())));
507 }
508
509 // Detect enforcement_literal => (var == value or var != value).
510 //
511 // Note that for domain with 2 values like [0, 1], we will detect both ==
512 // 0 and != 1. Similarly, for a domain in [min, max], we should both
513 // detect (== min) and (<= min), and both detect (== max) and (>= max).
514 {
515 const Domain inter = domain.IntersectionWith(domain_if_enforced);
516 if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
517 if (inter.Min() == 0) {
518 detector->ProcessConditionalZero(enforcement_literal,
519 mapping->Integer(var));
520 }
521 var_to_equalities[var].push_back(
522 {&ct, enforcement_literal, inter.Min(), true});
523 }
524 }
525 {
526 const Domain inter =
527 domain.IntersectionWith(domain_if_enforced.Complement());
528 if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
529 var_to_equalities[var].push_back(
530 {&ct, enforcement_literal, inter.Min(), false});
531 }
532 }
533 }
534
535 // Detect Literal <=> X >= value
536 int num_inequalities = 0;
537 std::sort(inequalities.begin(), inequalities.end());
538 for (int i = 0; i + 1 < inequalities.size(); i++) {
539 if (inequalities[i].literal != inequalities[i + 1].literal.Negated()) {
540 continue;
541 }
542
543 // TODO(user): In these cases, we could fix the enforcement literal right
544 // away or ignore the constraint. Note that it will be done later anyway
545 // though.
546 if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
547 integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
548 continue;
549 }
550 if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
551 integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
552 continue;
553 }
554
555 const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
556 const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
557 if (pair_a.first == pair_b.second) {
558 ++num_inequalities;
559 encoder->AssociateToIntegerLiteral(inequalities[i].literal,
560 inequalities[i].i_lit);
561 mapping->already_loaded_ct_.insert(inequalities[i].ct);
562 mapping->already_loaded_ct_.insert(inequalities[i + 1].ct);
563 }
564 }
565
566 // Encode the half-inequalities.
567 int num_half_inequalities = 0;
568 for (const auto inequality : inequalities) {
569 if (mapping->ConstraintIsAlreadyLoaded(inequality.ct)) continue;
570 m->Add(
571 Implication(inequality.literal,
572 encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
573 if (sat_solver->ModelIsUnsat()) return;
574
575 ++num_half_inequalities;
576 mapping->already_loaded_ct_.insert(inequality.ct);
577 mapping->is_half_encoding_ct_.insert(inequality.ct);
578 }
579 if (!inequalities.empty()) {
580 SOLVER_LOG(logger, "[Encoding] ", num_inequalities,
581 " literals associated to VAR >= value, and ",
582 num_half_inequalities, " half-associations.");
583 }
584
585 // Detect Literal <=> X == value and associate them in the IntegerEncoder.
586 //
587 // TODO(user): Fully encode variable that are almost fully encoded?
588 int num_equalities = 0;
589 int num_half_equalities = 0;
590 int num_fully_encoded = 0;
591 int num_partially_encoded = 0;
592 for (int i = 0; i < var_to_equalities.size(); ++i) {
593 std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
594 std::sort(encoding.begin(), encoding.end());
595 if (encoding.empty()) continue;
596
597 absl::flat_hash_set<int64_t> values;
598 const IntegerVariable var = mapping->integers_[i];
599 for (int j = 0; j + 1 < encoding.size(); j++) {
600 if ((encoding[j].value != encoding[j + 1].value) ||
601 (encoding[j].literal != encoding[j + 1].literal.Negated()) ||
602 (encoding[j].is_equality != true) ||
603 (encoding[j + 1].is_equality != false)) {
604 continue;
605 }
606
607 ++num_equalities;
608 encoder->AssociateToIntegerEqualValue(encoding[j].literal, var,
609 IntegerValue(encoding[j].value));
610 mapping->already_loaded_ct_.insert(encoding[j].ct);
611 mapping->already_loaded_ct_.insert(encoding[j + 1].ct);
612 values.insert(encoding[j].value);
613 }
614
615 // TODO(user): Try to remove it. Normally we caught UNSAT above, but
616 // tests are very flaky (it only happens in parallel). Keeping it there for
617 // the time being.
618 if (sat_solver->ModelIsUnsat()) return;
619
620 // Encode the half-equalities.
621 //
622 // TODO(user): delay this after PropagateEncodingFromEquivalenceRelations()?
623 // Otherwise we might create new Boolean variables for no reason. Note
624 // however, that in the presolve, we should only use the "representative" in
625 // linear constraints, so we should be fine.
626 for (const auto equality : encoding) {
627 if (mapping->ConstraintIsAlreadyLoaded(equality.ct)) continue;
628 if (equality.is_equality) {
629 // If we have just an half-equality, lets not create the <=> literal
630 // but just add two implications. If we don't create hole, we don't
631 // really need the reverse literal. This way it is also possible for
632 // the ExtractElementEncoding() to detect later that actually this
633 // literal is <=> to var == value, and this way we create one less
634 // Boolean for the same result.
635 //
636 // TODO(user): It is not 100% clear what is the best encoding and if
637 // we should create equivalent literal or rely on propagator instead
638 // to push bounds.
639 m->Add(Implication(
640 equality.literal,
641 encoder->GetOrCreateAssociatedLiteral(
642 IntegerLiteral::GreaterOrEqual(var, equality.value))));
643 m->Add(Implication(
644 equality.literal,
645 encoder->GetOrCreateAssociatedLiteral(
646 IntegerLiteral::LowerOrEqual(var, equality.value))));
647 } else {
648 const Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
649 var, equality.value);
650 m->Add(Implication(equality.literal, eq.Negated()));
651 }
652
653 ++num_half_equalities;
654 mapping->already_loaded_ct_.insert(equality.ct);
655 mapping->is_half_encoding_ct_.insert(equality.ct);
656 }
657
658 // Update stats.
659 if (encoder->VariableIsFullyEncoded(var)) {
660 ++num_fully_encoded;
661 } else {
662 ++num_partially_encoded;
663 }
664 }
665
666 if (num_equalities > 0 || num_half_equalities > 0) {
667 SOLVER_LOG(logger, "[Encoding] ", num_equalities,
668 " literals associated to VAR == value, and ",
669 num_half_equalities, " half-associations.");
670 }
671 if (num_fully_encoded > 0) {
672 SOLVER_LOG(logger,
673 "[Encoding] num_fully_encoded_variables:", num_fully_encoded);
674 }
675 if (num_partially_encoded > 0) {
676 SOLVER_LOG(logger, "[Encoding] num_partially_encoded_variables:",
677 num_partially_encoded);
678 }
679}
680
681void ExtractElementEncoding(const CpModelProto& model_proto, Model* m) {
682 int num_element_encoded = 0;
683 auto* mapping = m->GetOrCreate<CpModelMapping>();
684 auto* logger = m->GetOrCreate<SolverLogger>();
685 auto* encoder = m->GetOrCreate<IntegerEncoder>();
686 auto* sat_solver = m->GetOrCreate<SatSolver>();
687 auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
688 auto* element_encodings = m->GetOrCreate<ElementEncodings>();
689
690 // Scan all exactly_one constraints and look for literal => var == value to
691 // detect element encodings.
692 int num_support_clauses = 0;
693 int num_dedicated_propagator = 0;
694 std::vector<Literal> clause;
695 std::vector<Literal> selectors;
696 std::vector<AffineExpression> exprs;
697 std::vector<AffineExpression> negated_exprs;
698 for (int c = 0; c < model_proto.constraints_size(); ++c) {
699 const ConstraintProto& ct = model_proto.constraints(c);
700 if (ct.constraint_case() != ConstraintProto::kExactlyOne) continue;
701
702 // Project the implied values onto each integer variable.
703 absl::btree_map<IntegerVariable, std::vector<ValueLiteralPair>>
704 var_to_value_literal_list;
705 for (const int l : ct.exactly_one().literals()) {
706 const Literal literal = mapping->Literal(l);
707 for (const auto& var_value : implied_bounds->GetImpliedValues(literal)) {
708 var_to_value_literal_list[var_value.first].push_back(
709 {var_value.second, literal});
710 }
711 }
712
713 // Used for logging only.
714 std::vector<IntegerVariable> encoded_variables;
715 std::string encoded_variables_str;
716
717 // Search for variable fully covered by the literals of the exactly_one.
718 for (auto& [var, encoding] : var_to_value_literal_list) {
719 if (encoding.size() < ct.exactly_one().literals_size()) {
720 VLOG(2) << "X" << var.value() << " has " << encoding.size()
721 << " implied values, and a domain of size "
723 ->InitialVariableDomain(var)
724 .Size();
725 continue;
726 }
727
728 // We use the order of literals of the exactly_one.
729 ++num_element_encoded;
730 element_encodings->Add(var, encoding, c);
731 if (VLOG_IS_ON(1)) {
732 encoded_variables.push_back(var);
733 absl::StrAppend(&encoded_variables_str, " X", var.value());
734 }
735
736 // Encode the holes propagation (but we don't create extra literal if they
737 // are not already there). If there are non-encoded values we also add the
738 // direct min/max propagation.
739 bool need_extra_propagation = false;
740 std::sort(encoding.begin(), encoding.end(),
742 for (int i = 0, j = 0; i < encoding.size(); i = j) {
743 j = i + 1;
744 const IntegerValue value = encoding[i].value;
745 while (j < encoding.size() && encoding[j].value == value) ++j;
746
747 if (j - i == 1) {
748 // Lets not create var >= value or var <= value if they do not exist.
749 if (!encoder->IsFixedOrHasAssociatedLiteral(
751 !encoder->IsFixedOrHasAssociatedLiteral(
753 need_extra_propagation = true;
754 continue;
755 }
756 encoder->AssociateToIntegerEqualValue(encoding[i].literal, var,
757 value);
758 } else {
759 // We do not create an extra literal if it doesn't exist.
760 if (encoder->GetAssociatedEqualityLiteral(var, value) ==
762 need_extra_propagation = true;
763 continue;
764 }
765
766 // If all literal supporting a value are false, then the value must be
767 // false. Note that such a clause is only useful if there are more
768 // than one literal supporting the value, otherwise we should already
769 // have detected the equivalence.
770 ++num_support_clauses;
771 clause.clear();
772 for (int k = i; k < j; ++k) clause.push_back(encoding[k].literal);
773 const Literal eq_lit =
774 encoder->GetOrCreateLiteralAssociatedToEquality(var, value);
775 clause.push_back(eq_lit.Negated());
776
777 // TODO(user): It should be safe otherwise the exactly_one will have
778 // duplicate literal, but I am not sure that if presolve is off we can
779 // assume that.
780 sat_solver->AddProblemClause(clause, /*is_safe=*/false);
781 }
782 }
783 if (need_extra_propagation) {
784 ++num_dedicated_propagator;
785 selectors.clear();
786 exprs.clear();
787 negated_exprs.clear();
788 for (const auto [value, literal] : encoding) {
789 selectors.push_back(literal);
790 exprs.push_back(AffineExpression(value));
791 negated_exprs.push_back(AffineExpression(-value));
792 }
793 auto* constraint =
794 new GreaterThanAtLeastOneOfPropagator(var, exprs, selectors, {}, m);
796 m->TakeOwnership(constraint);
797
798 // And the <= side.
799 constraint = new GreaterThanAtLeastOneOfPropagator(
800 NegationOf(var), negated_exprs, selectors, {}, m);
802 m->TakeOwnership(constraint);
803 }
804 }
805 if (encoded_variables.size() > 1 && VLOG_IS_ON(1)) {
806 VLOG(1) << "exactly_one(" << c << ") encodes " << encoded_variables.size()
807 << " variables at the same time: " << encoded_variables_str;
808 }
809 }
810
811 if (num_element_encoded > 0) {
812 SOLVER_LOG(logger,
813 "[Encoding] num_element_encoding: ", num_element_encoded);
814 }
815 if (num_support_clauses > 0) {
816 SOLVER_LOG(logger, "[Encoding] Added ", num_support_clauses,
817 " element support clauses, and ", num_dedicated_propagator,
818 " dedicated propagators.");
819 }
820}
821
822void PropagateEncodingFromEquivalenceRelations(const CpModelProto& model_proto,
823 Model* m) {
824 auto* mapping = m->GetOrCreate<CpModelMapping>();
825 auto* encoder = m->GetOrCreate<IntegerEncoder>();
826 auto* sat_solver = m->GetOrCreate<SatSolver>();
827
828 // Loop over all constraints and find affine ones.
829 int64_t num_associations = 0;
830 int64_t num_set_to_false = 0;
831 for (const ConstraintProto& ct : model_proto.constraints()) {
832 if (!ct.enforcement_literal().empty()) continue;
833 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
834 if (ct.linear().vars_size() != 2) continue;
835 if (!ConstraintIsEq(ct.linear())) continue;
836
837 const IntegerValue rhs(ct.linear().domain(0));
838
839 // Make sure the coefficient are positive.
840 IntegerVariable var1 = mapping->Integer(ct.linear().vars(0));
841 IntegerVariable var2 = mapping->Integer(ct.linear().vars(1));
842 IntegerValue coeff1(ct.linear().coeffs(0));
843 IntegerValue coeff2(ct.linear().coeffs(1));
844 if (coeff1 < 0) {
845 var1 = NegationOf(var1);
846 coeff1 = -coeff1;
847 }
848 if (coeff2 < 0) {
849 var2 = NegationOf(var2);
850 coeff2 = -coeff2;
851 }
852
853 // TODO(user): This is not supposed to happen, but apparently it did on
854 // once on routing_GCM_0001_sat.fzn. Investigate and fix.
855 if (coeff1 == 0 || coeff2 == 0) continue;
856
857 // We first map the >= literals.
858 // It is important to do that first, since otherwise mapping a == literal
859 // might creates the underlying >= and <= literals.
860 for (int i = 0; i < 2; ++i) {
861 for (const auto [value1, literal1] :
862 encoder->PartialGreaterThanEncoding(var1)) {
863 const IntegerValue bound2 = FloorRatio(rhs - value1 * coeff1, coeff2);
864 ++num_associations;
865 encoder->AssociateToIntegerLiteral(
866 literal1, IntegerLiteral::LowerOrEqual(var2, bound2));
867 }
868 std::swap(var1, var2);
869 std::swap(coeff1, coeff2);
870 }
871
872 // Same for the == literals.
873 //
874 // TODO(user): This is similar to LoadEquivalenceAC() for unreified
875 // constraints, but when the later is called, more encoding might have taken
876 // place.
877 for (int i = 0; i < 2; ++i) {
878 const auto copy = encoder->PartialDomainEncoding(var1);
879 for (const auto value_literal : copy) {
880 const IntegerValue value1 = value_literal.value;
881 const IntegerValue intermediate = rhs - value1 * coeff1;
882 if (intermediate % coeff2 != 0) {
883 // Using this function deals properly with UNSAT.
884 ++num_set_to_false;
885 if (!sat_solver->AddUnitClause(value_literal.literal.Negated())) {
886 return;
887 }
888 continue;
889 }
890 ++num_associations;
891 encoder->AssociateToIntegerEqualValue(value_literal.literal, var2,
892 intermediate / coeff2);
893 }
894 std::swap(var1, var2);
895 std::swap(coeff1, coeff2);
896 }
897 }
898
899 if (num_associations > 0) {
900 VLOG(1) << "Num associations from equivalences = " << num_associations;
901 }
902 if (num_set_to_false > 0) {
903 VLOG(1) << "Num literals set to false from equivalences = "
904 << num_set_to_false;
905 }
906}
907
908void DetectOptionalVariables(const CpModelProto& model_proto, Model* m) {
909 const SatParameters& parameters = *(m->GetOrCreate<SatParameters>());
910 if (!parameters.use_optional_variables()) return;
911 if (parameters.enumerate_all_solutions()) return;
912
913 // The variables from the objective cannot be marked as optional!
914 const int num_proto_variables = model_proto.variables_size();
915 std::vector<bool> already_seen(num_proto_variables, false);
916 if (model_proto.has_objective()) {
917 for (const int ref : model_proto.objective().vars()) {
918 already_seen[PositiveRef(ref)] = true;
919 }
920 }
921
922 // Compute for each variables the intersection of the enforcement literals
923 // of the constraints in which they appear.
924 //
925 // TODO(user): This deals with the simplest cases, but we could try to
926 // detect literals that implies all the constraints in which a variable
927 // appear to false. This can be done with a LCA computation in the tree of
928 // Boolean implication (once the presolve remove cycles). Not sure if we can
929 // properly exploit that afterwards though. Do some research!
930 std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
931 absl::btree_set<int> literals_set;
932 for (int c = 0; c < model_proto.constraints_size(); ++c) {
933 const ConstraintProto& ct = model_proto.constraints(c);
934 if (ct.enforcement_literal().empty()) {
935 for (const int var : UsedVariables(ct)) {
936 already_seen[var] = true;
937 enforcement_intersection[var].clear();
938 }
939 } else {
940 literals_set.clear();
941 literals_set.insert(ct.enforcement_literal().begin(),
942 ct.enforcement_literal().end());
943 for (const int var : UsedVariables(ct)) {
944 if (!already_seen[var]) {
945 enforcement_intersection[var].assign(ct.enforcement_literal().begin(),
946 ct.enforcement_literal().end());
947 } else {
948 // Take the intersection.
949 std::vector<int>& vector_ref = enforcement_intersection[var];
950 int new_size = 0;
951 for (const int literal : vector_ref) {
952 if (literals_set.contains(literal)) {
953 vector_ref[new_size++] = literal;
954 }
955 }
956 vector_ref.resize(new_size);
957 }
958 already_seen[var] = true;
959 }
960 }
961 }
962
963 // Auto-detect optional variables.
964 int num_optionals = 0;
965 for (int var = 0; var < num_proto_variables; ++var) {
966 const IntegerVariableProto& var_proto = model_proto.variables(var);
967 const int64_t min = var_proto.domain(0);
968 const int64_t max = var_proto.domain(var_proto.domain().size() - 1);
969 if (min == max) continue;
970 if (min == 0 && max == 1) continue;
971 if (enforcement_intersection[var].empty()) continue;
972
973 ++num_optionals;
974 }
975
976 if (num_optionals > 0) {
977 SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Auto-detected ", num_optionals,
978 " optional variables. Note that for now we DO NOT do anything "
979 "with this information.");
980 }
981}
982
983void AddFullEncodingFromSearchBranching(const CpModelProto& model_proto,
984 Model* m) {
985 if (model_proto.search_strategy().empty()) return;
986
987 auto* mapping = m->GetOrCreate<CpModelMapping>();
988 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
989 for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
990 if (strategy.domain_reduction_strategy() ==
991 DecisionStrategyProto::SELECT_MEDIAN_VALUE) {
992 for (const LinearExpressionProto& expr : strategy.exprs()) {
993 const int var = expr.vars(0);
994 if (!mapping->IsInteger(var)) continue;
995 const IntegerVariable variable = mapping->Integer(var);
996 if (!integer_trail->IsFixed(variable)) {
997 m->Add(FullyEncodeVariable(variable));
998 }
999 }
1000 }
1001 }
1002}
1003
1004// ============================================================================
1005// Constraint loading functions.
1006// ============================================================================
1007
1008void LoadBoolOrConstraint(const ConstraintProto& ct, Model* m) {
1009 auto* mapping = m->GetOrCreate<CpModelMapping>();
1010 auto* sat_solver = m->GetOrCreate<SatSolver>();
1011 std::vector<Literal> literals = mapping->Literals(ct.bool_or().literals());
1012 for (const int ref : ct.enforcement_literal()) {
1013 literals.push_back(mapping->Literal(ref).Negated());
1014 }
1015 sat_solver->AddProblemClause(literals, /*is_safe=*/false);
1016 if (literals.size() == 3) {
1017 m->GetOrCreate<ProductDetector>()->ProcessTernaryClause(literals);
1018 }
1019}
1020
1021void LoadBoolAndConstraint(const ConstraintProto& ct, Model* m) {
1022 auto* mapping = m->GetOrCreate<CpModelMapping>();
1023 std::vector<Literal> literals;
1024 for (const int ref : ct.enforcement_literal()) {
1025 literals.push_back(mapping->Literal(ref).Negated());
1026 }
1027 auto* sat_solver = m->GetOrCreate<SatSolver>();
1028 for (const Literal literal : mapping->Literals(ct.bool_and().literals())) {
1029 literals.push_back(literal);
1030 sat_solver->AddProblemClause(literals, /*is_safe=*/false);
1031 literals.pop_back();
1032 }
1033}
1034
1035void LoadAtMostOneConstraint(const ConstraintProto& ct, Model* m) {
1036 auto* mapping = m->GetOrCreate<CpModelMapping>();
1037 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1038 m->Add(AtMostOneConstraint(mapping->Literals(ct.at_most_one().literals())));
1039}
1040
1041void LoadExactlyOneConstraint(const ConstraintProto& ct, Model* m) {
1042 auto* mapping = m->GetOrCreate<CpModelMapping>();
1043 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1044 const auto& literals = mapping->Literals(ct.exactly_one().literals());
1045 m->Add(ExactlyOneConstraint(literals));
1046 if (literals.size() == 3) {
1047 m->GetOrCreate<ProductDetector>()->ProcessTernaryExactlyOne(literals);
1048 }
1049}
1050
1051void LoadBoolXorConstraint(const ConstraintProto& ct, Model* m) {
1052 auto* mapping = m->GetOrCreate<CpModelMapping>();
1053 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1054 m->Add(LiteralXorIs(mapping->Literals(ct.bool_xor().literals()), true));
1055}
1056
1057namespace {
1058
1059// Boolean encoding of:
1060// enforcement_literal => coeff1 * var1 + coeff2 * var2 == rhs;
1061void LoadEquivalenceAC(const std::vector<Literal> enforcement_literal,
1062 IntegerValue coeff1, IntegerVariable var1,
1063 IntegerValue coeff2, IntegerVariable var2,
1064 const IntegerValue rhs, Model* m) {
1065 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1066 CHECK(encoder->VariableIsFullyEncoded(var1));
1067 CHECK(encoder->VariableIsFullyEncoded(var2));
1068 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1069 for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
1070 term1_value_to_literal[coeff1 * value_literal.value] =
1071 value_literal.literal;
1072 }
1073 const auto copy = encoder->FullDomainEncoding(var2);
1074 for (const auto value_literal : copy) {
1075 const IntegerValue target = rhs - value_literal.value * coeff2;
1076 if (!term1_value_to_literal.contains(target)) {
1077 m->Add(EnforcedClause(enforcement_literal,
1078 {value_literal.literal.Negated()}));
1079 } else {
1080 const Literal target_literal = term1_value_to_literal[target];
1081 m->Add(EnforcedClause(enforcement_literal,
1082 {value_literal.literal.Negated(), target_literal}));
1083 m->Add(EnforcedClause(enforcement_literal,
1084 {value_literal.literal, target_literal.Negated()}));
1085
1086 // This "target" can never be reached again, so it is safe to remove it.
1087 // We do that so we know the term1 values that are never reached.
1088 term1_value_to_literal.erase(target);
1089 }
1090 }
1091
1092 // Exclude the values that can never be "matched" by coeff2 * var2.
1093 // We need the std::sort() to be deterministic!
1094 std::vector<Literal> implied_false;
1095 for (const auto entry : term1_value_to_literal) {
1096 implied_false.push_back(entry.second);
1097 }
1098 std::sort(implied_false.begin(), implied_false.end());
1099 for (const Literal l : implied_false) {
1100 m->Add(EnforcedClause(enforcement_literal, {l.Negated()}));
1101 }
1102}
1103
1104// Boolean encoding of:
1105// enforcement_literal => coeff1 * var1 + coeff2 * var2 != rhs;
1106void LoadEquivalenceNeqAC(const std::vector<Literal> enforcement_literal,
1107 IntegerValue coeff1, IntegerVariable var1,
1108 IntegerValue coeff2, IntegerVariable var2,
1109 const IntegerValue rhs, Model* m) {
1110 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1111 CHECK(encoder->VariableIsFullyEncoded(var1));
1112 CHECK(encoder->VariableIsFullyEncoded(var2));
1113 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1114 for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
1115 term1_value_to_literal[coeff1 * value_literal.value] =
1116 value_literal.literal;
1117 }
1118 const auto copy = encoder->FullDomainEncoding(var2);
1119 for (const auto value_literal : copy) {
1120 const IntegerValue target_value = rhs - value_literal.value * coeff2;
1121 const auto& it = term1_value_to_literal.find(target_value);
1122 if (it != term1_value_to_literal.end()) {
1123 const Literal target_literal = it->second;
1124 m->Add(EnforcedClause(
1125 enforcement_literal,
1126 {value_literal.literal.Negated(), target_literal.Negated()}));
1127 }
1128 }
1129}
1130
1131bool IsPartOfProductEncoding(const ConstraintProto& ct) {
1132 if (ct.enforcement_literal().size() != 1) return false;
1133 if (ct.linear().vars().size() > 2) return false;
1134 if (ct.linear().domain().size() != 2) return false;
1135 if (ct.linear().domain(0) != 0) return false;
1136 if (ct.linear().domain(1) != 0) return false;
1137 for (const int64_t coeff : ct.linear().coeffs()) {
1138 if (std::abs(coeff) != 1) return false;
1139 }
1140 return true;
1141}
1142
1143} // namespace
1144
1145// TODO(user): We could use a smarter way to determine buckets, like putting
1146// everyone with the same coeff together if possible and the split is ok.
1147void SplitAndLoadIntermediateConstraints(bool lb_required, bool ub_required,
1148 std::vector<IntegerVariable>* vars,
1149 std::vector<int64_t>* coeffs,
1150 Model* m) {
1151 // If we enumerate all solutions, then we want intermediate variables to be
1152 // tight independently of what side is required.
1153 if (m->GetOrCreate<SatParameters>()->enumerate_all_solutions()) {
1154 lb_required = true;
1155 ub_required = true;
1156 }
1157
1158 std::vector<IntegerVariable> bucket_sum_vars;
1159 std::vector<int64_t> bucket_sum_coeffs;
1160 std::vector<IntegerVariable> local_vars;
1161 std::vector<int64_t> local_coeffs;
1162
1163 int64_t i = 0;
1164 const int64_t num_vars = vars->size();
1165 const int64_t num_buckets = static_cast<int>(std::round(std::sqrt(num_vars)));
1166 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1167 for (int64_t b = 0; b < num_buckets; ++b) {
1168 local_vars.clear();
1169 local_coeffs.clear();
1170 int64_t bucket_lb = 0;
1171 int64_t bucket_ub = 0;
1172 int64_t gcd = 0;
1173 const int64_t limit = num_vars * (b + 1);
1174 for (; i * num_buckets < limit; ++i) {
1175 const IntegerVariable var = (*vars)[i];
1176 const int64_t coeff = (*coeffs)[i];
1177 gcd = std::gcd(gcd, std::abs(coeff));
1178 local_vars.push_back(var);
1179 local_coeffs.push_back(coeff);
1180 const int64_t term1 = coeff * integer_trail->LowerBound(var).value();
1181 const int64_t term2 = coeff * integer_trail->UpperBound(var).value();
1182 bucket_lb += std::min(term1, term2);
1183 bucket_ub += std::max(term1, term2);
1184 }
1185 if (gcd == 0) continue;
1186 if (gcd > 1) {
1187 // Everything should be exactly divisible!
1188 for (int64_t& ref : local_coeffs) ref /= gcd;
1189 bucket_lb /= gcd;
1190 bucket_ub /= gcd;
1191 }
1192
1193 const IntegerVariable bucket_sum =
1194 integer_trail->AddIntegerVariable(bucket_lb, bucket_ub);
1195 bucket_sum_vars.push_back(bucket_sum);
1196 bucket_sum_coeffs.push_back(gcd);
1197 local_vars.push_back(bucket_sum);
1198 local_coeffs.push_back(-1);
1199
1200 if (lb_required) {
1201 // We have sum bucket_var >= lb, so we need local_vars >= bucket_var.
1202 m->Add(WeightedSumGreaterOrEqual(local_vars, local_coeffs, 0));
1203 }
1204 if (ub_required) {
1205 // Similarly, bucket_var <= ub, so we need local_vars <= bucket_var
1206 m->Add(WeightedSumLowerOrEqual(local_vars, local_coeffs, 0));
1207 }
1208 }
1209 *vars = bucket_sum_vars;
1210 *coeffs = bucket_sum_coeffs;
1211}
1212
1213void LoadLinearConstraint(const ConstraintProto& ct, Model* m) {
1214 auto* mapping = m->GetOrCreate<CpModelMapping>();
1215 if (ct.linear().vars().empty()) {
1216 const Domain rhs = ReadDomainFromProto(ct.linear());
1217 if (rhs.Contains(0)) return;
1219 std::vector<Literal> clause;
1220 for (const int ref : ct.enforcement_literal()) {
1221 clause.push_back(mapping->Literal(ref).Negated());
1222 }
1223 m->Add(ClauseConstraint(clause));
1224 } else {
1225 VLOG(1) << "Trivially UNSAT constraint: " << ct;
1226 m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1227 }
1228 return;
1229 }
1230
1231 if (IsPartOfProductEncoding(ct)) {
1232 const Literal l = mapping->Literal(ct.enforcement_literal(0));
1233 auto* detector = m->GetOrCreate<ProductDetector>();
1234 if (ct.linear().vars().size() == 1) {
1235 // TODO(user): Actually this should never be called since we process
1236 // linear1 in ExtractEncoding().
1237 detector->ProcessConditionalZero(l,
1238 mapping->Integer(ct.linear().vars(0)));
1239 } else if (ct.linear().vars().size() == 2) {
1240 const IntegerVariable x = mapping->Integer(ct.linear().vars(0));
1241 const IntegerVariable y = mapping->Integer(ct.linear().vars(1));
1242 detector->ProcessConditionalEquality(
1243 l, x,
1244 ct.linear().coeffs(0) == ct.linear().coeffs(1) ? NegationOf(y) : y);
1245 }
1246 }
1247
1248 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1249 std::vector<IntegerVariable> vars = mapping->Integers(ct.linear().vars());
1250 std::vector<int64_t> coeffs = ValuesFromProto(ct.linear().coeffs());
1251
1252 // Compute the min/max to relax the bounds if needed.
1253 //
1254 // TODO(user): Reuse ComputeLinearBounds()? but then we need another loop
1255 // to detect if we only have Booleans.
1256 IntegerValue min_sum(0);
1257 IntegerValue max_sum(0);
1258 IntegerValue max_domain_size(0);
1259 bool all_booleans = true;
1260 for (int i = 0; i < vars.size(); ++i) {
1261 if (all_booleans && !mapping->IsBoolean(ct.linear().vars(i))) {
1262 all_booleans = false;
1263 }
1264 const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1265 const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1266 max_domain_size = std::max(max_domain_size, ub - lb + 1);
1267 const IntegerValue term_a = coeffs[i] * lb;
1268 const IntegerValue term_b = coeffs[i] * ub;
1269 min_sum += std::min(term_a, term_b);
1270 max_sum += std::max(term_a, term_b);
1271 }
1272
1273 // Load conditional precedences.
1274 const SatParameters& params = *m->GetOrCreate<SatParameters>();
1275 if (params.auto_detect_greater_than_at_least_one_of() &&
1276 ct.enforcement_literal().size() == 1 && vars.size() <= 2) {
1277 // To avoid overflow in the code below, we tighten the bounds.
1278 int64_t rhs_min = ct.linear().domain(0);
1279 int64_t rhs_max = ct.linear().domain(ct.linear().domain().size() - 1);
1280 rhs_min = std::max(rhs_min, min_sum.value());
1281 rhs_max = std::min(rhs_max, max_sum.value());
1282
1283 auto* detector = m->GetOrCreate<GreaterThanAtLeastOneOfDetector>();
1284 const Literal lit = mapping->Literal(ct.enforcement_literal(0));
1285 const Domain domain = ReadDomainFromProto(ct.linear());
1286 if (vars.size() == 1) {
1287 detector->Add(lit, {vars[0], coeffs[0]}, {}, rhs_min, rhs_max);
1288 } else if (vars.size() == 2) {
1289 detector->Add(lit, {vars[0], coeffs[0]}, {vars[1], coeffs[1]}, rhs_min,
1290 rhs_max);
1291 }
1292 }
1293
1294 // Load precedences.
1295 if (!HasEnforcementLiteral(ct)) {
1296 auto* precedences = m->GetOrCreate<PrecedenceRelations>();
1297
1298 // To avoid overflow in the code below, we tighten the bounds.
1299 // Note that we detect and do not add trivial relation.
1300 int64_t rhs_min = ct.linear().domain(0);
1301 int64_t rhs_max = ct.linear().domain(ct.linear().domain().size() - 1);
1302 rhs_min = std::max(rhs_min, min_sum.value());
1303 rhs_max = std::min(rhs_max, max_sum.value());
1304
1305 if (vars.size() == 2) {
1306 if (std::abs(coeffs[0]) == std::abs(coeffs[1])) {
1307 const int64_t magnitude = std::abs(coeffs[0]);
1308 IntegerVariable v1 = vars[0];
1309 IntegerVariable v2 = vars[1];
1310 if (coeffs[0] < 0) v1 = NegationOf(v1);
1311 if (coeffs[1] > 0) v2 = NegationOf(v2);
1312
1313 // magnitude * v1 <= magnitude * v2 + rhs_max.
1314 precedences->Add(v1, v2, CeilOfRatio(-rhs_max, magnitude));
1315
1316 // magnitude * v1 >= magnitude * v2 + rhs_min.
1317 precedences->Add(v2, v1, CeilOfRatio(rhs_min, magnitude));
1318 }
1319 } else if (vars.size() == 3) {
1320 for (int i = 0; i < 3; ++i) {
1321 for (int j = 0; j < 3; ++j) {
1322 if (i == j) continue;
1323 if (std::abs(coeffs[i]) != std::abs(coeffs[j])) continue;
1324 const int other = 3 - i - j; // i + j + other = 0 + 1 + 2.
1325
1326 // Make the terms magnitude * v1 - magnitude * v2 ...
1327 const int64_t magnitude = std::abs(coeffs[i]);
1328 IntegerVariable v1 = vars[i];
1329 IntegerVariable v2 = vars[j];
1330 if (coeffs[i] < 0) v1 = NegationOf(v1);
1331 if (coeffs[j] > 0) v2 = NegationOf(v2);
1332
1333 // magnitude * v1 + other_lb <= magnitude * v2 + rhs_max
1334 const int64_t coeff = coeffs[other];
1335 const int64_t other_lb =
1336 coeff > 0
1337 ? coeff * integer_trail->LowerBound(vars[other]).value()
1338 : coeff * integer_trail->UpperBound(vars[other]).value();
1339 precedences->Add(v1, v2, CeilOfRatio(other_lb - rhs_max, magnitude));
1340
1341 // magnitude * v1 + other_ub >= magnitude * v2 + rhs_min
1342 const int64_t other_ub =
1343 coeff > 0
1344 ? coeff * integer_trail->UpperBound(vars[other]).value()
1345 : coeff * integer_trail->LowerBound(vars[other]).value();
1346 precedences->Add(v2, v1, CeilOfRatio(rhs_min - other_ub, magnitude));
1347 }
1348 }
1349 }
1350 }
1351
1352 const IntegerValue domain_size_limit(
1353 params.max_domain_size_when_encoding_eq_neq_constraints());
1354 if (ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1355 !integer_trail->IsFixed(vars[1]) &&
1356 max_domain_size <= domain_size_limit) {
1357 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1358 if (params.boolean_encoding_level() > 0 && ConstraintIsEq(ct.linear()) &&
1359 ct.linear().domain(0) != min_sum && ct.linear().domain(0) != max_sum &&
1360 encoder->VariableIsFullyEncoded(vars[0]) &&
1361 encoder->VariableIsFullyEncoded(vars[1])) {
1362 VLOG(3) << "Load AC version of " << ct << ", var0 domain = "
1363 << integer_trail->InitialVariableDomain(vars[0])
1364 << ", var1 domain = "
1365 << integer_trail->InitialVariableDomain(vars[1]);
1366 return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()),
1367 IntegerValue(coeffs[0]), vars[0],
1368 IntegerValue(coeffs[1]), vars[1],
1369 IntegerValue(ct.linear().domain(0)), m);
1370 }
1371
1372 int64_t single_value = 0;
1373 if (params.boolean_encoding_level() > 0 &&
1374 ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) &&
1375 single_value != min_sum && single_value != max_sum &&
1376 encoder->VariableIsFullyEncoded(vars[0]) &&
1377 encoder->VariableIsFullyEncoded(vars[1])) {
1378 VLOG(3) << "Load NAC version of " << ct << ", var0 domain = "
1379 << integer_trail->InitialVariableDomain(vars[0])
1380 << ", var1 domain = "
1381 << integer_trail->InitialVariableDomain(vars[1])
1382 << ", value = " << single_value;
1383 return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()),
1384 IntegerValue(coeffs[0]), vars[0],
1385 IntegerValue(coeffs[1]), vars[1],
1386 IntegerValue(single_value), m);
1387 }
1388 }
1389
1390 // Note that the domain/enforcement of the main constraint do not change.
1391 // Same for the min/sum and max_sum. The intermediate variables are always
1392 // equal to the intermediate sum, independently of the enforcement.
1393 const bool pseudo_boolean = !HasEnforcementLiteral(ct) &&
1394 ct.linear().domain_size() == 2 && all_booleans;
1395 if (!pseudo_boolean &&
1396 ct.linear().vars().size() > params.linear_split_size()) {
1397 const auto& domain = ct.linear().domain();
1399 domain.size() > 2 || min_sum < domain[0],
1400 domain.size() > 2 || max_sum > domain[1], &vars, &coeffs, m);
1401 }
1402
1403 if (ct.linear().domain_size() == 2) {
1404 const int64_t lb = ct.linear().domain(0);
1405 const int64_t ub = ct.linear().domain(1);
1406 const std::vector<Literal> enforcement_literals =
1407 mapping->Literals(ct.enforcement_literal());
1408 if (all_booleans && enforcement_literals.empty()) {
1409 // TODO(user): we should probably also implement an
1410 // half-reified version of this constraint.
1411 std::vector<LiteralWithCoeff> cst;
1412 for (int i = 0; i < vars.size(); ++i) {
1413 const int ref = ct.linear().vars(i);
1414 cst.push_back({mapping->Literal(ref), coeffs[i]});
1415 }
1416 m->GetOrCreate<SatSolver>()->AddLinearConstraint(
1417 /*use_lower_bound=*/(min_sum < lb), lb,
1418 /*use_upper_bound=*/(max_sum > ub), ub, &cst);
1419 } else {
1420 if (min_sum < lb) {
1421 AddWeightedSumGreaterOrEqual(enforcement_literals, vars, coeffs, lb, m);
1422 }
1423 if (max_sum > ub) {
1424 AddWeightedSumLowerOrEqual(enforcement_literals, vars, coeffs, ub, m);
1425 }
1426 }
1427 return;
1428 }
1429
1430 // We have a linear with a complex Domain, we need to create extra Booleans.
1431
1432 // In this case, we can create just one Boolean instead of two since one
1433 // is the negation of the other.
1434 const bool special_case =
1435 ct.enforcement_literal().empty() && ct.linear().domain_size() == 4;
1436
1437 // For enforcement => var \in domain, we can potentially reuse the encoding
1438 // literal directly rather than creating new ones.
1439 const bool is_linear1 = !special_case && vars.size() == 1 && coeffs[0] == 1;
1440
1441 std::vector<Literal> clause;
1442 std::vector<Literal> for_enumeration;
1443 auto* encoding = m->GetOrCreate<IntegerEncoder>();
1444 for (int i = 0; i < ct.linear().domain_size(); i += 2) {
1445 const int64_t lb = ct.linear().domain(i);
1446 const int64_t ub = ct.linear().domain(i + 1);
1447
1448 if (is_linear1) {
1449 if (lb == ub) {
1450 clause.push_back(
1451 encoding->GetOrCreateLiteralAssociatedToEquality(vars[0], lb));
1452 continue;
1453 } else if (min_sum >= lb) {
1454 clause.push_back(encoding->GetOrCreateAssociatedLiteral(
1455 IntegerLiteral::LowerOrEqual(vars[0], ub)));
1456 continue;
1457 } else if (max_sum <= ub) {
1458 clause.push_back(encoding->GetOrCreateAssociatedLiteral(
1459 IntegerLiteral::GreaterOrEqual(vars[0], lb)));
1460 continue;
1461 }
1462 }
1463
1464 const Literal subdomain_literal(
1465 special_case && i > 0 ? clause.back().Negated()
1466 : Literal(m->Add(NewBooleanVariable()), true));
1467 clause.push_back(subdomain_literal);
1468 for_enumeration.push_back(subdomain_literal);
1469
1470 if (min_sum < lb) {
1471 AddWeightedSumGreaterOrEqual({subdomain_literal}, vars, coeffs, lb, m);
1472 }
1473 if (max_sum > ub) {
1474 AddWeightedSumLowerOrEqual({subdomain_literal}, vars, coeffs, ub, m);
1475 }
1476 }
1477
1478 const std::vector<Literal> enforcement_literals =
1479 mapping->Literals(ct.enforcement_literal());
1480
1481 // Make sure all booleans are tights when enumerating all solutions.
1482 if (params.enumerate_all_solutions() && !enforcement_literals.empty()) {
1483 Literal linear_is_enforced;
1484 if (enforcement_literals.size() == 1) {
1485 linear_is_enforced = enforcement_literals[0];
1486 } else {
1487 linear_is_enforced = Literal(m->Add(NewBooleanVariable()), true);
1488 std::vector<Literal> maintain_linear_is_enforced;
1489 for (const Literal e_lit : enforcement_literals) {
1490 m->Add(Implication(e_lit.Negated(), linear_is_enforced.Negated()));
1491 maintain_linear_is_enforced.push_back(e_lit.Negated());
1492 }
1493 maintain_linear_is_enforced.push_back(linear_is_enforced);
1494 m->Add(ClauseConstraint(maintain_linear_is_enforced));
1495 }
1496 for (const Literal lit : for_enumeration) {
1497 m->Add(Implication(linear_is_enforced.Negated(), lit.Negated()));
1498 if (special_case) break; // For the unique Boolean var to be false.
1499 }
1500 }
1501
1502 if (!special_case) {
1503 for (const Literal e_lit : enforcement_literals) {
1504 clause.push_back(e_lit.Negated());
1505 }
1506 m->Add(ClauseConstraint(clause));
1507 }
1508}
1509
1510void LoadAllDiffConstraint(const ConstraintProto& ct, Model* m) {
1511 auto* mapping = m->GetOrCreate<CpModelMapping>();
1512 const std::vector<AffineExpression> expressions =
1513 mapping->Affines(ct.all_diff().exprs());
1514 m->Add(AllDifferentOnBounds(expressions));
1515}
1516
1517void LoadIntProdConstraint(const ConstraintProto& ct, Model* m) {
1518 auto* mapping = m->GetOrCreate<CpModelMapping>();
1519 const AffineExpression prod = mapping->Affine(ct.int_prod().target());
1520 std::vector<AffineExpression> terms;
1521 for (const LinearExpressionProto& expr : ct.int_prod().exprs()) {
1522 terms.push_back(mapping->Affine(expr));
1523 }
1524 switch (terms.size()) {
1525 case 0: {
1526 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1527 auto* sat_solver = m->GetOrCreate<SatSolver>();
1528 if (!integer_trail->Enqueue(prod.LowerOrEqual(1)) ||
1529 !integer_trail->Enqueue(prod.GreaterOrEqual(1))) {
1530 sat_solver->NotifyThatModelIsUnsat();
1531 }
1532 break;
1533 }
1534 case 1: {
1535 LinearConstraintBuilder builder(m, /*lb=*/0, /*ub=*/0);
1536 builder.AddTerm(prod, 1);
1537 builder.AddTerm(terms[0], -1);
1538 LoadLinearConstraint(builder.Build(), m);
1539 break;
1540 }
1541 case 2: {
1542 m->Add(ProductConstraint(terms[0], terms[1], prod));
1543 break;
1544 }
1545 default: {
1546 LOG(FATAL) << "Loading int_prod with arity > 2, should not be here.";
1547 break;
1548 }
1549 }
1550}
1551
1552void LoadIntDivConstraint(const ConstraintProto& ct, Model* m) {
1553 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1554 auto* mapping = m->GetOrCreate<CpModelMapping>();
1555 const AffineExpression div = mapping->Affine(ct.int_div().target());
1556 const AffineExpression num = mapping->Affine(ct.int_div().exprs(0));
1557 const AffineExpression denom = mapping->Affine(ct.int_div().exprs(1));
1558 if (integer_trail->IsFixed(denom)) {
1559 m->Add(FixedDivisionConstraint(num, integer_trail->FixedValue(denom), div));
1560 } else {
1561 if (VLOG_IS_ON(1)) {
1562 LinearConstraintBuilder builder(m);
1563 if (m->GetOrCreate<ProductDecomposer>()->TryToLinearize(num, denom,
1564 &builder)) {
1565 VLOG(1) << "Division " << ct << " can be linearized";
1566 }
1567 }
1568 m->Add(DivisionConstraint(num, denom, div));
1569 }
1570}
1571
1572void LoadIntModConstraint(const ConstraintProto& ct, Model* m) {
1573 auto* mapping = m->GetOrCreate<CpModelMapping>();
1574 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1575
1576 const AffineExpression target = mapping->Affine(ct.int_mod().target());
1577 const AffineExpression expr = mapping->Affine(ct.int_mod().exprs(0));
1578 const AffineExpression mod = mapping->Affine(ct.int_mod().exprs(1));
1579 CHECK(integer_trail->IsFixed(mod));
1580 const IntegerValue fixed_modulo = integer_trail->FixedValue(mod);
1581 m->Add(FixedModuloConstraint(expr, fixed_modulo, target));
1582}
1583
1584void LoadLinMaxConstraint(const ConstraintProto& ct, Model* m) {
1585 if (ct.lin_max().exprs().empty()) {
1586 m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1587 return;
1588 }
1589
1590 auto* mapping = m->GetOrCreate<CpModelMapping>();
1591 const LinearExpression max = mapping->GetExprFromProto(ct.lin_max().target());
1592 std::vector<LinearExpression> negated_exprs;
1593 negated_exprs.reserve(ct.lin_max().exprs_size());
1594 for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
1595 negated_exprs.push_back(
1596 NegationOf(mapping->GetExprFromProto(ct.lin_max().exprs(i))));
1597 }
1598 // TODO(user): Consider replacing the min propagator by max.
1599 m->Add(IsEqualToMinOf(NegationOf(max), negated_exprs));
1600}
1601
1602void LoadNoOverlapConstraint(const ConstraintProto& ct, Model* m) {
1603 auto* mapping = m->GetOrCreate<CpModelMapping>();
1604 AddDisjunctive(mapping->Intervals(ct.no_overlap().intervals()), m);
1605}
1606
1607void LoadNoOverlap2dConstraint(const ConstraintProto& ct, Model* m) {
1608 if (ct.no_overlap_2d().x_intervals().empty()) return;
1609 auto* mapping = m->GetOrCreate<CpModelMapping>();
1610 const std::vector<IntervalVariable> x_intervals =
1611 mapping->Intervals(ct.no_overlap_2d().x_intervals());
1612 const std::vector<IntervalVariable> y_intervals =
1613 mapping->Intervals(ct.no_overlap_2d().y_intervals());
1614 AddNonOverlappingRectangles(x_intervals, y_intervals, m);
1615}
1616
1617void LoadCumulativeConstraint(const ConstraintProto& ct, Model* m) {
1618 auto* mapping = m->GetOrCreate<CpModelMapping>();
1619 const std::vector<IntervalVariable> intervals =
1620 mapping->Intervals(ct.cumulative().intervals());
1621 const AffineExpression capacity = mapping->Affine(ct.cumulative().capacity());
1622 const std::vector<AffineExpression> demands =
1623 mapping->Affines(ct.cumulative().demands());
1624 m->Add(Cumulative(intervals, demands, capacity));
1625}
1626
1627void LoadReservoirConstraint(const ConstraintProto& ct, Model* m) {
1628 auto* mapping = m->GetOrCreate<CpModelMapping>();
1629 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1630 const std::vector<AffineExpression> times =
1631 mapping->Affines(ct.reservoir().time_exprs());
1632 const std::vector<AffineExpression> level_changes =
1633 mapping->Affines(ct.reservoir().level_changes());
1634 std::vector<Literal> presences;
1635 const int size = ct.reservoir().time_exprs().size();
1636 for (int i = 0; i < size; ++i) {
1637 if (!ct.reservoir().active_literals().empty()) {
1638 presences.push_back(mapping->Literal(ct.reservoir().active_literals(i)));
1639 } else {
1640 presences.push_back(encoder->GetTrueLiteral());
1641 }
1642 }
1643 AddReservoirConstraint(times, level_changes, presences,
1644 ct.reservoir().min_level(), ct.reservoir().max_level(),
1645 m);
1646}
1647
1648void LoadCircuitConstraint(const ConstraintProto& ct, Model* m) {
1649 const auto& circuit = ct.circuit();
1650 if (circuit.tails().empty()) return;
1651
1652 std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1653 std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1654 std::vector<Literal> literals =
1655 m->GetOrCreate<CpModelMapping>()->Literals(circuit.literals());
1656 const int num_nodes = ReindexArcs(&tails, &heads);
1657 LoadSubcircuitConstraint(num_nodes, tails, heads, literals, m);
1658}
1659
1660void LoadRoutesConstraint(const ConstraintProto& ct, Model* m) {
1661 const auto& routes = ct.routes();
1662 if (routes.tails().empty()) return;
1663
1664 std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1665 std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1666 std::vector<Literal> literals =
1667 m->GetOrCreate<CpModelMapping>()->Literals(routes.literals());
1668 const int num_nodes = ReindexArcs(&tails, &heads);
1669 LoadSubcircuitConstraint(num_nodes, tails, heads, literals, m,
1670 /*multiple_subcircuit_through_zero=*/true);
1671}
1672
1673bool LoadConstraint(const ConstraintProto& ct, Model* m) {
1674 switch (ct.constraint_case()) {
1675 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1676 return true;
1677 case ConstraintProto::ConstraintCase::kBoolOr:
1679 return true;
1680 case ConstraintProto::ConstraintCase::kBoolAnd:
1682 return true;
1683 case ConstraintProto::ConstraintCase::kAtMostOne:
1685 return true;
1686 case ConstraintProto::ConstraintCase::kExactlyOne:
1688 return true;
1689 case ConstraintProto::ConstraintCase::kBoolXor:
1691 return true;
1692 case ConstraintProto::ConstraintProto::kLinear:
1694 return true;
1695 case ConstraintProto::ConstraintProto::kAllDiff:
1697 return true;
1698 case ConstraintProto::ConstraintProto::kIntProd:
1700 return true;
1701 case ConstraintProto::ConstraintProto::kIntDiv:
1703 return true;
1704 case ConstraintProto::ConstraintProto::kIntMod:
1706 return true;
1707 case ConstraintProto::ConstraintProto::kLinMax:
1709 return true;
1710 case ConstraintProto::ConstraintProto::kInterval:
1711 // Already dealt with.
1712 return true;
1713 case ConstraintProto::ConstraintProto::kNoOverlap:
1715 return true;
1716 case ConstraintProto::ConstraintProto::kNoOverlap2D:
1718 return true;
1719 case ConstraintProto::ConstraintProto::kCumulative:
1721 return true;
1722 case ConstraintProto::ConstraintProto::kReservoir:
1724 return true;
1725 case ConstraintProto::ConstraintProto::kCircuit:
1727 return true;
1728 case ConstraintProto::ConstraintProto::kRoutes:
1730 return true;
1731 default:
1732 return false;
1733 }
1734}
1735
1736} // namespace sat
1737} // namespace operations_research
IntegerValue y
IntegerValue size
int64_t max
int64_t min
Domain IntersectionWith(const Domain &domain) const
bool Contains(int64_t value) const
Domain InverseMultiplicationBy(int64_t coeff) const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
bool Add(Literal literal, IntegerLiteral integer_literal)
void ReserveSpaceForNumVariables(int num_vars)
Definition integer.cc:821
LiteralIndex Index() const
Definition sat_base.h:91
T Add(std::function< T(Model *)> f)
Definition model.h:89
bool AddProblemClause(absl::Span< const Literal > literals, bool is_safe=true)
SatParameters parameters
CpModelProto proto
The output proto.
const Constraint * ct
int64_t value
IntVar * var
bool is_equality
int lit
int literal
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:58
std::function< std::vector< ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
Definition integer.h:2074
void LoadBoolXorConstraint(const ConstraintProto &ct, Model *m)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition integer.h:94
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Weighted sum >= constant.
void LoadCumulativeConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> LiteralXorIs(const std::vector< Literal > &literals, bool value)
Enforces the XOR of a set of literals to be equal to the given value.
void LoadLinMaxConstraint(const ConstraintProto &ct, Model *m)
void LoadIntProdConstraint(const ConstraintProto &ct, Model *m)
void SplitAndLoadIntermediateConstraints(bool lb_required, bool ub_required, std::vector< IntegerVariable > *vars, std::vector< int64_t > *coeffs, Model *m)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Weighted sum <= constant.
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> ProductConstraint(AffineExpression a, AffineExpression b, AffineExpression p)
Adds the constraint: a * b = p.
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
Automatically detect optional variables.
void LoadBoolOrConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition sat_solver.h:933
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
enforcement_literals => clause.
Definition sat_solver.h:973
IntType CeilOfRatio(IntType numerator, IntType denominator)
Definition util.h:729
void LoadSubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model, bool multiple_subcircuit_through_zero)
Definition circuit.cc:647
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition integer.h:1893
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
Definition integer.cc:51
std::function< void(Model *)> FixedDivisionConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
Adds the constraint: a / b = c where b is a constant.
bool HasEnforcementLiteral(const ConstraintProto &ct)
Small utility functions to deal with half-reified constraints.
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void LoadIntModConstraint(const ConstraintProto &ct, Model *m)
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> Implication(absl::Span< const Literal > enforcement_literals, IntegerLiteral i)
Definition integer.h:2025
const IntervalVariable kNoIntervalVariable(-1)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
Definition cumulative.cc:42
std::function< void(Model *)> DivisionConstraint(AffineExpression num, AffineExpression denom, AffineExpression div)
Adds the constraint: num / denom = div. (denom > 0).
void LoadRoutesConstraint(const ConstraintProto &ct, Model *m)
void LoadAtMostOneConstraint(const ConstraintProto &ct, Model *m)
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void LoadCircuitConstraint(const ConstraintProto &ct, Model *m)
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition sat_solver.h:919
void LoadReservoirConstraint(const ConstraintProto &ct, Model *m)
void AddWeightedSumLowerOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coefficients, int64_t upper_bound, Model *model)
enforcement_literals => sum <= upper_bound
int ReindexArcs(IntContainer *tails, IntContainer *heads, absl::flat_hash_map< int, int > *mapping_output=nullptr)
Definition circuit.h:210
void LoadNoOverlapConstraint(const ConstraintProto &ct, Model *m)
void LoadAllDiffConstraint(const ConstraintProto &ct, Model *m)
void AddDisjunctive(const std::vector< IntervalVariable > &intervals, Model *model)
std::vector< Literal > Literals(absl::Span< const int > input)
Definition sat_base.h:146
void LoadNoOverlap2dConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
void LoadBoolAndConstraint(const ConstraintProto &ct, Model *m)
void AddNonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, Model *model)
Definition diffn.cc:174
void LoadExactlyOneConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition sat_solver.h:905
bool LoadConstraint(const ConstraintProto &ct, Model *m)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
void AddReservoirConstraint(std::vector< AffineExpression > times, std::vector< AffineExpression > deltas, std::vector< Literal > presences, int64_t min_level, int64_t max_level, Model *model)
Definition timetable.cc:31
std::function< void(Model *)> FixedModuloConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
Adds the constraint: a % b = c where b is a constant.
const BooleanVariable kNoBooleanVariable(-1)
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
void AddWeightedSumGreaterOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coefficients, int64_t lower_bound, Model *model)
enforcement_literals => sum >= lower_bound
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
void LoadIntDivConstraint(const ConstraintProto &ct, Model *m)
In SWIG mode, we don't want anything besides these top-level includes.
const Variable x
Definition qp_tests.cc:127
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
var * coeff + constant >= bound.
Definition integer.h:1696
IntegerLiteral LowerOrEqual(IntegerValue bound) const
var * coeff + constant <= bound.
Definition integer.h:1708
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition integer.h:1667
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition integer.h:1673
#define SOLVER_LOG(logger,...)
Definition logging.h:109