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