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