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