OR-Tools  9.2
cp_model_loader.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <limits>
19#include <map>
20#include <memory>
21#include <set>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
35#include "ortools/sat/circuit.h"
40#include "ortools/sat/diffn.h"
43#include "ortools/sat/integer.h"
52#include "ortools/sat/table.h"
57
58namespace operations_research {
59namespace sat {
60
61namespace {
62
63template <typename Values>
64std::vector<int64_t> ValuesFromProto(const Values& values) {
65 return std::vector<int64_t>(values.begin(), values.end());
66}
67
68void ComputeLinearBounds(const LinearConstraintProto& proto,
69 CpModelMapping* mapping, IntegerTrail* integer_trail,
70 int64_t* sum_min, int64_t* sum_max) {
71 *sum_min = 0;
72 *sum_max = 0;
73
74 for (int i = 0; i < proto.vars_size(); ++i) {
75 const int64_t coeff = proto.coeffs(i);
76 const IntegerVariable var = mapping->Integer(proto.vars(i));
77 const int64_t lb = integer_trail->LowerBound(var).value();
78 const int64_t ub = integer_trail->UpperBound(var).value();
79 if (coeff >= 0) {
80 (*sum_min) += coeff * lb;
81 (*sum_max) += coeff * ub;
82 } else {
83 (*sum_min) += coeff * ub;
84 (*sum_max) += coeff * lb;
85 }
86 }
87}
88
89// We check if the constraint is a sum(ax * xi) == value.
90bool ConstraintIsEq(const LinearConstraintProto& proto) {
91 return proto.domain_size() == 2 && proto.domain(0) == proto.domain(1);
92}
93
94// We check if the constraint is a sum(ax * xi) != value.
95bool ConstraintIsNEq(const LinearConstraintProto& proto,
96 CpModelMapping* mapping, IntegerTrail* integer_trail,
97 int64_t* single_value) {
98 int64_t sum_min = 0;
99 int64_t sum_max = 0;
100 ComputeLinearBounds(proto, mapping, integer_trail, &sum_min, &sum_max);
101
102 const Domain complement =
103 Domain(sum_min, sum_max)
104 .IntersectionWith(ReadDomainFromProto(proto).Complement());
105 if (complement.IsEmpty()) return false;
106 const int64_t value = complement.Min();
107
108 if (complement.Size() == 1) {
109 if (single_value != nullptr) {
110 *single_value = value;
111 }
112 return true;
113 }
114 return false;
115}
116
117} // namespace
118
120 bool view_all_booleans_as_integers, Model* m) {
121 auto* mapping = m->GetOrCreate<CpModelMapping>();
122 const int num_proto_variables = model_proto.variables_size();
123
124 // All [0, 1] variables always have a corresponding Boolean, even if it is
125 // fixed to 0 (domain == [0,0]) or fixed to 1 (domain == [1,1]).
126 {
127 auto* sat_solver = m->GetOrCreate<SatSolver>();
128 CHECK_EQ(sat_solver->NumVariables(), 0);
129
130 BooleanVariable new_var(0);
131 std::vector<BooleanVariable> false_variables;
132 std::vector<BooleanVariable> true_variables;
133
134 mapping->booleans_.resize(num_proto_variables, kNoBooleanVariable);
135 mapping->reverse_boolean_map_.resize(num_proto_variables, -1);
136 for (int i = 0; i < num_proto_variables; ++i) {
137 const auto& domain = model_proto.variables(i).domain();
138 if (domain.size() != 2) continue;
139 if (domain[0] >= 0 && domain[1] <= 1) {
140 mapping->booleans_[i] = new_var;
141 mapping->reverse_boolean_map_[new_var] = i;
142 if (domain[1] == 0) {
143 false_variables.push_back(new_var);
144 } else if (domain[0] == 1) {
145 true_variables.push_back(new_var);
146 }
147 ++new_var;
148 }
149 }
150
151 sat_solver->SetNumVariables(new_var.value());
152 for (const BooleanVariable var : true_variables) {
153 m->Add(ClauseConstraint({sat::Literal(var, true)}));
154 }
155 for (const BooleanVariable var : false_variables) {
156 m->Add(ClauseConstraint({sat::Literal(var, false)}));
157 }
158 }
159
160 // Compute the list of positive variable reference for which we need to
161 // create an IntegerVariable.
162 std::vector<int> var_to_instantiate_as_integer;
163 if (view_all_booleans_as_integers) {
164 var_to_instantiate_as_integer.resize(num_proto_variables);
165 for (int i = 0; i < num_proto_variables; ++i) {
166 var_to_instantiate_as_integer[i] = i;
167 }
168 } else {
169 // Compute the integer variable references used by the model.
170 absl::flat_hash_set<int> used_variables;
171
172 IndexReferences refs;
173 for (int c = 0; c < model_proto.constraints_size(); ++c) {
176 for (const int ref : refs.variables) {
177 used_variables.insert(PositiveRef(ref));
178 }
179 }
180
181 // Add the objectives variables that needs to be referenceable as integer
182 // even if they are only used as Booleans.
184 for (const int obj_var : model_proto.objective().vars()) {
185 used_variables.insert(PositiveRef(obj_var));
186 }
187 }
188
189 // Make sure any unused variable, that is not already a Boolean is
190 // considered "used".
191 for (int i = 0; i < num_proto_variables; ++i) {
192 if (mapping->booleans_[i] == kNoBooleanVariable) {
193 used_variables.insert(i);
194 }
195 }
196
197 // We want the variable in the problem order.
198 var_to_instantiate_as_integer.assign(used_variables.begin(),
199 used_variables.end());
200 gtl::STLSortAndRemoveDuplicates(&var_to_instantiate_as_integer);
201 }
202 mapping->integers_.resize(num_proto_variables, kNoIntegerVariable);
203
204 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
205 integer_trail->ReserveSpaceForNumVariables(
206 var_to_instantiate_as_integer.size());
207 mapping->reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(),
208 -1);
209 for (const int i : var_to_instantiate_as_integer) {
210 const auto& var_proto = model_proto.variables(i);
211 mapping->integers_[i] =
212 integer_trail->AddIntegerVariable(ReadDomainFromProto(var_proto));
213 DCHECK_LT(mapping->integers_[i], mapping->reverse_integer_map_.size());
214 mapping->reverse_integer_map_[mapping->integers_[i]] = i;
215 }
216
217 auto* encoder = m->GetOrCreate<IntegerEncoder>();
218 auto* intervals_repository = m->GetOrCreate<IntervalsRepository>();
219
220 // Link any variable that has both views.
221 for (int i = 0; i < num_proto_variables; ++i) {
222 if (mapping->integers_[i] == kNoIntegerVariable) continue;
223 if (mapping->booleans_[i] == kNoBooleanVariable) continue;
224
225 // Associate with corresponding integer variable.
226 encoder->AssociateToIntegerEqualValue(
227 sat::Literal(mapping->booleans_[i], true), mapping->integers_[i],
228 IntegerValue(1));
229 }
230
231 // Create the interval variables.
232 mapping->intervals_.resize(model_proto.constraints_size(),
234 for (int c = 0; c < model_proto.constraints_size(); ++c) {
236 if (ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
237 continue;
238 }
240 const sat::Literal enforcement_literal =
241 mapping->Literal(ct.enforcement_literal(0));
242 // TODO(user): Fix the constant variable situation. An optional interval
243 // with constant start/end or size cannot share the same constant
244 // variable if it is used in non-optional situation.
245 mapping->intervals_[c] = intervals_repository->CreateInterval(
246 mapping->Affine(ct.interval().start()),
247 mapping->Affine(ct.interval().end()),
248 mapping->Affine(ct.interval().size()), enforcement_literal.Index(),
249 /*add_linear_relation=*/false);
250 } else {
251 mapping->intervals_[c] = intervals_repository->CreateInterval(
252 mapping->Affine(ct.interval().start()),
253 mapping->Affine(ct.interval().end()),
254 mapping->Affine(ct.interval().size()), kNoLiteralIndex,
255 /*add_linear_relation=*/false);
256 }
257 mapping->already_loaded_ct_.insert(&ct);
258 }
259}
260
262 auto* mapping = m->GetOrCreate<CpModelMapping>();
263 const SymmetryProto& symmetry = model_proto.symmetry();
264 if (symmetry.permutations().empty()) return;
265
266 // We currently can only use symmetry that touch a subset of variables.
267 const int num_vars = model_proto.variables().size();
268 std::vector<bool> can_be_used_in_symmetry(num_vars, true);
269
270 // First, we currently only support loading symmetry between Booleans.
271 for (int v = 0; v < num_vars; ++v) {
272 if (!mapping->IsBoolean(v)) can_be_used_in_symmetry[v] = false;
273 }
274
275 // Tricky: Moreover, some constraint will causes extra Boolean to be created
276 // and linked with the Boolean in the constraints. We can't use any of the
277 // symmetry that touch these since we potentially miss the component that will
278 // map these extra Booleans between each other.
279 //
280 // TODO(user): We could add these extra Boolean during expansion/presolve so
281 // that we have the symmetry involing them. Or maybe comes up with a different
282 // solution.
283 const int num_constraints = model_proto.constraints().size();
284 for (int c = 0; c < num_constraints; ++c) {
286 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
287 if (ct.linear().domain().size() <= 2) continue;
288
289 // A linear with a complex domain might need extra Booleans to be loaded.
290 // Note that it should be fine for the Boolean(s) in enforcement_literal
291 // though.
292 for (const int ref : ct.linear().vars()) {
293 can_be_used_in_symmetry[PositiveRef(ref)] = false;
294 }
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
302 for (const SparsePermutationProto& perm : symmetry.permutations()) {
303 bool can_be_used = true;
304 for (const int var : perm.support()) {
305 if (!can_be_used_in_symmetry[var]) {
306 can_be_used = false;
307 break;
308 }
309 }
310 if (!can_be_used) continue;
311
312 // Convert the variable symmetry to a "literal" one.
313 auto literal_permutation =
314 absl::make_unique<SparsePermutation>(num_literals);
315 int support_index = 0;
316 const int num_cycle = perm.cycle_sizes().size();
317 for (int i = 0; i < num_cycle; ++i) {
318 const int size = perm.cycle_sizes(i);
319 const int saved_support_index = support_index;
320 for (int j = 0; j < size; ++j) {
321 const int var = perm.support(support_index++);
322 literal_permutation->AddToCurrentCycle(
323 mapping->Literal(var).Index().value());
324 }
325 literal_permutation->CloseCurrentCycle();
326
327 // Note that we also need to add the corresponding cycle for the negated
328 // literals.
329 support_index = saved_support_index;
330 for (int j = 0; j < size; ++j) {
331 const int var = perm.support(support_index++);
332 literal_permutation->AddToCurrentCycle(
333 mapping->Literal(var).NegatedIndex().value());
334 }
335 literal_permutation->CloseCurrentCycle();
336 }
337 symmetry_handler->AddSymmetry(std::move(literal_permutation));
338 }
339
340 SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Added ",
341 symmetry_handler->num_permutations(),
342 " symmetry to the SAT solver.");
343}
344
345// The logic assumes that the linear constraints have been presolved, so that
346// equality with a domain bound have been converted to <= or >= and so that we
347// never have any trivial inequalities.
348//
349// TODO(user): Regroup/presolve two encoding like b => x > 2 and the same
350// Boolean b => x > 5. These shouldn't happen if we merge linear constraints.
352 auto* mapping = m->GetOrCreate<CpModelMapping>();
353 auto* encoder = m->GetOrCreate<IntegerEncoder>();
354 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
355 auto* sat_solver = m->GetOrCreate<SatSolver>();
356
357 // TODO(user): Debug what makes it unsat at this point.
358 if (sat_solver->IsModelUnsat()) return;
359
360 // Detection of literal equivalent to (i_var == value). We collect all the
361 // half-reified constraint lit => equality or lit => inequality for a given
362 // variable, and we will later sort them to detect equivalence.
363 struct EqualityDetectionHelper {
364 const ConstraintProto* ct;
366 int64_t value;
367 bool is_equality; // false if != instead.
368
369 bool operator<(const EqualityDetectionHelper& o) const {
370 if (literal.Variable() == o.literal.Variable()) {
371 if (value == o.value) return is_equality && !o.is_equality;
372 return value < o.value;
373 }
374 return literal.Variable() < o.literal.Variable();
375 }
376 };
377 std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
379
380 // TODO(user): We will re-add the same implied bounds during probing, so
381 // it might not be necessary to do that here. Also, it might be too early
382 // if some of the literal view used in the LP are created later, but that
383 // should be fixable via calls to implied_bounds->NotifyNewIntegerView().
384 auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
385
386 // Detection of literal equivalent to (i_var >= bound). We also collect
387 // all the half-refied part and we will sort the vector for detection of the
388 // equivalence.
389 struct InequalityDetectionHelper {
390 const ConstraintProto* ct;
392 IntegerLiteral i_lit;
393
394 bool operator<(const InequalityDetectionHelper& o) const {
395 if (literal.Variable() == o.literal.Variable()) {
396 return i_lit.var < o.i_lit.var;
397 }
398 return literal.Variable() < o.literal.Variable();
399 }
400 };
401 std::vector<InequalityDetectionHelper> inequalities;
402
403 // Loop over all constraints and fill var_to_equalities and inequalities.
404 for (const ConstraintProto& ct : model_proto.constraints()) {
405 if (ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
406 continue;
407 }
408 if (ct.enforcement_literal().size() != 1) continue;
409 if (ct.linear().vars_size() != 1) continue;
410
411 // ct is a linear constraint with one term and one enforcement literal.
412 const sat::Literal enforcement_literal =
413 mapping->Literal(ct.enforcement_literal(0));
414 if (sat_solver->Assignment().LiteralIsFalse(enforcement_literal)) continue;
415
416 const int ref = ct.linear().vars(0);
417 const int var = PositiveRef(ref);
418
420 const Domain domain_if_enforced =
421 ReadDomainFromProto(ct.linear())
422 .InverseMultiplicationBy(ct.linear().coeffs(0) *
423 (RefIsPositive(ref) ? 1 : -1));
424
425 if (domain_if_enforced.IsEmpty()) {
426 if (!sat_solver->AddUnitClause(enforcement_literal.Negated())) return;
427 continue;
428 }
429
430 // Detect enforcement_literal => (var >= value or var <= value).
431 if (domain_if_enforced.NumIntervals() == 1) {
432 if (domain_if_enforced.Max() >= domain.Max() &&
433 domain_if_enforced.Min() > domain.Min()) {
434 inequalities.push_back({&ct, enforcement_literal,
436 mapping->Integer(var),
437 IntegerValue(domain_if_enforced.Min()))});
438 } else if (domain_if_enforced.Min() <= domain.Min() &&
439 domain_if_enforced.Max() < domain.Max()) {
440 inequalities.push_back({&ct, enforcement_literal,
442 mapping->Integer(var),
443 IntegerValue(domain_if_enforced.Max()))});
444 }
445 }
446
447 // Detect implied bounds. The test is less strict than the above
448 // test.
449 if (domain_if_enforced.Min() > domain.Min()) {
450 implied_bounds->Add(
451 enforcement_literal,
453 mapping->Integer(var), IntegerValue(domain_if_enforced.Min())));
454 }
455 if (domain_if_enforced.Max() < domain.Max()) {
456 implied_bounds->Add(
457 enforcement_literal,
458 IntegerLiteral::LowerOrEqual(mapping->Integer(var),
459 IntegerValue(domain_if_enforced.Max())));
460 }
461
462 // Detect enforcement_literal => (var == value or var != value).
463 //
464 // Note that for domain with 2 values like [0, 1], we will detect both ==
465 // 0 and != 1. Similarly, for a domain in [min, max], we should both
466 // detect (== min) and (<= min), and both detect (== max) and (>= max).
467 {
468 const Domain inter = domain.IntersectionWith(domain_if_enforced);
469 if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
470 var_to_equalities[var].push_back(
471 {&ct, enforcement_literal, inter.Min(), true});
472 if (domain.Contains(inter.Min())) {
473 mapping->variables_to_encoded_values_[var].insert(inter.Min());
474 }
475 }
476 }
477 {
478 const Domain inter =
479 domain.IntersectionWith(domain_if_enforced.Complement());
480 if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
481 var_to_equalities[var].push_back(
482 {&ct, enforcement_literal, inter.Min(), false});
483 if (domain.Contains(inter.Min())) {
484 mapping->variables_to_encoded_values_[var].insert(inter.Min());
485 }
486 }
487 }
488 }
489
490 // Detect Literal <=> X >= value
491 int num_inequalities = 0;
492 std::sort(inequalities.begin(), inequalities.end());
493 for (int i = 0; i + 1 < inequalities.size(); i++) {
494 if (inequalities[i].literal != inequalities[i + 1].literal.Negated()) {
495 continue;
496 }
497
498 // TODO(user): In these cases, we could fix the enforcement literal right
499 // away or ignore the constraint. Note that it will be done later anyway
500 // though.
501 if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
502 integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
503 continue;
504 }
505 if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
506 integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
507 continue;
508 }
509
510 const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
511 const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
512 if (pair_a.first == pair_b.second) {
513 ++num_inequalities;
514 encoder->AssociateToIntegerLiteral(inequalities[i].literal,
515 inequalities[i].i_lit);
516 mapping->already_loaded_ct_.insert(inequalities[i].ct);
517 mapping->already_loaded_ct_.insert(inequalities[i + 1].ct);
518 }
519 }
520
521 // Encode the half-inequalities.
522 int num_half_inequalities = 0;
523 for (const auto inequality : inequalities) {
524 if (mapping->ConstraintIsAlreadyLoaded(inequality.ct)) continue;
525 m->Add(
526 Implication(inequality.literal,
527 encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
528 if (sat_solver->IsModelUnsat()) return;
529
530 ++num_half_inequalities;
531 mapping->already_loaded_ct_.insert(inequality.ct);
532 mapping->is_half_encoding_ct_.insert(inequality.ct);
533 }
534
535 if (!inequalities.empty()) {
536 VLOG(1) << num_inequalities << " literals associated to VAR >= value, and "
537 << num_half_inequalities << " half-associations.";
538 }
539
540 // Detect Literal <=> X == value and associate them in the IntegerEncoder.
541 //
542 // TODO(user): Fully encode variable that are almost fully encoded?
543 int num_constraints = 0;
544 int num_equalities = 0;
545 int num_half_equalities = 0;
546 int num_fully_encoded = 0;
547 int num_partially_encoded = 0;
548 for (int i = 0; i < var_to_equalities.size(); ++i) {
549 std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
550 std::sort(encoding.begin(), encoding.end());
551 if (encoding.empty()) continue;
552 num_constraints += encoding.size();
553
554 absl::flat_hash_set<int64_t> values;
555 for (int j = 0; j + 1 < encoding.size(); j++) {
556 if ((encoding[j].value != encoding[j + 1].value) ||
557 (encoding[j].literal != encoding[j + 1].literal.Negated()) ||
558 (encoding[j].is_equality != true) ||
559 (encoding[j + 1].is_equality != false)) {
560 continue;
561 }
562
563 ++num_equalities;
564 encoder->AssociateToIntegerEqualValue(encoding[j].literal,
565 mapping->integers_[i],
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->IsModelUnsat()) return;
576
577 // Encode the half-equalities.
578 //
579 // TODO(user): delay this after PropagateEncodingFromEquivalenceRelations()?
580 // Otherwise we might create new Boolean variables for no reason. Note
581 // however, that in the presolve, we should only use the "representative" in
582 // linear constraints, so we should be fine.
583 for (const auto equality : encoding) {
584 if (mapping->ConstraintIsAlreadyLoaded(equality.ct)) continue;
585 const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
586 mapping->integers_[i], IntegerValue(equality.value));
587 if (equality.is_equality) {
588 m->Add(Implication(equality.literal, eq));
589 } else {
590 m->Add(Implication(equality.literal, eq.Negated()));
591 }
592
593 ++num_half_equalities;
594 mapping->already_loaded_ct_.insert(equality.ct);
595 mapping->is_half_encoding_ct_.insert(equality.ct);
596 }
597
598 // Update stats.
599 if (VLOG_IS_ON(1)) {
600 if (encoder->VariableIsFullyEncoded(mapping->integers_[i])) {
601 ++num_fully_encoded;
602 } else {
603 ++num_partially_encoded;
604 }
605 }
606 }
607
608 if (num_constraints > 0) {
609 VLOG(1) << num_equalities << " literals associated to VAR == value, and "
610 << num_half_equalities << " half-associations.";
611 }
612 if (num_fully_encoded > 0) {
613 VLOG(1) << "num_fully_encoded_variables: " << num_fully_encoded;
614 }
615 if (num_partially_encoded > 0) {
616 VLOG(1) << "num_partially_encoded_variables: " << num_partially_encoded;
617 }
618}
619
621 int num_element_encoded = 0;
622 auto* mapping = m->GetOrCreate<CpModelMapping>();
623 auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
624
625 // Scan all exactly_one constraints and look for literal => var == value to
626 // detect element encodings.
627 for (int c = 0; c < model_proto.constraints_size(); ++c) {
629
630 if (ct.constraint_case() != ConstraintProto::kExactlyOne) continue;
631
632 // Project the implied values onto each integer variable.
633 absl::flat_hash_map<IntegerVariable, std::vector<ValueLiteralPair>>
634 var_to_value_literal_list;
635 for (const int l : ct.exactly_one().literals()) {
636 const Literal literal = mapping->Literal(l);
637 for (const auto& var_value : implied_bounds->GetImpliedValues(literal)) {
638 var_to_value_literal_list[var_value.first].push_back(
639 {var_value.second, literal});
640 }
641 }
642
643 // VLOG info.
644 std::vector<IntegerVariable> encoded_variables;
645 std::string encoded_variables_str;
646
647 // Search for variable fully covered by the literals of the exactly_one.
648 for (const auto& [var, literal_value_list] : var_to_value_literal_list) {
649 if (literal_value_list.size() < ct.exactly_one().literals_size()) {
650 VLOG(2) << "X" << var.value() << " has " << literal_value_list.size()
651 << " implied values, and a domain of size "
653 ->InitialVariableDomain(var)
654 .Size();
655 continue;
656 }
657
658 // We use the order of literals of the exactly_one.
659 implied_bounds->AddElementEncoding(var, literal_value_list, c);
660 if (VLOG_IS_ON(1)) {
661 encoded_variables.push_back(var);
662 absl::StrAppend(&encoded_variables_str, " X", var.value());
663 num_element_encoded++;
664 }
665 }
666 if (encoded_variables.size() > 1 && VLOG_IS_ON(1)) {
667 VLOG(1) << "exactly_one(" << c << ") encodes " << encoded_variables.size()
668 << " variables at the same time: " << encoded_variables_str;
669 }
670 }
671
672 if (num_element_encoded > 0) {
673 VLOG(1) << "num_element_encoded: " << num_element_encoded;
674 }
675}
676
678 Model* m) {
679 auto* mapping = m->GetOrCreate<CpModelMapping>();
680 auto* encoder = m->GetOrCreate<IntegerEncoder>();
681 auto* sat_solver = m->GetOrCreate<SatSolver>();
682
683 // Loop over all constraints and find affine ones.
684 int64_t num_associations = 0;
685 int64_t num_set_to_false = 0;
686 for (const ConstraintProto& ct : model_proto.constraints()) {
687 if (!ct.enforcement_literal().empty()) continue;
688 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
689 if (ct.linear().vars_size() != 2) continue;
690 if (!ConstraintIsEq(ct.linear())) continue;
691
692 const IntegerValue rhs(ct.linear().domain(0));
693
694 // Make sure the coefficient are positive.
695 IntegerVariable var1 = mapping->Integer(ct.linear().vars(0));
696 IntegerVariable var2 = mapping->Integer(ct.linear().vars(1));
697 IntegerValue coeff1(ct.linear().coeffs(0));
698 IntegerValue coeff2(ct.linear().coeffs(1));
699 if (coeff1 < 0) {
700 var1 = NegationOf(var1);
701 coeff1 = -coeff1;
702 }
703 if (coeff2 < 0) {
704 var2 = NegationOf(var2);
705 coeff2 = -coeff2;
706 }
707
708 // TODO(user): This is not supposed to happen, but apparently it did on
709 // once on routing_GCM_0001_sat.fzn. Investigate and fix.
710 if (coeff1 == 0 || coeff2 == 0) continue;
711
712 // We first map the >= literals.
713 // It is important to do that first, since otherwise mapping a == literal
714 // might creates the underlying >= and <= literals.
715 for (int i = 0; i < 2; ++i) {
716 for (const auto value_literal :
717 encoder->PartialGreaterThanEncoding(var1)) {
718 const IntegerValue value1 = value_literal.first;
719 const IntegerValue bound2 = FloorRatio(rhs - value1 * coeff1, coeff2);
720 ++num_associations;
721 encoder->AssociateToIntegerLiteral(
722 value_literal.second, IntegerLiteral::LowerOrEqual(var2, bound2));
723 }
724 std::swap(var1, var2);
725 std::swap(coeff1, coeff2);
726 }
727
728 // Same for the == literals.
729 //
730 // TODO(user): This is similar to LoadEquivalenceAC() for unreified
731 // constraints, but when the later is called, more encoding might have taken
732 // place.
733 for (int i = 0; i < 2; ++i) {
734 for (const auto value_literal : encoder->PartialDomainEncoding(var1)) {
735 const IntegerValue value1 = value_literal.value;
736 const IntegerValue intermediate = rhs - value1 * coeff1;
737 if (intermediate % coeff2 != 0) {
738 // Using this function deals properly with UNSAT.
739 ++num_set_to_false;
740 sat_solver->AddUnitClause(value_literal.literal.Negated());
741 continue;
742 }
743 ++num_associations;
744 encoder->AssociateToIntegerEqualValue(value_literal.literal, var2,
745 intermediate / coeff2);
746 }
747 std::swap(var1, var2);
748 std::swap(coeff1, coeff2);
749 }
750 }
751
752 if (num_associations > 0) {
753 VLOG(1) << "Num associations from equivalences = " << num_associations;
754 }
755 if (num_set_to_false > 0) {
756 VLOG(1) << "Num literals set to false from equivalences = "
757 << num_set_to_false;
758 }
759}
760
762 auto* mapping = m->GetOrCreate<CpModelMapping>();
764 if (!parameters.use_optional_variables()) return;
766
767 // The variables from the objective cannot be marked as optional!
768 const int num_proto_variables = model_proto.variables_size();
769 std::vector<bool> already_seen(num_proto_variables, false);
771 for (const int ref : model_proto.objective().vars()) {
772 already_seen[PositiveRef(ref)] = true;
773 }
774 }
775
776 // Compute for each variables the intersection of the enforcement literals
777 // of the constraints in which they appear.
778 //
779 // TODO(user): This deals with the simplest cases, but we could try to
780 // detect literals that implies all the constraints in which a variable
781 // appear to false. This can be done with a LCA computation in the tree of
782 // Boolean implication (once the presolve remove cycles). Not sure if we can
783 // properly exploit that afterwards though. Do some research!
784 std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
785 std::set<int> literals_set;
786 for (int c = 0; c < model_proto.constraints_size(); ++c) {
788 if (ct.enforcement_literal().empty()) {
789 for (const int var : UsedVariables(ct)) {
790 already_seen[var] = true;
791 enforcement_intersection[var].clear();
792 }
793 } else {
794 literals_set.clear();
795 literals_set.insert(ct.enforcement_literal().begin(),
796 ct.enforcement_literal().end());
797 for (const int var : UsedVariables(ct)) {
798 if (!already_seen[var]) {
799 enforcement_intersection[var].assign(ct.enforcement_literal().begin(),
800 ct.enforcement_literal().end());
801 } else {
802 // Take the intersection.
803 std::vector<int>& vector_ref = enforcement_intersection[var];
804 int new_size = 0;
805 for (const int literal : vector_ref) {
806 if (gtl::ContainsKey(literals_set, literal)) {
807 vector_ref[new_size++] = literal;
808 }
809 }
810 vector_ref.resize(new_size);
811 }
812 already_seen[var] = true;
813 }
814 }
815 }
816
817 // Auto-detect optional variables.
818 int num_optionals = 0;
819 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
820 for (int var = 0; var < num_proto_variables; ++var) {
821 const IntegerVariableProto& var_proto = model_proto.variables(var);
822 const int64_t min = var_proto.domain(0);
823 const int64_t max = var_proto.domain(var_proto.domain().size() - 1);
824 if (min == max) continue;
825 if (min == 0 && max == 1) continue;
826 if (enforcement_intersection[var].empty()) continue;
827
828 ++num_optionals;
829 integer_trail->MarkIntegerVariableAsOptional(
830 mapping->Integer(var),
831 mapping->Literal(enforcement_intersection[var].front()));
832 }
833 VLOG(2) << "Auto-detected " << num_optionals << " optional variables.";
834}
835
837 Model* m) {
838 if (model_proto.search_strategy().empty()) return;
839
840 auto* mapping = m->GetOrCreate<CpModelMapping>();
841 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
842 for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
843 if (strategy.domain_reduction_strategy() ==
845 for (const int ref : strategy.variables()) {
846 if (!mapping->IsInteger(ref)) return;
847 const IntegerVariable variable = mapping->Integer(PositiveRef(ref));
848 if (!integer_trail->IsFixed(variable)) {
849 m->Add(FullyEncodeVariable(variable));
850 }
851 }
852 }
853 }
854}
855
856// ============================================================================
857// Constraint loading functions.
858// ============================================================================
859
861 auto* mapping = m->GetOrCreate<CpModelMapping>();
862 std::vector<Literal> literals = mapping->Literals(ct.bool_or().literals());
863 for (const int ref : ct.enforcement_literal()) {
864 literals.push_back(mapping->Literal(ref).Negated());
865 }
866 m->Add(ClauseConstraint(literals));
867}
868
870 auto* mapping = m->GetOrCreate<CpModelMapping>();
871 std::vector<Literal> literals;
872 for (const int ref : ct.enforcement_literal()) {
873 literals.push_back(mapping->Literal(ref).Negated());
874 }
875 auto* sat_solver = m->GetOrCreate<SatSolver>();
876 for (const Literal literal : mapping->Literals(ct.bool_and().literals())) {
877 literals.push_back(literal);
878 sat_solver->AddProblemClause(literals);
879 literals.pop_back();
880 }
881}
882
884 auto* mapping = m->GetOrCreate<CpModelMapping>();
885 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
886 m->Add(AtMostOneConstraint(mapping->Literals(ct.at_most_one().literals())));
887}
888
890 auto* mapping = m->GetOrCreate<CpModelMapping>();
891 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
892 m->Add(ExactlyOneConstraint(mapping->Literals(ct.exactly_one().literals())));
893}
894
896 auto* mapping = m->GetOrCreate<CpModelMapping>();
897 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
898 m->Add(LiteralXorIs(mapping->Literals(ct.bool_xor().literals()), true));
899}
900
901namespace {
902
903// Boolean encoding of:
904// enforcement_literal => coeff1 * var1 + coeff2 * var2 == rhs;
905void LoadEquivalenceAC(const std::vector<Literal> enforcement_literal,
906 IntegerValue coeff1, IntegerVariable var1,
907 IntegerValue coeff2, IntegerVariable var2,
908 const IntegerValue rhs, Model* m) {
909 auto* encoder = m->GetOrCreate<IntegerEncoder>();
910 CHECK(encoder->VariableIsFullyEncoded(var1));
911 CHECK(encoder->VariableIsFullyEncoded(var2));
912 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
913 for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
914 term1_value_to_literal[coeff1 * value_literal.value] =
915 value_literal.literal;
916 }
917 for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
918 const IntegerValue target = rhs - value_literal.value * coeff2;
919 if (!term1_value_to_literal.contains(target)) {
920 m->Add(EnforcedClause(enforcement_literal,
921 {value_literal.literal.Negated()}));
922 } else {
923 const Literal target_literal = term1_value_to_literal[target];
924 m->Add(EnforcedClause(enforcement_literal,
925 {value_literal.literal.Negated(), target_literal}));
926 m->Add(EnforcedClause(enforcement_literal,
927 {value_literal.literal, target_literal.Negated()}));
928
929 // This "target" can never be reached again, so it is safe to remove it.
930 // We do that so we know the term1 values that are never reached.
931 term1_value_to_literal.erase(target);
932 }
933 }
934
935 // Exclude the values that can never be "matched" by coeff2 * var2.
936 // We need the std::sort() to be deterministic!
937 std::vector<Literal> implied_false;
938 for (const auto entry : term1_value_to_literal) {
939 implied_false.push_back(entry.second);
940 }
941 std::sort(implied_false.begin(), implied_false.end());
942 for (const Literal l : implied_false) {
943 m->Add(EnforcedClause(enforcement_literal, {l.Negated()}));
944 }
945}
946
947// Boolean encoding of:
948// enforcement_literal => coeff1 * var1 + coeff2 * var2 != rhs;
949void LoadEquivalenceNeqAC(const std::vector<Literal> enforcement_literal,
950 IntegerValue coeff1, IntegerVariable var1,
951 IntegerValue coeff2, IntegerVariable var2,
952 const IntegerValue rhs, Model* m) {
953 auto* encoder = m->GetOrCreate<IntegerEncoder>();
954 CHECK(encoder->VariableIsFullyEncoded(var1));
955 CHECK(encoder->VariableIsFullyEncoded(var2));
956 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
957 for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
958 term1_value_to_literal[coeff1 * value_literal.value] =
959 value_literal.literal;
960 }
961 for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
962 const IntegerValue target_value = rhs - value_literal.value * coeff2;
963 const auto& it = term1_value_to_literal.find(target_value);
964 if (it != term1_value_to_literal.end()) {
965 const Literal target_literal = it->second;
966 m->Add(EnforcedClause(
967 enforcement_literal,
968 {value_literal.literal.Negated(), target_literal.Negated()}));
969 }
970 }
971}
972
973} // namespace
974
976 auto* mapping = m->GetOrCreate<CpModelMapping>();
977 if (ct.linear().vars().empty()) {
978 const Domain rhs = ReadDomainFromProto(ct.linear());
979 if (rhs.Contains(0)) return;
981 std::vector<Literal> clause;
982 for (const int ref : ct.enforcement_literal()) {
983 clause.push_back(mapping->Literal(ref).Negated());
984 }
985 m->Add(ClauseConstraint(clause));
986 } else {
987 VLOG(1) << "Trivially UNSAT constraint: " << ct.DebugString();
988 m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
989 }
990 return;
991 }
992
993 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
994 const std::vector<IntegerVariable> vars =
995 mapping->Integers(ct.linear().vars());
996 const std::vector<int64_t> coeffs = ValuesFromProto(ct.linear().coeffs());
997
998 // Compute the min/max to relax the bounds if needed.
999 //
1000 // TODO(user): Reuse ComputeLinearBounds()? but then we need another loop
1001 // to detect if we only have Booleans.
1002 IntegerValue min_sum(0);
1003 IntegerValue max_sum(0);
1004 IntegerValue max_domain_size(0);
1005 bool all_booleans = true;
1006 for (int i = 0; i < vars.size(); ++i) {
1007 if (all_booleans && !mapping->IsBoolean(ct.linear().vars(i))) {
1008 all_booleans = false;
1009 }
1010 const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1011 const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1012 max_domain_size = std::max(max_domain_size, ub - lb + 1);
1013 const IntegerValue term_a = coeffs[i] * lb;
1014 const IntegerValue term_b = coeffs[i] * ub;
1015 min_sum += std::min(term_a, term_b);
1016 max_sum += std::max(term_a, term_b);
1017 }
1018
1019 const SatParameters& params = *m->GetOrCreate<SatParameters>();
1020 const IntegerValue domain_size_limit(
1022 if (ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1023 !integer_trail->IsFixed(vars[1]) &&
1024 max_domain_size <= domain_size_limit) {
1025 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1026 if (params.boolean_encoding_level() > 0 && ConstraintIsEq(ct.linear()) &&
1027 ct.linear().domain(0) != min_sum && ct.linear().domain(0) != max_sum &&
1028 encoder->VariableIsFullyEncoded(vars[0]) &&
1029 encoder->VariableIsFullyEncoded(vars[1])) {
1030 VLOG(3) << "Load AC version of " << ct.DebugString() << ", var0 domain = "
1031 << integer_trail->InitialVariableDomain(vars[0])
1032 << ", var1 domain = "
1033 << integer_trail->InitialVariableDomain(vars[1]);
1034 return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()),
1035 IntegerValue(coeffs[0]), vars[0],
1036 IntegerValue(coeffs[1]), vars[1],
1037 IntegerValue(ct.linear().domain(0)), m);
1038 }
1039
1040 int64_t single_value = 0;
1041 if (params.boolean_encoding_level() > 0 &&
1042 ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) &&
1043 single_value != min_sum && single_value != max_sum &&
1044 encoder->VariableIsFullyEncoded(vars[0]) &&
1045 encoder->VariableIsFullyEncoded(vars[1])) {
1046 VLOG(3) << "Load NAC version of " << ct.DebugString()
1047 << ", var0 domain = "
1048 << integer_trail->InitialVariableDomain(vars[0])
1049 << ", var1 domain = "
1050 << integer_trail->InitialVariableDomain(vars[1])
1051 << ", value = " << single_value;
1052 return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()),
1053 IntegerValue(coeffs[0]), vars[0],
1054 IntegerValue(coeffs[1]), vars[1],
1055 IntegerValue(single_value), m);
1056 }
1057 }
1058
1059 if (ct.linear().domain_size() == 2) {
1060 int64_t lb = ct.linear().domain(0);
1061 int64_t ub = ct.linear().domain(1);
1062 if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1063 if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1064
1065 if (!HasEnforcementLiteral(ct)) {
1066 if (all_booleans) {
1067 // TODO(user): we should probably also implement an
1068 // half-reified version of this constraint.
1069 std::vector<LiteralWithCoeff> cst;
1070 for (int i = 0; i < vars.size(); ++i) {
1071 const int ref = ct.linear().vars(i);
1072 cst.push_back({mapping->Literal(ref), coeffs[i]});
1073 }
1074 m->Add(BooleanLinearConstraint(lb, ub, &cst));
1075 } else {
1077 m->Add(WeightedSumGreaterOrEqual(vars, coeffs, lb));
1078 }
1080 m->Add(WeightedSumLowerOrEqual(vars, coeffs, ub));
1081 }
1082 }
1083 } else {
1084 const std::vector<Literal> enforcement_literals =
1085 mapping->Literals(ct.enforcement_literal());
1087 m->Add(ConditionalWeightedSumGreaterOrEqual(enforcement_literals, vars,
1088 coeffs, lb));
1089 }
1091 m->Add(ConditionalWeightedSumLowerOrEqual(enforcement_literals, vars,
1092 coeffs, ub));
1093 }
1094 }
1095 } else {
1096 // In this case, we can create just one Boolean instead of two since one
1097 // is the negation of the other.
1098 const bool special_case =
1099 ct.enforcement_literal().empty() && ct.linear().domain_size() == 4;
1100
1101 std::vector<Literal> clause;
1102 for (int i = 0; i < ct.linear().domain_size(); i += 2) {
1103 int64_t lb = ct.linear().domain(i);
1104 int64_t ub = ct.linear().domain(i + 1);
1105 if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1106 if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1107
1108 const Literal subdomain_literal(
1109 special_case && i > 0 ? clause.back().Negated()
1110 : Literal(m->Add(NewBooleanVariable()), true));
1111 clause.push_back(subdomain_literal);
1112
1114 m->Add(ConditionalWeightedSumGreaterOrEqual({subdomain_literal}, vars,
1115 coeffs, lb));
1116 }
1118 m->Add(ConditionalWeightedSumLowerOrEqual({subdomain_literal}, vars,
1119 coeffs, ub));
1120 }
1121 }
1122 for (const int ref : ct.enforcement_literal()) {
1123 clause.push_back(mapping->Literal(ref).Negated());
1124 }
1125 if (!special_case) m->Add(ClauseConstraint(clause));
1126 }
1127}
1128
1130 auto* mapping = m->GetOrCreate<CpModelMapping>();
1131 const std::vector<AffineExpression> expressions =
1132 mapping->Affines(ct.all_diff().exprs());
1133 m->Add(AllDifferentOnBounds(expressions));
1134}
1135
1137 auto* mapping = m->GetOrCreate<CpModelMapping>();
1138 const AffineExpression prod = mapping->Affine(ct.int_prod().target());
1139 CHECK_EQ(ct.int_prod().exprs_size(), 2)
1140 << "General int_prod not supported yet.";
1141
1142 const AffineExpression expr0 = mapping->Affine(ct.int_prod().exprs(0));
1143 const AffineExpression expr1 = mapping->Affine(ct.int_prod().exprs(1));
1144 if (VLOG_IS_ON(1)) {
1145 LinearConstraintBuilder builder(m);
1146 if (DetectLinearEncodingOfProducts(expr0, expr1, m, &builder)) {
1147 VLOG(1) << "Product " << ct.DebugString() << " can be linearized";
1148 }
1149 }
1150 m->Add(ProductConstraint(expr0, expr1, prod));
1151}
1152
1154 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1155 auto* mapping = m->GetOrCreate<CpModelMapping>();
1156 const AffineExpression div = mapping->Affine(ct.int_div().target());
1157 const AffineExpression num = mapping->Affine(ct.int_div().exprs(0));
1158 const AffineExpression denom = mapping->Affine(ct.int_div().exprs(1));
1159 if (integer_trail->IsFixed(denom)) {
1160 m->Add(FixedDivisionConstraint(num, integer_trail->FixedValue(denom), div));
1161 } else {
1162 if (VLOG_IS_ON(1)) {
1163 LinearConstraintBuilder builder(m);
1164 if (DetectLinearEncodingOfProducts(num, denom, m, &builder)) {
1165 VLOG(1) << "Division " << ct.DebugString() << " can be linearized";
1166 }
1167 }
1168 m->Add(DivisionConstraint(num, denom, div));
1169 }
1170}
1171
1173 auto* mapping = m->GetOrCreate<CpModelMapping>();
1174 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1175
1176 const AffineExpression target = mapping->Affine(ct.int_mod().target());
1177 const AffineExpression expr = mapping->Affine(ct.int_mod().exprs(0));
1178 const AffineExpression mod = mapping->Affine(ct.int_mod().exprs(1));
1179 CHECK(integer_trail->IsFixed(mod));
1180 const IntegerValue fixed_modulo = integer_trail->FixedValue(mod);
1181 m->Add(FixedModuloConstraint(expr, fixed_modulo, target));
1182}
1183
1185 if (ct.lin_max().exprs().empty()) {
1186 m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1187 return;
1188 }
1189
1190 auto* mapping = m->GetOrCreate<CpModelMapping>();
1191 const LinearExpression max = mapping->GetExprFromProto(ct.lin_max().target());
1192 std::vector<LinearExpression> negated_exprs;
1193 negated_exprs.reserve(ct.lin_max().exprs_size());
1194 for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
1195 negated_exprs.push_back(
1196 NegationOf(mapping->GetExprFromProto(ct.lin_max().exprs(i))));
1197 }
1198 // TODO(user): Consider replacing the min propagator by max.
1199 m->Add(IsEqualToMinOf(NegationOf(max), negated_exprs));
1200}
1201
1203 auto* mapping = m->GetOrCreate<CpModelMapping>();
1204 m->Add(Disjunctive(mapping->Intervals(ct.no_overlap().intervals())));
1205}
1206
1208 if (ct.no_overlap_2d().x_intervals().empty()) return;
1209 auto* mapping = m->GetOrCreate<CpModelMapping>();
1210 const std::vector<IntervalVariable> x_intervals =
1211 mapping->Intervals(ct.no_overlap_2d().x_intervals());
1212 const std::vector<IntervalVariable> y_intervals =
1213 mapping->Intervals(ct.no_overlap_2d().y_intervals());
1215 x_intervals, y_intervals,
1216 !ct.no_overlap_2d().boxes_with_null_area_can_overlap(),
1218}
1219
1221 auto* mapping = m->GetOrCreate<CpModelMapping>();
1222 const std::vector<IntervalVariable> intervals =
1223 mapping->Intervals(ct.cumulative().intervals());
1224 const AffineExpression capacity = mapping->Affine(ct.cumulative().capacity());
1225 const std::vector<AffineExpression> demands =
1226 mapping->Affines(ct.cumulative().demands());
1227 m->Add(Cumulative(intervals, demands, capacity));
1228}
1229
1231 const auto& circuit = ct.circuit();
1232 if (circuit.tails().empty()) return;
1233
1234 std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1235 std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1236 std::vector<Literal> literals =
1237 m->GetOrCreate<CpModelMapping>()->Literals(circuit.literals());
1238 const int num_nodes = ReindexArcs(&tails, &heads);
1239 m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals));
1240}
1241
1243 const auto& routes = ct.routes();
1244 if (routes.tails().empty()) return;
1245
1246 std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1247 std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1248 std::vector<Literal> literals =
1249 m->GetOrCreate<CpModelMapping>()->Literals(routes.literals());
1250 const int num_nodes = ReindexArcs(&tails, &heads);
1251 m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals,
1252 /*multiple_subcircuit_through_zero=*/true));
1253}
1254
1256 switch (ct.constraint_case()) {
1257 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1258 return true;
1259 case ConstraintProto::ConstraintCase::kBoolOr:
1261 return true;
1262 case ConstraintProto::ConstraintCase::kBoolAnd:
1264 return true;
1265 case ConstraintProto::ConstraintCase::kAtMostOne:
1267 return true;
1268 case ConstraintProto::ConstraintCase::kExactlyOne:
1270 return true;
1271 case ConstraintProto::ConstraintCase::kBoolXor:
1273 return true;
1274 case ConstraintProto::ConstraintProto::kLinear:
1276 return true;
1277 case ConstraintProto::ConstraintProto::kAllDiff:
1279 return true;
1280 case ConstraintProto::ConstraintProto::kIntProd:
1282 return true;
1283 case ConstraintProto::ConstraintProto::kIntDiv:
1285 return true;
1286 case ConstraintProto::ConstraintProto::kIntMod:
1288 return true;
1289 case ConstraintProto::ConstraintProto::kLinMax:
1291 return true;
1292 case ConstraintProto::ConstraintProto::kInterval:
1293 // Already dealt with.
1294 return true;
1295 case ConstraintProto::ConstraintProto::kNoOverlap:
1297 return true;
1298 case ConstraintProto::ConstraintProto::kNoOverlap2D:
1300 return true;
1301 case ConstraintProto::ConstraintProto::kCumulative:
1303 return true;
1304 case ConstraintProto::ConstraintProto::kCircuit:
1306 return true;
1307 case ConstraintProto::ConstraintProto::kRoutes:
1309 return true;
1310 default:
1311 return false;
1312 }
1313}
1314
1315} // namespace sat
1316} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:492
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:699
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:890
#define VLOG(verboselevel)
Definition: base/logging.h:980
We call domain any subset of Int64 = [kint64min, kint64max].
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
Domain Complement() const
Returns the set Int64 ∖ D.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t Max() const
Returns the max value of the domain.
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
std::vector< AffineExpression > Affines(const List &list) const
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
const ::operations_research::sat::SymmetryProto & symmetry() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
static constexpr DomainReductionStrategy SELECT_MEDIAN_VALUE
Definition: cp_model.pb.h:5274
void ReserveSpaceForNumVariables(int num_vars)
Definition: integer.cc:611
Literal(int signed_value)
Definition: sat_base.h:70
LiteralIndex Index() const
Definition: sat_base.h:86
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
T Add(std::function< T(Model *)> f)
This makes it possible to have a nicer API on the client side, and it allows both of these forms:
Definition: sat/model.h:81
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:106
int32_t max_domain_size_when_encoding_eq_neq_constraints() const
bool AddProblemClause(absl::Span< const Literal > literals)
Definition: sat_solver.cc:204
SatParameters parameters
CpModelProto proto
CpModelProto const * model_proto
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
std::function< std::vector< ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1773
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:92
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:468
std::function< void(Model *)> LiteralXorIs(const std::vector< Literal > &literals, bool value)
void LoadExactlyOneConstraint(const ConstraintProto &ct, Model *m)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void LoadIntProdConstraint(const ConstraintProto &ct, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void LoadBoolOrConstraint(const ConstraintProto &ct, Model *m)
bool RefIsPositive(int ref)
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:361
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> ProductConstraint(AffineExpression a, AffineExpression b, AffineExpression p)
Definition: integer_expr.h:834
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:906
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
Definition: sat_solver.h:952
std::function< void(Model *)> SubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, bool multiple_subcircuit_through_zero)
Definition: circuit.cc:499
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition: integer.h:1598
std::function< void(Model *)> FixedDivisionConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
Definition: integer_expr.h:876
bool HasEnforcementLiteral(const ConstraintProto &ct)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> ConditionalWeightedSumGreaterOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:578
void LoadCumulativeConstraint(const ConstraintProto &ct, Model *m)
void LoadRoutesConstraint(const ConstraintProto &ct, Model *m)
void LoadBoolAndConstraint(const ConstraintProto &ct, Model *m)
void LoadLinMaxConstraint(const ConstraintProto &ct, Model *m)
void LoadBoolXorConstraint(const ConstraintProto &ct, Model *m)
void LoadIntModConstraint(const ConstraintProto &ct, Model *m)
const IntegerVariable kNoIntegerVariable(-1)
const IntervalVariable kNoIntervalVariable(-1)
std::function< void(Model *)> ConditionalWeightedSumLowerOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:490
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
Definition: cumulative.cc:35
void LoadIntDivConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> DivisionConstraint(AffineExpression num, AffineExpression denom, AffineExpression div)
Definition: integer_expr.h:857
std::function< void(Model *)> Implication(const std::vector< Literal > &enforcement_literals, IntegerLiteral i)
Definition: integer.h:1724
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:892
bool DetectLinearEncodingOfProducts(const AffineExpression &left, const AffineExpression &right, Model *model, LinearConstraintBuilder *builder)
int ReindexArcs(IntContainer *tails, IntContainer *heads)
Definition: circuit.h:168
std::function< void(Model *)> Disjunctive(const std::vector< IntervalVariable > &vars)
Definition: disjunctive.cc:30
void LoadAtMostOneConstraint(const ConstraintProto &ct, Model *m)
void LoadCircuitConstraint(const ConstraintProto &ct, Model *m)
void LoadNoOverlapConstraint(const ConstraintProto &ct, Model *m)
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
void LoadAllDiffConstraint(const ConstraintProto &ct, Model *m)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:30
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:737
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void LoadNoOverlap2dConstraint(const ConstraintProto &ct, Model *m)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
std::function< void(Model *)> NonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, bool is_strict, bool add_cumulative_relaxation=true)
Definition: diffn.h:145
std::function< void(Model *)> BooleanLinearConstraint(int64_t lower_bound, int64_t upper_bound, std::vector< LiteralWithCoeff > *cst)
Definition: sat_solver.h:853
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:878
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> FixedModuloConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
Definition: integer_expr.h:890
const BooleanVariable kNoBooleanVariable(-1)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:85
int64_t capacity
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1383
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1377
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41