OR-Tools  9.2
presolve_context.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 <string>
20
28
29namespace operations_research {
30namespace sat {
31
33 return context->GetLiteralRepresentative(ref_);
34}
35
37 return context->GetVariableRepresentative(ref_);
38}
39
40void PresolveContext::ClearStats() { stats_by_rule_name_.clear(); }
41
44 FillDomainInProto(domain, var);
46 return working_model->variables_size() - 1;
47}
48
50
52 if (!constant_to_ref_.contains(cst)) {
53 constant_to_ref_[cst] = SavedVariable(working_model->variables_size());
55 var_proto->add_domain(cst);
56 var_proto->add_domain(cst);
58 }
59 return constant_to_ref_[cst].Get(this);
60}
61
62// a => b.
65 ct->add_enforcement_literal(a);
66 ct->mutable_bool_and()->add_literals(b);
67}
68
69// b => x in [lb, ub].
70void PresolveContext::AddImplyInDomain(int b, int x, const Domain& domain) {
72
73 // Doing it like this seems to use slightly less memory.
74 // TODO(user): Find the best way to create such small proto.
75 imply->mutable_enforcement_literal()->Resize(1, b);
76 LinearConstraintProto* mutable_linear = imply->mutable_linear();
77 mutable_linear->mutable_vars()->Resize(1, x);
78 mutable_linear->mutable_coeffs()->Resize(1, 1);
79 FillDomainInProto(domain, mutable_linear);
80}
81
82bool PresolveContext::DomainIsEmpty(int ref) const {
83 return domains[PositiveRef(ref)].IsEmpty();
84}
85
86bool PresolveContext::IsFixed(int ref) const {
87 DCHECK_LT(PositiveRef(ref), domains.size());
88 DCHECK(!DomainIsEmpty(ref));
89 return domains[PositiveRef(ref)].IsFixed();
90}
91
93 const int var = PositiveRef(ref);
94 return domains[var].Min() >= 0 && domains[var].Max() <= 1;
95}
96
97bool PresolveContext::LiteralIsTrue(int lit) const {
99 if (RefIsPositive(lit)) {
100 return domains[lit].Min() == 1;
101 } else {
102 return domains[PositiveRef(lit)].Max() == 0;
103 }
104}
105
108 if (RefIsPositive(lit)) {
109 return domains[lit].Max() == 0;
110 } else {
111 return domains[PositiveRef(lit)].Min() == 1;
112 }
113}
114
115int64_t PresolveContext::MinOf(int ref) const {
116 DCHECK(!DomainIsEmpty(ref));
117 return RefIsPositive(ref) ? domains[PositiveRef(ref)].Min()
118 : -domains[PositiveRef(ref)].Max();
119}
120
121int64_t PresolveContext::MaxOf(int ref) const {
122 DCHECK(!DomainIsEmpty(ref));
123 return RefIsPositive(ref) ? domains[PositiveRef(ref)].Max()
124 : -domains[PositiveRef(ref)].Min();
125}
126
127int64_t PresolveContext::FixedValue(int ref) const {
128 DCHECK(!DomainIsEmpty(ref));
129 CHECK(IsFixed(ref));
130 return RefIsPositive(ref) ? domains[PositiveRef(ref)].Min()
131 : -domains[PositiveRef(ref)].Min();
132}
133
135 int64_t result = expr.offset();
136 for (int i = 0; i < expr.vars_size(); ++i) {
137 const int64_t coeff = expr.coeffs(i);
138 if (coeff > 0) {
139 result += coeff * MinOf(expr.vars(i));
140 } else {
141 result += coeff * MaxOf(expr.vars(i));
142 }
143 }
144 return result;
145}
146
148 int64_t result = expr.offset();
149 for (int i = 0; i < expr.vars_size(); ++i) {
150 const int64_t coeff = expr.coeffs(i);
151 if (coeff > 0) {
152 result += coeff * MaxOf(expr.vars(i));
153 } else {
154 result += coeff * MinOf(expr.vars(i));
155 }
156 }
157 return result;
158}
159
161 for (int i = 0; i < expr.vars_size(); ++i) {
162 if (expr.coeffs(i) != 0 && !IsFixed(expr.vars(i))) return false;
163 }
164 return true;
165}
166
168 int64_t result = expr.offset();
169 for (int i = 0; i < expr.vars_size(); ++i) {
170 if (expr.coeffs(i) == 0) continue;
171 result += expr.coeffs(i) * FixedValue(expr.vars(i));
172 }
173 return result;
174}
175
177 const LinearExpressionProto& expr) const {
178 Domain result(expr.offset());
179 for (int i = 0; i < expr.vars_size(); ++i) {
180 result = result.AdditionWith(
181 DomainOf(expr.vars(i)).MultiplicationBy(expr.coeffs(i)));
182 }
183 return result;
184}
185
187 const LinearExpressionProto& expr) const {
188 if (expr.vars().size() != 1) return false;
189 return CanBeUsedAsLiteral(expr.vars(0));
190}
191
193 const LinearExpressionProto& expr) const {
194 const int ref = expr.vars(0);
195 return RefIsPositive(ref) == (expr.coeffs(0) > 0) ? ref : NegatedRef(ref);
196}
197
199 const LinearExpressionProto& expr) const {
200 return expr.offset() == 0 && expr.vars_size() == 1 && expr.coeffs(0) == 1;
201}
202
204 int* literal) const {
205 if (expr.vars_size() != 1) return false;
206 const int ref = expr.vars(0);
207 const int var = PositiveRef(ref);
208 if (MinOf(var) < 0 || MaxOf(var) > 1) return false;
209
210 if (expr.offset() == 0 && expr.coeffs(0) == 1 && RefIsPositive(ref)) {
211 if (literal != nullptr) *literal = ref;
212 return true;
213 }
214 if (expr.offset() == 1 && expr.coeffs(0) == -1 && RefIsPositive(ref)) {
215 if (literal != nullptr) *literal = NegatedRef(ref);
216 return true;
217 }
218 if (expr.offset() == 1 && expr.coeffs(0) == 1 && !RefIsPositive(ref)) {
219 if (literal != nullptr) *literal = ref;
220 return true;
221 }
222 return false;
223}
224
225// Note that we only support converted intervals.
228 if (!proto.enforcement_literal().empty()) return false;
229 if (!IsFixed(proto.interval().start())) return false;
230 if (!IsFixed(proto.interval().size())) return false;
231 if (!IsFixed(proto.interval().end())) return false;
232 return true;
233}
234
235std::string PresolveContext::IntervalDebugString(int ct_ref) const {
236 if (IntervalIsConstant(ct_ref)) {
237 return absl::StrCat("interval_", ct_ref, "(", StartMin(ct_ref), "..",
238 EndMax(ct_ref), ")");
239 } else if (ConstraintIsOptional(ct_ref)) {
240 const int literal =
242 if (SizeMin(ct_ref) == SizeMax(ct_ref)) {
243 return absl::StrCat("interval_", ct_ref, "(lit=", literal, ", ",
244 StartMin(ct_ref), " --(", SizeMin(ct_ref), ")--> ",
245 EndMax(ct_ref), ")");
246 } else {
247 return absl::StrCat("interval_", ct_ref, "(lit=", literal, ", ",
248 StartMin(ct_ref), " --(", SizeMin(ct_ref), "..",
249 SizeMax(ct_ref), ")--> ", EndMax(ct_ref), ")");
250 }
251 } else if (SizeMin(ct_ref) == SizeMax(ct_ref)) {
252 return absl::StrCat("interval_", ct_ref, "(", StartMin(ct_ref), " --(",
253 SizeMin(ct_ref), ")--> ", EndMax(ct_ref), ")");
254 } else {
255 return absl::StrCat("interval_", ct_ref, "(", StartMin(ct_ref), " --(",
256 SizeMin(ct_ref), "..", SizeMax(ct_ref), ")--> ",
257 EndMax(ct_ref), ")");
258 }
259}
260
261int64_t PresolveContext::StartMin(int ct_ref) const {
264 return MinOf(interval.start());
265}
266
267int64_t PresolveContext::StartMax(int ct_ref) const {
270 return MaxOf(interval.start());
271}
272
273int64_t PresolveContext::EndMin(int ct_ref) const {
276 return MinOf(interval.end());
277}
278
279int64_t PresolveContext::EndMax(int ct_ref) const {
282 return MaxOf(interval.end());
283}
284
285int64_t PresolveContext::SizeMin(int ct_ref) const {
288 return MinOf(interval.size());
289}
290
291int64_t PresolveContext::SizeMax(int ct_ref) const {
294 return MaxOf(interval.size());
295}
296
297// Important: To be sure a variable can be removed, we need it to not be a
298// representative of both affine and equivalence relation.
299bool PresolveContext::VariableIsNotRepresentativeOfEquivalenceClass(
300 int var) const {
302 if (affine_relations_.ClassSize(var) > 1 &&
303 affine_relations_.Get(var).representative == var) {
304 return false;
305 }
306 if (var_equiv_relations_.ClassSize(var) > 1 &&
307 var_equiv_relations_.Get(var).representative == var) {
308 return false;
309 }
310 return true;
311}
312
314 const int var = PositiveRef(ref);
315 return VariableIsNotRepresentativeOfEquivalenceClass(var) &&
317}
318
319// Tricky: If this variable is equivalent to another one (but not the
320// representative) and appear in just one constraint, then this constraint must
321// be the affine defining one. And in this case the code using this function
322// should do the proper stuff.
324 if (!ConstraintVariableGraphIsUpToDate()) return false;
325 const int var = PositiveRef(ref);
326 return var_to_constraints_[var].size() == 1 && VariableIsRemovable(var);
327}
328
330 if (!ConstraintVariableGraphIsUpToDate()) return false;
331 const int var = PositiveRef(ref);
332 return VariableIsNotRepresentativeOfEquivalenceClass(var) &&
333 var_to_constraints_[var].contains(kObjectiveConstraint) &&
334 var_to_constraints_[var].size() == 2;
335}
336
337// Tricky: Same remark as for VariableIsUniqueAndRemovable().
338//
339// Also if the objective domain is constraining, we can't have a preferred
340// direction, so we cannot easily remove such variable.
342 if (!ConstraintVariableGraphIsUpToDate()) return false;
343 const int var = PositiveRef(ref);
344 return VariableIsRemovable(var) && !objective_domain_is_constraining_ &&
346}
347
348// Here, even if the variable is equivalent to others, if its affine defining
349// constraints where removed, then it is not needed anymore.
351 if (!ConstraintVariableGraphIsUpToDate()) return false;
352 return var_to_constraints_[PositiveRef(ref)].empty();
353}
354
356 removed_variables_.insert(PositiveRef(ref));
357}
358
359// Note(user): I added an indirection and a function for this to be able to
360// display debug information when this return false. This should actually never
361// return false in the cases where it is used.
363 // It is okay to reuse removed fixed variable.
364 if (IsFixed(ref)) return false;
365 if (!removed_variables_.contains(PositiveRef(ref))) return false;
366 if (!var_to_constraints_[PositiveRef(ref)].empty()) {
367 SOLVER_LOG(logger_, "Variable ", PositiveRef(ref),
368 " was removed, yet it appears in some constraints!");
369 SOLVER_LOG(logger_, "affine relation: ",
371 for (const int c : var_to_constraints_[PositiveRef(ref)]) {
373 logger_, "constraint #", c, " : ",
374 c >= 0 ? working_model->constraints(c).ShortDebugString() : "");
375 }
376 }
377 return true;
378}
379
381 int ref) const {
382 if (!ConstraintVariableGraphIsUpToDate()) return false;
383 const int var = PositiveRef(ref);
384 return var_to_num_linear1_[var] == var_to_constraints_[var].size() ||
385 (var_to_constraints_[var].contains(kObjectiveConstraint) &&
386 var_to_num_linear1_[var] + 1 == var_to_constraints_[var].size());
387}
388
390 Domain result;
391 if (RefIsPositive(ref)) {
392 result = domains[ref];
393 } else {
394 result = domains[PositiveRef(ref)].Negation();
395 }
396 return result;
397}
398
399bool PresolveContext::DomainContains(int ref, int64_t value) const {
400 if (!RefIsPositive(ref)) {
401 return domains[PositiveRef(ref)].Contains(-value);
402 }
403 return domains[ref].Contains(value);
404}
405
407 int64_t value) const {
408 CHECK_LE(expr.vars_size(), 1);
409 if (IsFixed(expr)) {
410 return FixedValue(expr) == value;
411 }
412 if ((value - expr.offset()) % expr.coeffs(0) != 0) return false;
413 return DomainContains(expr.vars(0), (value - expr.offset()) / expr.coeffs(0));
414}
415
416ABSL_MUST_USE_RESULT bool PresolveContext::IntersectDomainWith(
417 int ref, const Domain& domain, bool* domain_modified) {
418 DCHECK(!DomainIsEmpty(ref));
419 const int var = PositiveRef(ref);
420
421 if (RefIsPositive(ref)) {
422 if (domains[var].IsIncludedIn(domain)) {
423 return true;
424 }
425 domains[var] = domains[var].IntersectionWith(domain);
426 } else {
427 const Domain temp = domain.Negation();
428 if (domains[var].IsIncludedIn(temp)) {
429 return true;
430 }
431 domains[var] = domains[var].IntersectionWith(temp);
432 }
433
434 if (domain_modified != nullptr) {
435 *domain_modified = true;
436 }
438 if (domains[var].IsEmpty()) {
439 is_unsat_ = true;
440 return false;
441 }
442
443 // Propagate the domain of the representative right away.
444 // Note that the recursive call should only by one level deep.
446 if (r.representative == var) return true;
449 .AdditionWith(Domain(-r.offset))
451}
452
453ABSL_MUST_USE_RESULT bool PresolveContext::IntersectDomainWith(
454 const LinearExpressionProto& expr, const Domain& domain,
455 bool* domain_modified) {
456 if (expr.vars().empty()) {
457 if (domain.Contains(expr.offset())) {
458 return true;
459 } else {
460 is_unsat_ = true;
461 return false;
462 }
463 }
464 if (expr.vars().size() == 1) { // Affine
465 return IntersectDomainWith(expr.vars(0),
466 domain.AdditionWith(Domain(-expr.offset()))
468 domain_modified);
469 }
470
471 // We don't do anything for longer expression for now.
472 return true;
473}
474
475ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToFalse(int lit) {
476 const int var = PositiveRef(lit);
477 const int64_t value = RefIsPositive(lit) ? 0 : 1;
479}
480
481ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToTrue(int lit) {
482 return SetLiteralToFalse(NegatedRef(lit));
483}
484
487 if (ct.constraint_case() ==
488 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
489 return true;
490 }
491 for (const int literal : ct.enforcement_literal()) {
492 if (LiteralIsFalse(literal)) return true;
493 }
494 return false;
495}
496
499 bool contains_one_free_literal = false;
500 for (const int literal : ct.enforcement_literal()) {
501 if (LiteralIsFalse(literal)) return false;
502 if (!LiteralIsTrue(literal)) contains_one_free_literal = true;
503 }
504 return contains_one_free_literal;
505}
506
507void PresolveContext::UpdateRuleStats(const std::string& name, int num_times) {
508 // We only count if we are going to display it.
509 if (logger_->LoggingIsEnabled()) {
510 VLOG(2) << num_presolve_operations << " : " << name;
511 stats_by_rule_name_[name] += num_times;
512 }
513 num_presolve_operations += num_times;
514}
515
516void PresolveContext::UpdateLinear1Usage(const ConstraintProto& ct, int c) {
517 const int old_var = constraint_to_linear1_var_[c];
518 if (old_var >= 0) {
519 var_to_num_linear1_[old_var]--;
520 }
521 if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
522 ct.linear().vars().size() == 1) {
523 const int var = PositiveRef(ct.linear().vars(0));
524 constraint_to_linear1_var_[c] = var;
525 var_to_num_linear1_[var]++;
526 }
527}
528
529void PresolveContext::AddVariableUsage(int c) {
530 const ConstraintProto& ct = working_model->constraints(c);
531 constraint_to_vars_[c] = UsedVariables(ct);
532 constraint_to_intervals_[c] = UsedIntervals(ct);
533 for (const int v : constraint_to_vars_[c]) {
535 var_to_constraints_[v].insert(c);
536 }
537 for (const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
538 UpdateLinear1Usage(ct, c);
539}
540
542 if (is_unsat_) return;
543 DCHECK_EQ(constraint_to_vars_.size(), working_model->constraints_size());
545
546 // We don't optimize the interval usage as this is not super frequent.
547 for (const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
548 constraint_to_intervals_[c] = UsedIntervals(ct);
549 for (const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
550
551 // For the variables, we avoid an erase() followed by an insert() for the
552 // variables that didn't change.
553 tmp_new_usage_ = UsedVariables(ct);
554 const std::vector<int>& old_usage = constraint_to_vars_[c];
555 const int old_size = old_usage.size();
556 int i = 0;
557 for (const int var : tmp_new_usage_) {
559 while (i < old_size && old_usage[i] < var) {
560 var_to_constraints_[old_usage[i]].erase(c);
561 ++i;
562 }
563 if (i < old_size && old_usage[i] == var) {
564 ++i;
565 } else {
566 var_to_constraints_[var].insert(c);
567 }
568 }
569 for (; i < old_size; ++i) var_to_constraints_[old_usage[i]].erase(c);
570 constraint_to_vars_[c] = tmp_new_usage_;
571
572 UpdateLinear1Usage(ct, c);
573}
574
576 return constraint_to_vars_.size() == working_model->constraints_size();
577}
578
580 if (is_unsat_) return;
581 const int old_size = constraint_to_vars_.size();
582 const int new_size = working_model->constraints_size();
583 CHECK_LE(old_size, new_size);
584 constraint_to_vars_.resize(new_size);
585 constraint_to_linear1_var_.resize(new_size, -1);
586 constraint_to_intervals_.resize(new_size);
587 interval_usage_.resize(new_size);
588 for (int c = old_size; c < new_size; ++c) {
589 AddVariableUsage(c);
590 }
591}
592
593// TODO(user): Also test var_to_constraints_ !!
595 if (is_unsat_) return true; // We do not care in this case.
596 if (constraint_to_vars_.size() != working_model->constraints_size()) {
597 LOG(INFO) << "Wrong constraint_to_vars size!";
598 return false;
599 }
600 for (int c = 0; c < constraint_to_vars_.size(); ++c) {
601 if (constraint_to_vars_[c] !=
603 LOG(INFO) << "Wrong variables usage for constraint: \n"
605 << "old_size: " << constraint_to_vars_[c].size();
606 return false;
607 }
608 }
609 int num_in_objective = 0;
610 for (int v = 0; v < var_to_constraints_.size(); ++v) {
611 if (var_to_constraints_[v].contains(kObjectiveConstraint)) {
612 ++num_in_objective;
613 if (!objective_map_.contains(v)) {
614 LOG(INFO) << "Variable " << v
615 << " is marked as part of the objective but isn't.";
616 return false;
617 }
618 }
619 }
620 if (num_in_objective != objective_map_.size()) {
621 LOG(INFO) << "Not all variables are marked as part of the objective";
622 return false;
623 }
624
625 return true;
626}
627
628// If a Boolean variable (one with domain [0, 1]) appear in this affine
629// equivalence class, then we want its representative to be Boolean. Note that
630// this is always possible because a Boolean variable can never be equal to a
631// multiple of another if std::abs(coeff) is greater than 1 and if it is not
632// fixed to zero. This is important because it allows to simply use the same
633// representative for any referenced literals.
634//
635// Note(user): When both domain contains [0,1] and later the wrong variable
636// become usable as boolean, then we have a bug. Because of that, the code
637// for GetLiteralRepresentative() is not as simple as it should be.
638bool PresolveContext::AddRelation(int x, int y, int64_t c, int64_t o,
639 AffineRelation* repo) {
640 // When the coefficient is larger than one, then if later one variable becomes
641 // Boolean, it must be the representative.
642 if (std::abs(c) != 1) return repo->TryAdd(x, y, c, o);
643
646
647 // To avoid integer overflow, we always want to use the representative with
648 // the smallest domain magnitude. Otherwise we might express a variable in say
649 // [0, 3] as ([x, x + 3] - x) for an arbitrary large x, and substituting
650 // something like this in a linear expression could break our overflow
651 // precondition.
652 //
653 // Note that if either rep_x or rep_y can be used as a literal, then it will
654 // also be the variable with the smallest domain magnitude (1 or 0 if fixed).
655 const int rep_x = repo->Get(x).representative;
656 const int rep_y = repo->Get(y).representative;
657 const int64_t m_x = std::max(std::abs(MinOf(rep_x)), std::abs(MaxOf(rep_x)));
658 const int64_t m_y = std::max(std::abs(MinOf(rep_y)), std::abs(MaxOf(rep_y)));
659 bool allow_rep_x = m_x < m_y;
660 bool allow_rep_y = m_y < m_x;
661 if (m_x == m_y) {
662 // If both magnitude are the same, we prefer a positive domain.
663 // This is important so we don't use [-1, 0] as a representative for [0, 1].
664 allow_rep_x = MinOf(rep_x) >= MinOf(rep_y);
665 allow_rep_y = MinOf(rep_y) >= MinOf(rep_x);
666 }
667 if (allow_rep_x && allow_rep_y) {
668 // If both representative are okay, we force the choice to the variable
669 // with lower index. This is needed because we have two "equivalence"
670 // relations, and we want the same representative in both.
671 if (rep_x < rep_y) {
672 allow_rep_y = false;
673 } else {
674 allow_rep_x = false;
675 }
676 }
677 return repo->TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
678}
679
680// Note that we just add the relation to the var_equiv_relations_, not to the
681// affine one. This is enough, and should prevent overflow in the affine
682// relation class: if we keep chaining variable fixed to zero, the coefficient
683// in the relation can overflow. For instance if x = 200 y and z = 200 t,
684// nothing prevent us if all end up being zero, to say y = z, which will result
685// in x = 200^2 t. If we make a few bad choices like this, then we can have an
686// overflow.
690 const int64_t min = MinOf(var);
691 if (constant_to_ref_.contains(min)) {
692 const int rep = constant_to_ref_[min].Get(this);
693 if (RefIsPositive(rep)) {
694 if (rep != var) {
695 AddRelation(var, rep, 1, 0, &var_equiv_relations_);
696 }
697 } else {
698 if (PositiveRef(rep) == var) {
699 CHECK_EQ(min, 0);
700 } else {
701 AddRelation(var, PositiveRef(rep), -1, 0, &var_equiv_relations_);
702 }
703 }
704 } else {
705 constant_to_ref_[min] = SavedVariable(var);
706 }
707}
708
710 const int var = PositiveRef(ref);
712 if (r.representative == var) return true;
713
714 // Propagate domains both ways.
715 // var = coeff * rep + offset
718 .AdditionWith(Domain(-r.offset))
720 return false;
721 }
724 .AdditionWith(Domain(r.offset)))) {
725 return false;
726 }
727
728 return true;
729}
730
732 for (auto& ref_map : var_to_constraints_) {
733 ref_map.erase(kAffineRelationConstraint);
734 }
735}
736
737// We only call that for a non representative variable that is only used in
738// the kAffineRelationConstraint. Such variable can be ignored and should never
739// be seen again in the presolve.
741 const int rep = GetAffineRelation(var).representative;
742
744 CHECK_NE(var, rep);
745 CHECK_EQ(var_to_constraints_[var].size(), 1);
746 CHECK(var_to_constraints_[var].contains(kAffineRelationConstraint));
747 CHECK(var_to_constraints_[rep].contains(kAffineRelationConstraint));
748
749 // We shouldn't reuse this variable again!
751
752 var_to_constraints_[var].erase(kAffineRelationConstraint);
753 affine_relations_.IgnoreFromClassSize(var);
754 var_equiv_relations_.IgnoreFromClassSize(var);
755
756 // If the representative is left alone, we can remove it from the special
757 // affine relation constraint too.
758 if (affine_relations_.ClassSize(rep) == 1 &&
759 var_equiv_relations_.ClassSize(rep) == 1) {
760 var_to_constraints_[rep].erase(kAffineRelationConstraint);
761 }
762
763 if (VLOG_IS_ON(2)) {
764 LOG(INFO) << "Removing affine relation: " << AffineRelationDebugString(var);
765 }
766}
767
769 const int var = GetAffineRelation(ref).representative;
770 const int64_t min = MinOf(var);
771 if (min == 0 || IsFixed(var)) return; // Nothing to do.
772
773 const int new_var = NewIntVar(DomainOf(var).AdditionWith(Domain(-min)));
774 CHECK(StoreAffineRelation(var, new_var, 1, min, /*debug_no_recursion=*/true));
775 UpdateRuleStats("variables: canonicalize domain");
777}
778
782 const auto& objective = working_model->floating_point_objective();
783 std::vector<std::pair<int, double>> terms;
784 for (int i = 0; i < objective.vars_size(); ++i) {
785 DCHECK(RefIsPositive(objective.vars(i)));
786 terms.push_back({objective.vars(i), objective.coeffs(i)});
787 }
788 const double offset = objective.offset();
789 const bool maximize = objective.maximize();
791
792 // We need the domains up to date before scaling.
794 return ScaleAndSetObjective(params_, terms, offset, maximize, working_model,
795 logger_);
796}
797
799 int64_t mod, int64_t rhs) {
800 CHECK_NE(mod, 0);
801 CHECK_NE(coeff, 0);
802
803 const int64_t gcd = std::gcd(coeff, mod);
804 if (gcd != 1) {
805 if (rhs % gcd != 0) {
807 absl::StrCat("Infeasible ", coeff, " * X = ", rhs, " % ", mod));
808 }
809 coeff /= gcd;
810 mod /= gcd;
811 rhs /= gcd;
812 }
813
814 // We just abort in this case as there is no point introducing a new variable.
815 if (std::abs(mod) == 1) return true;
816
817 int var = ref;
818 if (!RefIsPositive(var)) {
819 var = NegatedRef(ref);
820 coeff = -coeff;
821 rhs = -rhs;
822 }
823
824 // From var * coeff % mod = rhs
825 // We have var = mod * X + offset.
826 const int64_t offset = ProductWithModularInverse(coeff, mod, rhs);
827
828 // Lets create a new integer variable and add the affine relation.
829 const Domain new_domain =
831 if (new_domain.IsEmpty()) {
833 "Empty domain in CanonicalizeAffineVariable()");
834 }
835 if (new_domain.IsFixed()) {
836 UpdateRuleStats("variables: fixed value due to affine relation");
837 return IntersectDomainWith(
839 Domain(offset)));
840 }
841
842 // We make sure the new variable has a domain starting at zero to minimize
843 // future overflow issues. If it end up Boolean, it is also nice to be able to
844 // use it as such.
845 //
846 // A potential problem with this is that it messes up the natural variable
847 // order chosen by the modeler. We try to correct that when mapping variables
848 // at the end of the presolve.
849 const int64_t min_value = new_domain.Min();
850 const int new_var = NewIntVar(new_domain.AdditionWith(Domain(-min_value)));
851 CHECK(StoreAffineRelation(var, new_var, mod, offset + mod * min_value,
852 /*debug_no_recursion=*/true));
853 UpdateRuleStats("variables: canonicalize affine domain");
855 return true;
856}
857
858bool PresolveContext::StoreAffineRelation(int ref_x, int ref_y, int64_t coeff,
859 int64_t offset,
860 bool debug_no_recursion) {
861 CHECK_NE(coeff, 0);
862 if (is_unsat_) return false;
863
864 // TODO(user): I am not 100% sure why, but sometimes the representative is
865 // fixed but that is not propagated to ref_x or ref_y and this causes issues.
866 if (!PropagateAffineRelation(ref_x)) return false;
867 if (!PropagateAffineRelation(ref_y)) return false;
868
869 if (IsFixed(ref_x)) {
870 const int64_t lhs = DomainOf(ref_x).FixedValue() - offset;
871 if (lhs % std::abs(coeff) != 0) {
872 return NotifyThatModelIsUnsat();
873 }
874 UpdateRuleStats("affine: fixed");
875 return IntersectDomainWith(ref_y, Domain(lhs / coeff));
876 }
877
878 if (IsFixed(ref_y)) {
879 const int64_t value_x = DomainOf(ref_y).FixedValue() * coeff + offset;
880 UpdateRuleStats("affine: fixed");
881 return IntersectDomainWith(ref_x, Domain(value_x));
882 }
883
884 // If both are already in the same class, we need to make sure the relations
885 // are compatible.
888 if (rx.representative == ry.representative) {
889 // x = rx.coeff * rep + rx.offset;
890 // y = ry.coeff * rep + ry.offset;
891 // And x == coeff * ry.coeff * rep + (coeff * ry.offset + offset).
892 //
893 // So we get the relation a * rep == b with a and b defined here:
894 const int64_t a = coeff * ry.coeff - rx.coeff;
895 const int64_t b = coeff * ry.offset + offset - rx.offset;
896 if (a == 0) {
897 if (b != 0) return NotifyThatModelIsUnsat();
898 return true;
899 }
900 if (b % a != 0) {
901 return NotifyThatModelIsUnsat();
902 }
903 UpdateRuleStats("affine: unique solution");
904 const int64_t unique_value = -b / a;
905 if (!IntersectDomainWith(rx.representative, Domain(unique_value))) {
906 return false;
907 }
908 if (!IntersectDomainWith(ref_x,
909 Domain(unique_value * rx.coeff + rx.offset))) {
910 return false;
911 }
912 if (!IntersectDomainWith(ref_y,
913 Domain(unique_value * ry.coeff + ry.offset))) {
914 return false;
915 }
916 return true;
917 }
918
919 // ref_x = coeff * ref_y + offset;
920 // rx.coeff * rep_x + rx.offset =
921 // coeff * (ry.coeff * rep_y + ry.offset) + offset
922 //
923 // We have a * rep_x + b * rep_y == o
924 int64_t a = rx.coeff;
925 int64_t b = coeff * ry.coeff;
926 int64_t o = coeff * ry.offset + offset - rx.offset;
927 CHECK_NE(a, 0);
928 CHECK_NE(b, 0);
929 {
930 const int64_t gcd = MathUtil::GCD64(std::abs(a), std::abs(b));
931 if (gcd != 1) {
932 a /= gcd;
933 b /= gcd;
934 if (o % gcd != 0) return NotifyThatModelIsUnsat();
935 o /= gcd;
936 }
937 }
938
939 // In this (rare) case, we need to canonicalize one of the variable that will
940 // become the representative for both.
941 if (std::abs(a) > 1 && std::abs(b) > 1) {
942 UpdateRuleStats("affine: created common representative");
943 if (!CanonicalizeAffineVariable(rx.representative, a, std::abs(b),
944 offset)) {
945 return false;
946 }
947
948 // Re-add the relation now that a will resolve to a multiple of b.
949 return StoreAffineRelation(ref_x, ref_y, coeff, offset,
950 /*debug_no_recursion=*/true);
951 }
952
953 // Canonicalize to x = c * y + o
954 int x, y;
955 int64_t c;
956 bool negate = false;
957 if (std::abs(a) == 1) {
958 x = rx.representative;
959 y = ry.representative;
960 c = b;
961 negate = a < 0;
962 } else {
963 CHECK_EQ(std::abs(b), 1);
964 x = ry.representative;
965 y = rx.representative;
966 c = a;
967 negate = b < 0;
968 }
969 if (negate) {
970 c = -c;
971 o = -o;
972 }
975
976 // Lets propagate domains first.
978 y, DomainOf(x).AdditionWith(Domain(-o)).InverseMultiplicationBy(c))) {
979 return false;
980 }
982 x,
983 DomainOf(y).ContinuousMultiplicationBy(c).AdditionWith(Domain(o)))) {
984 return false;
985 }
986
987 // To avoid corner cases where replacing x by y in a linear expression
988 // can cause overflow, we might want to canonicalize y first to avoid
989 // cases like x = c * [large_value, ...] - large_value.
990 //
991 // TODO(user): we can do better for overflow by not always choosing the
992 // min at zero, do the best things if it becomes needed.
993 if (std::abs(o) > std::max(std::abs(MinOf(x)), std::abs(MaxOf(x)))) {
994 // Both these function recursively call StoreAffineRelation() but shouldn't
995 // be able to cascade (CHECKED).
996 CHECK(!debug_no_recursion);
998 return StoreAffineRelation(x, y, c, o, /*debug_no_recursion=*/true);
999 }
1000
1001 // TODO(user): can we force the rep and remove GetAffineRelation()?
1002 CHECK(AddRelation(x, y, c, o, &affine_relations_));
1003 if ((c == 1 || c == -1) && o == 0) {
1004 CHECK(AddRelation(x, y, c, o, &var_equiv_relations_));
1005 }
1006
1007 UpdateRuleStats("affine: new relation");
1008
1009 // Lets propagate again the new relation. We might as well do it as early
1010 // as possible and not all call site do it.
1011 //
1012 // TODO(user): I am not sure this is needed given the propagation above.
1013 if (!PropagateAffineRelation(ref_x)) return false;
1014 if (!PropagateAffineRelation(ref_y)) return false;
1015
1016 // These maps should only contains representative, so only need to remap
1017 // either x or y.
1018 const int rep = GetAffineRelation(x).representative;
1019
1020 // The domain didn't change, but this notification allows to re-process any
1021 // constraint containing these variables. Note that we do not need to
1022 // retrigger a propagation of the constraint containing a variable whose
1023 // representative didn't change.
1024 if (x != rep) modified_domains.Set(x);
1025 if (y != rep) modified_domains.Set(y);
1026
1027 var_to_constraints_[x].insert(kAffineRelationConstraint);
1028 var_to_constraints_[y].insert(kAffineRelationConstraint);
1029 return true;
1030}
1031
1033 if (is_unsat_) return false;
1034
1035 CHECK(!VariableWasRemoved(ref_a));
1036 CHECK(!VariableWasRemoved(ref_b));
1037 CHECK(!DomainOf(ref_a).IsEmpty());
1038 CHECK(!DomainOf(ref_b).IsEmpty());
1039 CHECK(CanBeUsedAsLiteral(ref_a));
1040 CHECK(CanBeUsedAsLiteral(ref_b));
1041
1042 if (ref_a == ref_b) return true;
1043 if (ref_a == NegatedRef(ref_b)) return IntersectDomainWith(ref_a, Domain(0));
1044
1045 const int var_a = PositiveRef(ref_a);
1046 const int var_b = PositiveRef(ref_b);
1047 if (RefIsPositive(ref_a) == RefIsPositive(ref_b)) {
1048 // a = b
1049 return StoreAffineRelation(var_a, var_b, /*coeff=*/1, /*offset=*/0);
1050 }
1051 // a = 1 - b
1052 return StoreAffineRelation(var_a, var_b, /*coeff=*/-1, /*offset=*/1);
1053}
1054
1055bool PresolveContext::StoreAbsRelation(int target_ref, int ref) {
1056 const auto insert_status = abs_relations_.insert(
1057 std::make_pair(target_ref, SavedVariable(PositiveRef(ref))));
1058 if (!insert_status.second) {
1059 // Tricky: overwrite if the old value refer to a now unused variable.
1060 const int candidate = insert_status.first->second.Get(this);
1061 if (removed_variables_.contains(candidate)) {
1062 insert_status.first->second = SavedVariable(PositiveRef(ref));
1063 return true;
1064 }
1065 return false;
1066 }
1067 return true;
1068}
1069
1070bool PresolveContext::GetAbsRelation(int target_ref, int* ref) {
1071 auto it = abs_relations_.find(target_ref);
1072 if (it == abs_relations_.end()) return false;
1073
1074 // Tricky: In some rare case the stored relation can refer to a deleted
1075 // variable, so we need to ignore it.
1076 //
1077 // TODO(user): Incorporate this as part of SavedVariable/SavedLiteral so we
1078 // make sure we never forget about this.
1079 const int candidate = it->second.Get(this);
1080 if (removed_variables_.contains(candidate)) {
1081 abs_relations_.erase(it);
1082 return false;
1083 }
1084 *ref = candidate;
1085 CHECK(!VariableWasRemoved(*ref));
1086 return true;
1087}
1088
1091
1094 // Note(user): This can happen is some corner cases where the affine
1095 // relation where added before the variable became usable as Boolean. When
1096 // this is the case, the domain will be of the form [x, x + 1] and should be
1097 // later remapped to a Boolean variable.
1098 return ref;
1099 }
1100
1101 // We made sure that the affine representative can always be used as a
1102 // literal. However, if some variable are fixed, we might not have only
1103 // (coeff=1 offset=0) or (coeff=-1 offset=1) and we might have something like
1104 // (coeff=8 offset=0) which is only valid for both variable at zero...
1105 //
1106 // What is sure is that depending on the value, only one mapping can be valid
1107 // because r.coeff can never be zero.
1108 const bool positive_possible = (r.offset == 0 || r.coeff + r.offset == 1);
1109 const bool negative_possible = (r.offset == 1 || r.coeff + r.offset == 0);
1110 DCHECK_NE(positive_possible, negative_possible);
1111 if (RefIsPositive(ref)) {
1112 return positive_possible ? r.representative : NegatedRef(r.representative);
1113 } else {
1114 return positive_possible ? NegatedRef(r.representative) : r.representative;
1115 }
1116}
1117
1119 const AffineRelation::Relation r = var_equiv_relations_.Get(PositiveRef(ref));
1120 CHECK_EQ(std::abs(r.coeff), 1);
1121 CHECK_EQ(r.offset, 0);
1122 return RefIsPositive(ref) == (r.coeff == 1) ? r.representative
1124}
1125
1126// This makes sure that the affine relation only uses one of the
1127// representative from the var_equiv_relations_.
1129 AffineRelation::Relation r = affine_relations_.Get(PositiveRef(ref));
1130 AffineRelation::Relation o = var_equiv_relations_.Get(r.representative);
1132 if (o.coeff == -1) r.coeff = -r.coeff;
1133 if (!RefIsPositive(ref)) {
1134 r.coeff *= -1;
1135 r.offset *= -1;
1136 }
1137 return r;
1138}
1139
1140std::string PresolveContext::RefDebugString(int ref) const {
1141 return absl::StrCat(RefIsPositive(ref) ? "X" : "-X", PositiveRef(ref),
1142 DomainOf(ref).ToString());
1143}
1144
1147 return absl::StrCat(RefDebugString(ref), " = ", r.coeff, " * ",
1148 RefDebugString(r.representative), " + ", r.offset);
1149}
1150
1151// Create the internal structure for any new variables in working_model.
1153 for (int i = domains.size(); i < working_model->variables_size(); ++i) {
1154 domains.emplace_back(ReadDomainFromProto(working_model->variables(i)));
1155 if (domains.back().IsEmpty()) {
1156 is_unsat_ = true;
1157 return;
1158 }
1159 if (IsFixed(i)) ExploitFixedDomain(i);
1160 }
1161 modified_domains.Resize(domains.size());
1162 var_to_constraints_.resize(domains.size());
1163 var_to_num_linear1_.resize(domains.size());
1164 var_to_ub_only_constraints.resize(domains.size());
1165 var_to_lb_only_constraints.resize(domains.size());
1166}
1167
1170 CHECK_EQ(DomainOf(var).Size(), 2);
1171 const int64_t var_min = MinOf(var);
1172 const int64_t var_max = MaxOf(var);
1173
1174 if (is_unsat_) return;
1175
1176 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1177
1178 // Find encoding for min if present.
1179 auto min_it = var_map.find(var_min);
1180 if (min_it != var_map.end()) {
1181 const int old_var = PositiveRef(min_it->second.Get(this));
1182 if (removed_variables_.contains(old_var)) {
1183 var_map.erase(min_it);
1184 min_it = var_map.end();
1185 }
1186 }
1187
1188 // Find encoding for max if present.
1189 auto max_it = var_map.find(var_max);
1190 if (max_it != var_map.end()) {
1191 const int old_var = PositiveRef(max_it->second.Get(this));
1192 if (removed_variables_.contains(old_var)) {
1193 var_map.erase(max_it);
1194 max_it = var_map.end();
1195 }
1196 }
1197
1198 // Insert missing encoding.
1199 int min_literal;
1200 int max_literal;
1201 if (min_it != var_map.end() && max_it != var_map.end()) {
1202 min_literal = min_it->second.Get(this);
1203 max_literal = max_it->second.Get(this);
1204 if (min_literal != NegatedRef(max_literal)) {
1205 UpdateRuleStats("variables with 2 values: merge encoding literals");
1206 StoreBooleanEqualityRelation(min_literal, NegatedRef(max_literal));
1207 if (is_unsat_) return;
1208 }
1209 min_literal = GetLiteralRepresentative(min_literal);
1210 max_literal = GetLiteralRepresentative(max_literal);
1211 if (!IsFixed(min_literal)) CHECK_EQ(min_literal, NegatedRef(max_literal));
1212 } else if (min_it != var_map.end() && max_it == var_map.end()) {
1213 UpdateRuleStats("variables with 2 values: register other encoding");
1214 min_literal = min_it->second.Get(this);
1215 max_literal = NegatedRef(min_literal);
1216 var_map[var_max] = SavedLiteral(max_literal);
1217 } else if (min_it == var_map.end() && max_it != var_map.end()) {
1218 UpdateRuleStats("variables with 2 values: register other encoding");
1219 max_literal = max_it->second.Get(this);
1220 min_literal = NegatedRef(max_literal);
1221 var_map[var_min] = SavedLiteral(min_literal);
1222 } else {
1223 UpdateRuleStats("variables with 2 values: create encoding literal");
1224 max_literal = NewBoolVar();
1225 min_literal = NegatedRef(max_literal);
1226 var_map[var_min] = SavedLiteral(min_literal);
1227 var_map[var_max] = SavedLiteral(max_literal);
1228 }
1229
1230 if (IsFixed(min_literal) || IsFixed(max_literal)) {
1231 CHECK(IsFixed(min_literal));
1232 CHECK(IsFixed(max_literal));
1233 UpdateRuleStats("variables with 2 values: fixed encoding");
1234 if (LiteralIsTrue(min_literal)) {
1235 return static_cast<void>(IntersectDomainWith(var, Domain(var_min)));
1236 } else {
1237 return static_cast<void>(IntersectDomainWith(var, Domain(var_max)));
1238 }
1239 }
1240
1241 // Add affine relation.
1242 if (GetAffineRelation(var).representative != PositiveRef(min_literal)) {
1243 UpdateRuleStats("variables with 2 values: new affine relation");
1244 if (RefIsPositive(max_literal)) {
1245 (void)StoreAffineRelation(var, PositiveRef(max_literal),
1246 var_max - var_min, var_min);
1247 } else {
1248 (void)StoreAffineRelation(var, PositiveRef(max_literal),
1249 var_min - var_max, var_max);
1250 }
1251 }
1252}
1253
1254void PresolveContext::InsertVarValueEncodingInternal(int literal, int var,
1255 int64_t value,
1256 bool add_constraints) {
1260 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1261
1262 // The code below is not 100% correct if this is not the case.
1263 DCHECK(DomainOf(var).Contains(value));
1264
1265 // If an encoding already exist, make the two Boolean equals.
1266 const auto [it, inserted] =
1267 var_map.insert(std::make_pair(value, SavedLiteral(literal)));
1268 if (!inserted) {
1269 const int previous_literal = it->second.Get(this);
1270
1271 // Ticky and rare: I have only observed this on the LNS of
1272 // radiation_m18_12_05_sat.fzn. The value was encoded, but maybe we never
1273 // used the involved variables / constraints, so it was removed (with the
1274 // encoding constraints) from the model already! We have to be careful.
1275 if (VariableWasRemoved(previous_literal)) {
1276 it->second = SavedLiteral(literal);
1277 } else {
1278 if (literal != previous_literal) {
1280 "variables: merge equivalent var value encoding literals");
1281 StoreBooleanEqualityRelation(literal, previous_literal);
1282 }
1283 }
1284 return;
1285 }
1286
1287 if (DomainOf(var).Size() == 2) {
1288 // TODO(user): There is a bug here if the var == value was not in the
1289 // domain, it will just be ignored.
1291 } else {
1292 VLOG(2) << "Insert lit(" << literal << ") <=> var(" << var
1293 << ") == " << value;
1294 eq_half_encoding_[var][value].insert(literal);
1295 neq_half_encoding_[var][value].insert(NegatedRef(literal));
1296 if (add_constraints) {
1297 UpdateRuleStats("variables: add encoding constraint");
1298 AddImplyInDomain(literal, var, Domain(value));
1299 AddImplyInDomain(NegatedRef(literal), var, Domain(value).Complement());
1300 }
1301 }
1302}
1303
1304bool PresolveContext::InsertHalfVarValueEncoding(int literal, int var,
1305 int64_t value, bool imply_eq) {
1306 if (is_unsat_) return false;
1308
1309 // Creates the linking sets on demand.
1310 // Insert the enforcement literal in the half encoding map.
1311 auto& direct_set =
1312 imply_eq ? eq_half_encoding_[var][value] : neq_half_encoding_[var][value];
1313 if (!direct_set.insert(literal).second) return false; // Already there.
1314
1315 VLOG(2) << "Collect lit(" << literal << ") implies var(" << var
1316 << (imply_eq ? ") == " : ") != ") << value;
1317 UpdateRuleStats("variables: detect half reified value encoding");
1318
1319 // Note(user): We don't expect a lot of literals in these sets, so doing
1320 // a scan should be okay.
1321 auto& other_set =
1322 imply_eq ? neq_half_encoding_[var][value] : eq_half_encoding_[var][value];
1323 for (const int other : other_set) {
1324 if (GetLiteralRepresentative(other) != NegatedRef(literal)) continue;
1325
1326 UpdateRuleStats("variables: detect fully reified value encoding");
1327 const int imply_eq_literal = imply_eq ? literal : NegatedRef(literal);
1328 InsertVarValueEncodingInternal(imply_eq_literal, var, value,
1329 /*add_constraints=*/false);
1330 break;
1331 }
1332
1333 return true;
1334}
1335
1336bool PresolveContext::CanonicalizeEncoding(int* ref, int64_t* value) {
1337 const AffineRelation::Relation r = GetAffineRelation(*ref);
1338 if ((*value - r.offset) % r.coeff != 0) return false;
1339 *ref = r.representative;
1340 *value = (*value - r.offset) / r.coeff;
1341 return true;
1342}
1343
1345 int64_t value) {
1346 if (!CanonicalizeEncoding(&ref, &value)) {
1347 return SetLiteralToFalse(literal);
1348 }
1350 InsertVarValueEncodingInternal(literal, ref, value, /*add_constraints=*/true);
1351 return true;
1352}
1353
1355 int64_t value) {
1356 if (!CanonicalizeEncoding(&var, &value)) return false;
1358 return InsertHalfVarValueEncoding(literal, var, value, /*imply_eq=*/true);
1359}
1360
1362 int64_t value) {
1363 if (!CanonicalizeEncoding(&var, &value)) return false;
1365 return InsertHalfVarValueEncoding(literal, var, value, /*imply_eq=*/false);
1366}
1367
1369 int* literal) {
1371 if (!CanonicalizeEncoding(&ref, &value)) return false;
1372 const absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[ref];
1373 const auto it = var_map.find(value);
1374 if (it != var_map.end()) {
1375 if (literal != nullptr) {
1376 *literal = it->second.Get(this);
1377 }
1378 return true;
1379 }
1380 return false;
1381}
1382
1384 const int var = PositiveRef(ref);
1385 const int64_t size = domains[var].Size();
1386 if (size <= 2) return true;
1387 const auto& it = encoding_.find(var);
1388 return it == encoding_.end() ? false : size <= it->second.size();
1389}
1390
1392 CHECK_LE(expr.vars_size(), 1);
1393 if (IsFixed(expr)) return true;
1394 return IsFullyEncoded(expr.vars(0));
1395}
1396
1399 if (!CanonicalizeEncoding(&ref, &value)) return GetOrCreateConstantVar(0);
1400
1401 // Positive after CanonicalizeEncoding().
1402 const int var = ref;
1403
1404 // Returns the false literal if the value is not in the domain.
1405 if (!domains[var].Contains(value)) {
1406 return GetOrCreateConstantVar(0);
1407 }
1408
1409 // Returns the associated literal if already present.
1410 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1411 auto it = var_map.find(value);
1412 if (it != var_map.end()) {
1413 const int lit = it->second.Get(this);
1414 if (VariableWasRemoved(lit)) {
1415 // If the variable was already removed, for now we create a new one.
1416 // This should be rare hopefully.
1417 var_map.erase(value);
1418 } else {
1419 return lit;
1420 }
1421 }
1422
1423 // Special case for fixed domains.
1424 if (domains[var].Size() == 1) {
1425 const int true_literal = GetOrCreateConstantVar(1);
1426 var_map[value] = SavedLiteral(true_literal);
1427 return true_literal;
1428 }
1429
1430 // Special case for domains of size 2.
1431 const int64_t var_min = MinOf(var);
1432 const int64_t var_max = MaxOf(var);
1433 if (domains[var].Size() == 2) {
1434 // Checks if the other value is already encoded.
1435 const int64_t other_value = value == var_min ? var_max : var_min;
1436 auto other_it = var_map.find(other_value);
1437 if (other_it != var_map.end()) {
1438 const int literal = NegatedRef(other_it->second.Get(this));
1440 // If the variable was already removed, for now we create a new one.
1441 // This should be rare hopefully.
1442 var_map.erase(other_value);
1443 } else {
1444 // Update the encoding map. The domain could have been reduced to size
1445 // two after the creation of the first literal.
1446 var_map[value] = SavedLiteral(literal);
1447 return literal;
1448 }
1449 }
1450
1451 if (var_min == 0 && var_max == 1) {
1453 var_map[1] = SavedLiteral(representative);
1454 var_map[0] = SavedLiteral(NegatedRef(representative));
1456 } else {
1457 const int literal = NewBoolVar();
1460 return value == var_max ? representative : NegatedRef(representative);
1461 }
1462 }
1463
1464 const int literal = NewBoolVar();
1467}
1468
1470 const LinearExpressionProto& expr, int64_t value) {
1471 DCHECK_LE(expr.vars_size(), 1);
1472 if (IsFixed(expr)) {
1473 if (FixedValue(expr) == value) {
1474 return GetOrCreateConstantVar(1);
1475 } else {
1476 return GetOrCreateConstantVar(0);
1477 }
1478 }
1479
1480 if ((value - expr.offset()) % expr.coeffs(0) != 0) {
1481 return GetOrCreateConstantVar(0);
1482 }
1483
1484 return GetOrCreateVarValueEncoding(expr.vars(0),
1485 (value - expr.offset()) / expr.coeffs(0));
1486}
1487
1490
1491 objective_offset_ = obj.offset();
1492 objective_scaling_factor_ = obj.scaling_factor();
1493 if (objective_scaling_factor_ == 0.0) {
1494 objective_scaling_factor_ = 1.0;
1495 }
1496
1497 objective_integer_offset_ = obj.integer_offset();
1498 objective_integer_scaling_factor_ = obj.integer_scaling_factor();
1499 if (objective_integer_scaling_factor_ == 0) {
1500 objective_integer_scaling_factor_ = 1;
1501 }
1502
1503 if (!obj.domain().empty()) {
1504 // We might relax this in CanonicalizeObjective() when we will compute
1505 // the possible objective domain from the domains of the variables.
1506 objective_domain_is_constraining_ = true;
1507 objective_domain_ = ReadDomainFromProto(obj);
1508 } else {
1509 objective_domain_is_constraining_ = false;
1510 objective_domain_ = Domain::AllValues();
1511 }
1512
1513 // This is an upper bound of the higher magnitude that can be reach by
1514 // summing an objective partial sum. Because of the model validation, this
1515 // shouldn't overflow, and we make sure it stays this way.
1516 objective_overflow_detection_ = 0;
1517
1518 objective_map_.clear();
1519 for (int i = 0; i < obj.vars_size(); ++i) {
1520 const int ref = obj.vars(i);
1521 const int64_t var_max_magnitude =
1522 std::max(std::abs(MinOf(ref)), std::abs(MaxOf(ref)));
1523
1524 // Skipping var fixed to zero allow to avoid some overflow in situation
1525 // were we can deal with it.
1526 if (var_max_magnitude == 0) continue;
1527
1528 const int64_t coeff = obj.coeffs(i);
1529 objective_overflow_detection_ += var_max_magnitude * std::abs(coeff);
1530
1531 const int var = PositiveRef(ref);
1532 objective_map_[var] += RefIsPositive(ref) ? coeff : -coeff;
1533 if (objective_map_[var] == 0) {
1534 objective_map_.erase(var);
1535 var_to_constraints_[var].erase(kObjectiveConstraint);
1536 } else {
1537 var_to_constraints_[var].insert(kObjectiveConstraint);
1538 }
1539 }
1540}
1541
1542bool PresolveContext::CanonicalizeObjective(bool simplify_domain) {
1543 int64_t offset_change = 0;
1544
1545 // We replace each entry by its affine representative.
1546 // Note that the non-deterministic loop is fine, but because we iterate
1547 // one the map while modifying it, it is safer to do a copy rather than to
1548 // try to handle that in one pass.
1549 tmp_entries_.clear();
1550 for (const auto& entry : objective_map_) {
1551 tmp_entries_.push_back(entry);
1552 }
1553
1554 // TODO(user): This is a bit duplicated with the presolve linear code.
1555 // We also do not propagate back any domain restriction from the objective to
1556 // the variables if any.
1557 for (const auto& entry : tmp_entries_) {
1558 const int var = entry.first;
1559 const auto it = objective_map_.find(var);
1560 if (it == objective_map_.end()) continue;
1561 const int64_t coeff = it->second;
1562
1563 // If a variable only appear in objective, we can fix it!
1564 // Note that we don't care if it was in affine relation, because if none
1565 // of the relations are left, then we can still fix it.
1566 if (!keep_all_feasible_solutions && !objective_domain_is_constraining_ &&
1568 var_to_constraints_[var].size() == 1 &&
1569 var_to_constraints_[var].contains(kObjectiveConstraint)) {
1570 UpdateRuleStats("objective: variable not used elsewhere");
1571 if (coeff > 0) {
1573 return false;
1574 }
1575 } else {
1577 return false;
1578 }
1579 }
1580 }
1581
1582 if (IsFixed(var)) {
1583 offset_change += coeff * MinOf(var);
1584 var_to_constraints_[var].erase(kObjectiveConstraint);
1585 objective_map_.erase(var);
1586 continue;
1587 }
1588
1590 if (r.representative == var) continue;
1591
1592 objective_map_.erase(var);
1593 var_to_constraints_[var].erase(kObjectiveConstraint);
1594
1595 // Do the substitution.
1596 offset_change += coeff * r.offset;
1597 const int64_t new_coeff = objective_map_[r.representative] +=
1598 coeff * r.coeff;
1599
1600 // Process new term.
1601 if (new_coeff == 0) {
1602 objective_map_.erase(r.representative);
1603 var_to_constraints_[r.representative].erase(kObjectiveConstraint);
1604 } else {
1605 var_to_constraints_[r.representative].insert(kObjectiveConstraint);
1606 if (IsFixed(r.representative)) {
1607 offset_change += new_coeff * MinOf(r.representative);
1608 var_to_constraints_[r.representative].erase(kObjectiveConstraint);
1609 objective_map_.erase(r.representative);
1610 }
1611 }
1612 }
1613
1614 Domain implied_domain(0);
1615 int64_t gcd(0);
1616
1617 // We need to sort the entries to be deterministic.
1618 tmp_entries_.clear();
1619 for (const auto& entry : objective_map_) {
1620 tmp_entries_.push_back(entry);
1621 }
1622 std::sort(tmp_entries_.begin(), tmp_entries_.end());
1623 for (const auto& entry : tmp_entries_) {
1624 const int var = entry.first;
1625 const int64_t coeff = entry.second;
1626 gcd = MathUtil::GCD64(gcd, std::abs(coeff));
1627 implied_domain =
1628 implied_domain.AdditionWith(DomainOf(var).MultiplicationBy(coeff))
1630 }
1631
1632 // This is the new domain.
1633 // Note that the domain never include the offset.
1634 objective_domain_ = objective_domain_.AdditionWith(Domain(-offset_change))
1635 .IntersectionWith(implied_domain);
1636
1637 // Depending on the use case, we cannot do that.
1638 if (simplify_domain) {
1639 objective_domain_ =
1640 objective_domain_.SimplifyUsingImpliedDomain(implied_domain);
1641 }
1642
1643 // Update the offset.
1644 objective_offset_ += offset_change;
1645 objective_integer_offset_ +=
1646 offset_change * objective_integer_scaling_factor_;
1647
1648 // Maybe divide by GCD.
1649 if (gcd > 1) {
1650 for (auto& entry : objective_map_) {
1651 entry.second /= gcd;
1652 }
1653 objective_domain_ = objective_domain_.InverseMultiplicationBy(gcd);
1654 objective_offset_ /= static_cast<double>(gcd);
1655 objective_scaling_factor_ *= static_cast<double>(gcd);
1656 objective_integer_scaling_factor_ *= gcd;
1657 }
1658
1659 if (objective_domain_.IsEmpty()) return false;
1660
1661 // Detect if the objective domain do not limit the "optimal" objective value.
1662 // If this is true, then we can apply any reduction that reduce the objective
1663 // value without any issues.
1664 objective_domain_is_constraining_ =
1665 !implied_domain
1667 objective_domain_.Max()))
1668 .IsIncludedIn(objective_domain_);
1669 return true;
1670}
1671
1673 objective_map_.erase(var);
1674 var_to_constraints_[var].erase(kObjectiveConstraint);
1675}
1676
1678 int64_t& map_ref = objective_map_[var];
1679 map_ref += value;
1680 if (map_ref == 0) {
1681 objective_map_.erase(var);
1682 var_to_constraints_[var].erase(kObjectiveConstraint);
1683 } else {
1684 var_to_constraints_[var].insert(kObjectiveConstraint);
1685 }
1686}
1687
1689 // Tricky: The objective domain is without the offset, so we need to shift it.
1690 objective_offset_ += static_cast<double>(value);
1691 objective_integer_offset_ += value * objective_integer_scaling_factor_;
1692 objective_domain_ = objective_domain_.AdditionWith(Domain(-value));
1693}
1694
1696 int var_in_equality, int64_t coeff_in_equality,
1697 const ConstraintProto& equality, std::vector<int>* new_vars_in_objective) {
1698 CHECK(equality.enforcement_literal().empty());
1699 CHECK(RefIsPositive(var_in_equality));
1700
1701 if (new_vars_in_objective != nullptr) new_vars_in_objective->clear();
1702
1703 // We can only "easily" substitute if the objective coefficient is a multiple
1704 // of the one in the constraint.
1705 const int64_t coeff_in_objective =
1706 gtl::FindOrDie(objective_map_, var_in_equality);
1707 CHECK_NE(coeff_in_equality, 0);
1708 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1709
1710 const int64_t multiplier = coeff_in_objective / coeff_in_equality;
1711
1712 // Abort if the new objective seems to violate our overflow preconditions.
1713 int64_t change = 0;
1714 for (int i = 0; i < equality.linear().vars().size(); ++i) {
1715 int var = equality.linear().vars(i);
1716 if (PositiveRef(var) == var_in_equality) continue;
1717 int64_t coeff = equality.linear().coeffs(i);
1718 change +=
1719 std::abs(coeff) * std::max(std::abs(MinOf(var)), std::abs(MaxOf(var)));
1720 }
1721 const int64_t new_value =
1722 CapAdd(CapProd(std::abs(multiplier), change),
1723 objective_overflow_detection_ -
1724 std::abs(coeff_in_equality) *
1725 std::max(std::abs(MinOf(var_in_equality)),
1726 std::abs(MaxOf(var_in_equality))));
1727 if (new_value == std::numeric_limits<int64_t>::max()) return false;
1728 objective_overflow_detection_ = new_value;
1729
1730 // Compute the objective offset change.
1731 Domain offset = ReadDomainFromProto(equality.linear());
1732 DCHECK_EQ(offset.Min(), offset.Max());
1733 bool exact = true;
1734 offset = offset.MultiplicationBy(multiplier, &exact);
1735 CHECK(exact);
1736 CHECK(!offset.IsEmpty());
1737
1738 // We also need to make sure the integer_offset will not overflow.
1739 {
1740 int64_t temp = CapProd(offset.Min(), objective_integer_scaling_factor_);
1741 if (temp == std::numeric_limits<int64_t>::max()) return false;
1742 if (temp == std::numeric_limits<int64_t>::min()) return false;
1743 temp = CapAdd(temp, objective_integer_offset_);
1744 if (temp == std::numeric_limits<int64_t>::max()) return false;
1745 if (temp == std::numeric_limits<int64_t>::min()) return false;
1746 }
1747
1748 // Perform the substitution.
1749 for (int i = 0; i < equality.linear().vars().size(); ++i) {
1750 int var = equality.linear().vars(i);
1751 int64_t coeff = equality.linear().coeffs(i);
1752 if (!RefIsPositive(var)) {
1753 var = NegatedRef(var);
1754 coeff = -coeff;
1755 }
1756 if (var == var_in_equality) continue;
1757
1758 int64_t& map_ref = objective_map_[var];
1759 if (map_ref == 0 && new_vars_in_objective != nullptr) {
1760 new_vars_in_objective->push_back(var);
1761 }
1762 map_ref -= coeff * multiplier;
1763
1764 if (map_ref == 0) {
1765 objective_map_.erase(var);
1766 var_to_constraints_[var].erase(kObjectiveConstraint);
1767 } else {
1768 var_to_constraints_[var].insert(kObjectiveConstraint);
1769 }
1770 }
1771
1772 objective_map_.erase(var_in_equality);
1773 var_to_constraints_[var_in_equality].erase(kObjectiveConstraint);
1774
1775 // Tricky: The objective domain is without the offset, so we need to shift it.
1776 objective_offset_ += static_cast<double>(offset.Min());
1777 objective_integer_offset_ += offset.Min() * objective_integer_scaling_factor_;
1778 objective_domain_ = objective_domain_.AdditionWith(Domain(-offset.Min()));
1779
1780 // Because we can assume that the constraint we used was constraining
1781 // (otherwise it would have been removed), the objective domain should be now
1782 // constraining.
1783 objective_domain_is_constraining_ = true;
1784
1785 if (objective_domain_.IsEmpty()) {
1786 return NotifyThatModelIsUnsat();
1787 }
1788 return true;
1789}
1790
1792 absl::Span<const int> exactly_one) {
1793 if (objective_map_.empty()) return false;
1794
1795 int64_t min_coeff = std::numeric_limits<int64_t>::max();
1796 for (const int ref : exactly_one) {
1797 const auto it = objective_map_.find(PositiveRef(ref));
1798 if (it == objective_map_.end()) return false;
1799
1800 const int64_t coeff = it->second;
1801 if (RefIsPositive(ref)) {
1802 min_coeff = std::min(min_coeff, coeff);
1803 } else {
1804 // Objective = coeff * var = coeff * (1 - ref);
1805 min_coeff = std::min(min_coeff, -coeff);
1806 }
1807 }
1808
1809 int64_t offset = min_coeff;
1810 for (const int ref : exactly_one) {
1811 const int var = PositiveRef(ref);
1812 int64_t& map_ref = objective_map_.at(var);
1813 if (RefIsPositive(ref)) {
1814 map_ref -= min_coeff;
1815 if (map_ref == 0) {
1816 objective_map_.erase(var);
1817 var_to_constraints_[var].erase(kObjectiveConstraint);
1818 }
1819 } else {
1820 // Term = coeff * (1 - X) = coeff - coeff * X;
1821 // So -coeff -> -coeff -min_coeff
1822 // And Term = coeff + min_coeff - min_coeff - (coeff + min_coeff) * X
1823 // = (coeff + min_coeff) * (1 - X) - min_coeff;
1824 map_ref += min_coeff;
1825 if (map_ref == 0) {
1826 objective_map_.erase(var);
1827 var_to_constraints_[var].erase(kObjectiveConstraint);
1828 }
1829 offset -= min_coeff;
1830 }
1831 }
1832
1833 // Note that the domain never include the offset, so we need to update it.
1834 if (offset != 0) {
1835 objective_offset_ += offset;
1836 objective_integer_offset_ += offset * objective_integer_scaling_factor_;
1837 objective_domain_ = objective_domain_.AdditionWith(Domain(-offset));
1838 }
1839
1840 return true;
1841}
1842
1844 // We need to sort the entries to be deterministic.
1845 std::vector<std::pair<int, int64_t>> entries;
1846 for (const auto& entry : objective_map_) {
1847 entries.push_back(entry);
1848 }
1849 std::sort(entries.begin(), entries.end());
1850
1852 mutable_obj->set_offset(objective_offset_);
1853 mutable_obj->set_scaling_factor(objective_scaling_factor_);
1854 mutable_obj->set_integer_offset(objective_integer_offset_);
1855 if (objective_integer_scaling_factor_ == 1) {
1856 mutable_obj->set_integer_scaling_factor(0); // Default.
1857 } else {
1858 mutable_obj->set_integer_scaling_factor(objective_integer_scaling_factor_);
1859 }
1860 FillDomainInProto(objective_domain_, mutable_obj);
1861 mutable_obj->clear_vars();
1862 mutable_obj->clear_coeffs();
1863 for (const auto& entry : entries) {
1864 mutable_obj->add_vars(entry.first);
1865 mutable_obj->add_coeffs(entry.second);
1866 }
1867}
1868
1870 for (int i = 0; i < working_model->variables_size(); ++i) {
1872 }
1873}
1874
1876 const LinearExpressionProto& time_i, const LinearExpressionProto& time_j,
1877 int active_i, int active_j) {
1878 CHECK(!LiteralIsFalse(active_i));
1879 CHECK(!LiteralIsFalse(active_j));
1880 DCHECK(ExpressionIsAffine(time_i));
1881 DCHECK(ExpressionIsAffine(time_j));
1882
1883 const std::tuple<int, int64_t, int, int64_t, int64_t, int, int> key =
1884 GetReifiedPrecedenceKey(time_i, time_j, active_i, active_j);
1885 const auto& it = reified_precedences_cache_.find(key);
1886 if (it != reified_precedences_cache_.end()) return it->second;
1887
1888 const int result = NewBoolVar();
1889 reified_precedences_cache_[key] = result;
1890
1891 // result => (time_i <= time_j) && active_i && active_j.
1893 lesseq->add_enforcement_literal(result);
1894 if (!IsFixed(time_i)) {
1895 lesseq->mutable_linear()->add_vars(time_i.vars(0));
1896 lesseq->mutable_linear()->add_coeffs(-time_i.coeffs(0));
1897 }
1898 if (!IsFixed(time_j)) {
1899 lesseq->mutable_linear()->add_vars(time_j.vars(0));
1900 lesseq->mutable_linear()->add_coeffs(time_j.coeffs(0));
1901 }
1902
1903 const int64_t offset =
1904 (IsFixed(time_i) ? FixedValue(time_i) : time_i.offset()) -
1905 (IsFixed(time_j) ? FixedValue(time_j) : time_j.offset());
1906 lesseq->mutable_linear()->add_domain(offset);
1908 if (!LiteralIsTrue(active_i)) {
1909 AddImplication(result, active_i);
1910 }
1911 if (!LiteralIsTrue(active_j)) {
1912 AddImplication(result, active_j);
1913 }
1914
1915 // Not(result) && active_i && active_j => (time_i > time_j)
1916 ConstraintProto* const greater = working_model->add_constraints();
1917 if (!IsFixed(time_i)) {
1918 greater->mutable_linear()->add_vars(time_i.vars(0));
1919 greater->mutable_linear()->add_coeffs(-time_i.coeffs(0));
1920 }
1921 if (!IsFixed(time_j)) {
1922 greater->mutable_linear()->add_vars(time_j.vars(0));
1923 greater->mutable_linear()->add_coeffs(time_j.coeffs(0));
1924 }
1926 greater->mutable_linear()->add_domain(offset - 1);
1927
1928 // Manages enforcement literal.
1929 greater->add_enforcement_literal(NegatedRef(result));
1930 if (!LiteralIsTrue(active_i)) {
1931 greater->add_enforcement_literal(active_i);
1932 }
1933 if (!LiteralIsTrue(active_j)) {
1934 greater->add_enforcement_literal(active_j);
1935 }
1936
1937 // This is redundant but should improves performance.
1938 //
1939 // If GetOrCreateReifiedPrecedenceLiteral(time_j, time_i, active_j, active_i)
1940 // (the reverse precedence) has been called too, then we can link the two
1941 // precedence literals, and the two active literals together.
1942 const auto& rev_it = reified_precedences_cache_.find(
1943 GetReifiedPrecedenceKey(time_j, time_i, active_j, active_i));
1944 if (rev_it != reified_precedences_cache_.end()) {
1945 auto* const bool_or = working_model->add_constraints()->mutable_bool_or();
1946 bool_or->add_literals(result);
1947 bool_or->add_literals(rev_it->second);
1948 bool_or->add_literals(NegatedRef(active_i));
1949 bool_or->add_literals(NegatedRef(active_j));
1950 }
1951
1952 return result;
1953}
1954
1955std::tuple<int, int64_t, int, int64_t, int64_t, int, int>
1957 const LinearExpressionProto& time_j,
1958 int active_i, int active_j) {
1959 const int var_i =
1960 IsFixed(time_i) ? std::numeric_limits<int>::min() : time_i.vars(0);
1961 const int64_t coeff_i = IsFixed(time_i) ? 0 : time_i.coeffs(0);
1962 const int var_j =
1963 IsFixed(time_j) ? std::numeric_limits<int>::min() : time_j.vars(0);
1964 const int64_t coeff_j = IsFixed(time_j) ? 0 : time_j.coeffs(0);
1965 const int64_t offset =
1966 (IsFixed(time_i) ? FixedValue(time_i) : time_i.offset()) -
1967 (IsFixed(time_j) ? FixedValue(time_j) : time_j.offset());
1968 // In all formulas, active_i and active_j are symmetrical, we can sort the
1969 // active literals.
1970 if (active_j < active_i) std::swap(active_i, active_j);
1971 return std::make_tuple(var_i, coeff_i, var_j, coeff_j, offset, active_i,
1972 active_j);
1973}
1974
1976 reified_precedences_cache_.clear();
1977}
1978
1980 SOLVER_LOG(logger_, "");
1981 SOLVER_LOG(logger_, "Presolve summary:");
1982 SOLVER_LOG(logger_, " - ", NumAffineRelations(),
1983 " affine relations were detected.");
1984 std::map<std::string, int> sorted_rules(stats_by_rule_name_.begin(),
1985 stats_by_rule_name_.end());
1986 for (const auto& entry : sorted_rules) {
1987 if (entry.second == 1) {
1988 SOLVER_LOG(logger_, " - rule '", entry.first, "' was applied 1 time.");
1989 } else {
1990 SOLVER_LOG(logger_, " - rule '", entry.first, "' was applied ",
1991 entry.second, " times.");
1992 }
1993 }
1994}
1995
1997 if (context->ModelIsUnsat()) return false;
1998
1999 // Update the domain in the current CpModelProto.
2000 context->WriteVariableDomainsToProto();
2001 const CpModelProto& model_proto = *(context->working_model);
2002
2003 // Load the constraints in a local model.
2004 //
2005 // TODO(user): The model we load does not contain affine relations! But
2006 // ideally we should be able to remove all of them once we allow more complex
2007 // constraints to contains linear expression.
2008 //
2009 // TODO(user): remove code duplication with cp_model_solver. Here we also do
2010 // not run the heuristic to decide which variable to fully encode.
2011 //
2012 // TODO(user): Maybe do not load slow to propagate constraints? for instance
2013 // we do not use any linear relaxation here.
2014 Model model;
2015 local_model->Register<SolverLogger>(context->logger());
2016
2017 // Adapt some of the parameters during this probing phase.
2018 auto* local_param = local_model->GetOrCreate<SatParameters>();
2019 *local_param = context->params();
2020 local_param->set_use_implied_bounds(false);
2021
2022 local_model->GetOrCreate<TimeLimit>()->MergeWithGlobalTimeLimit(
2023 context->time_limit());
2024 local_model->Register<ModelRandomGenerator>(context->random());
2025 auto* encoder = local_model->GetOrCreate<IntegerEncoder>();
2026 encoder->DisableImplicationBetweenLiteral();
2027 auto* mapping = local_model->GetOrCreate<CpModelMapping>();
2028
2029 // Important: Because the model_proto do not contains affine relation or the
2030 // objective, we cannot call DetectOptionalVariables() ! This might wrongly
2031 // detect optionality and derive bad conclusion.
2032 LoadVariables(model_proto, /*view_all_booleans_as_integers=*/false,
2033 local_model);
2034 ExtractEncoding(model_proto, local_model);
2035 auto* sat_solver = local_model->GetOrCreate<SatSolver>();
2036 for (const ConstraintProto& ct : model_proto.constraints()) {
2037 if (mapping->ConstraintIsAlreadyLoaded(&ct)) continue;
2038 CHECK(LoadConstraint(ct, local_model));
2039 if (sat_solver->IsModelUnsat()) {
2040 return context->NotifyThatModelIsUnsat(absl::StrCat(
2041 "after loading constraint during probing ", ct.ShortDebugString()));
2042 }
2043 }
2044 encoder->AddAllImplicationsBetweenAssociatedLiterals();
2045 if (!sat_solver->Propagate()) {
2046 return context->NotifyThatModelIsUnsat(
2047 "during probing initial propagation");
2048 }
2049
2050 return true;
2051}
2052
2053} // namespace sat
2054} // 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 DCHECK_LE(val1, val2)
Definition: base/logging.h:892
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:891
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
#define CHECK_NE(val1, val2)
Definition: base/logging.h:703
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:893
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:889
#define CHECK_LE(val1, val2)
Definition: base/logging.h:704
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:890
#define VLOG(verboselevel)
Definition: base/logging.h:983
bool TryAdd(int x, int y, int64_t coeff, int64_t offset)
We call domain any subset of Int64 = [kint64min, kint64max].
static Domain AllValues()
Returns the full domain Int64.
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
Domain ContinuousMultiplicationBy(int64_t coeff) const
Returns a superset of MultiplicationBy() to avoid the explosion in the representation size.
int64_t FixedValue() const
Returns the value of a fixed domain.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
bool IsFixed() const
Returns true iff the domain is reduced to a single value.
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.
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
Advanced usage.
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
void Set(IntegerType index)
Definition: bitset.h:804
void Resize(IntegerType size)
Definition: bitset.h:790
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
const ::operations_research::sat::IntervalConstraintProto & interval() const
const ::operations_research::sat::LinearConstraintProto & linear() const
int32_t enforcement_literal(int index) const
Definition: cp_model.pb.h:9472
::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > * mutable_enforcement_literal()
Definition: cp_model.pb.h:9501
::operations_research::sat::LinearConstraintProto * mutable_linear()
::operations_research::sat::BoolArgumentProto * mutable_bool_or()
Definition: cp_model.pb.h:9574
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
::operations_research::sat::IntegerVariableProto * mutable_variables(int index)
::operations_research::sat::ConstraintProto * add_constraints()
const ::operations_research::sat::FloatObjectiveProto & floating_point_objective() const
::operations_research::sat::IntegerVariableProto * add_variables()
::operations_research::sat::CpObjectiveProto * mutable_objective()
const ::operations_research::sat::ConstraintProto & constraints(int index) const
::PROTOBUF_NAMESPACE_ID::RepeatedField< int64_t > * mutable_coeffs()
Definition: cp_model.pb.h:7576
::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > * mutable_vars()
Definition: cp_model.pb.h:7529
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
void Register(T *non_owned_class)
Register a non-owned class that will be "singleton" in the model.
Definition: sat/model.h:169
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:106
bool CanonicalizeAffineVariable(int ref, int64_t coeff, int64_t mod, int64_t rhs)
bool ExpressionIsALiteral(const LinearExpressionProto &expr, int *literal=nullptr) const
bool StoreAbsRelation(int target_ref, int ref)
void AddToObjective(int var, int64_t value)
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
int GetOrCreateReifiedPrecedenceLiteral(const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
ABSL_MUST_USE_RESULT bool CanonicalizeObjective(bool simplify_domain=true)
bool StoreBooleanEqualityRelation(int ref_a, int ref_b)
bool VariableWithCostIsUniqueAndRemovable(int ref) const
bool ExpressionIsSingleVariable(const LinearExpressionProto &expr) const
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
int GetOrCreateAffineValueEncoding(const LinearExpressionProto &expr, int64_t value)
ABSL_MUST_USE_RESULT bool ScaleFloatingPointObjective()
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
int GetOrCreateVarValueEncoding(int ref, int64_t value)
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
std::string AffineRelationDebugString(int ref) const
bool InsertVarValueEncoding(int literal, int ref, int64_t value)
std::tuple< int, int64_t, int, int64_t, int64_t, int, int > GetReifiedPrecedenceKey(const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
bool HasVarValueEncoding(int ref, int64_t value, int *literal=nullptr)
bool DomainContains(int ref, int64_t value) const
void UpdateRuleStats(const std::string &name, int num_times=1)
AffineRelation::Relation GetAffineRelation(int ref) const
bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset, bool debug_no_recursion=false)
std::string IntervalDebugString(int ct_ref) const
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
int LiteralForExpressionMax(const LinearExpressionProto &expr) const
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
void AddImplyInDomain(int b, int x, const Domain &domain)
bool VariableIsOnlyUsedInEncodingAndMaybeInObjective(int ref) const
bool GetAbsRelation(int target_ref, int *ref)
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
int Get(PresolveContext *context) const
int Get(PresolveContext *context) const
int64_t b
int64_t a
CpModelProto proto
CpModelProto const * model_proto
const std::string name
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
GurobiMPCallbackContext * context
const int INFO
Definition: log_severity.h:31
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
bool ScaleAndSetObjective(const SatParameters &params, const std::vector< std::pair< int, double > > &objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool RefIsPositive(int ref)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
constexpr int kAffineRelationConstraint
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
bool ExpressionIsAffine(const LinearExpressionProto &expr)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
Definition: sat/util.cc:104
bool LoadModelForProbing(PresolveContext *context, Model *local_model)
constexpr int kObjectiveConstraint
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
int64_t CapProd(int64_t x, int64_t y)
std::string ProtobufDebugString(const P &message)
Literal literal
Definition: optimization.cc:85
int index
Definition: pack.cc:509
ColIndex representative
IntervalVar * interval
Definition: resource.cc:100
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
const double coeff
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:44