27#include "absl/base/attributes.h"
28#include "absl/container/btree_map.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/flat_hash_set.h"
31#include "absl/flags/flag.h"
32#include "absl/log/check.h"
33#include "absl/meta/type_traits.h"
34#include "absl/numeric/int128.h"
35#include "absl/strings/str_cat.h"
36#include "absl/strings/string_view.h"
37#include "absl/types/span.h"
40#include "ortools/sat/cp_model.pb.h"
48#include "ortools/sat/sat_parameters.pb.h"
59 "DEBUG ONLY. When set to true, the mapping_model.proto will contains "
60 "file:line of the code that created that constraint. This is helpful "
61 "for debugging postsolve");
75 IntegerVariableProto*
const var =
working_model->add_variables();
82 const Domain& domain, absl::Span<
const std::pair<int, int64_t>> definition,
83 bool append_constraint_to_mapping_model) {
84 if (domain.
Size() == 1) {
93 append_constraint_to_mapping_model
96 for (
const auto [var, coeff] : definition) {
97 new_linear->add_vars(var);
98 new_linear->add_coeffs(coeff);
100 new_linear->add_vars(new_var);
101 new_linear->add_coeffs(-1);
102 new_linear->add_domain(0);
103 new_linear->add_domain(0);
105 new_linear->coeffs())) {
107 if (append_constraint_to_mapping_model) {
114 if (!append_constraint_to_mapping_model) {
118 solution_crush_.SetVarToLinearExpression(new_var, definition);
128 const int new_var =
NewBoolVar(
"with clause");
129 solution_crush_.SetVarToClause(new_var, clause);
134 absl::Span<const int> conjunction) {
135 const int new_var =
NewBoolVar(
"with conjunction");
136 solution_crush_.SetVarToConjunction(new_var, conjunction);
141 if (!true_literal_is_defined_) {
142 true_literal_is_defined_ =
true;
144 solution_crush_.SetVarToConjunction(true_literal_, {});
146 return true_literal_;
154 ConstraintProto*
const ct =
working_model->add_constraints();
155 ct->add_enforcement_literal(a);
156 ct->mutable_bool_and()->add_literals(
b);
161 ConstraintProto*
const imply =
working_model->add_constraints();
165 imply->mutable_enforcement_literal()->Resize(1,
b);
166 LinearConstraintProto* mutable_linear = imply->mutable_linear();
167 mutable_linear->mutable_vars()->Resize(1, x);
168 mutable_linear->mutable_coeffs()->Resize(1, 1);
174 ConstraintProto*
const imply =
working_model->add_constraints();
176 imply->mutable_enforcement_literal()->Resize(1,
b);
177 LinearConstraintProto* mutable_linear = imply->mutable_linear();
194 return domains_[var].Min() >= 0 && domains_[var].Max() <= 1;
200 return domains_[lit].Min() == 1;
209 return domains_[lit].Max() == 0;
235 int64_t result = expr.offset();
236 for (
int i = 0;
i < expr.vars_size(); ++
i) {
237 const int64_t coeff = expr.coeffs(
i);
239 result += coeff *
MinOf(expr.vars(
i));
241 result += coeff *
MaxOf(expr.vars(
i));
248 int64_t result = expr.offset();
249 for (
int i = 0;
i < expr.vars_size(); ++
i) {
250 const int64_t coeff = expr.coeffs(
i);
252 result += coeff *
MaxOf(expr.vars(
i));
254 result += coeff *
MinOf(expr.vars(
i));
261 for (
int i = 0;
i < expr.vars_size(); ++
i) {
262 if (expr.coeffs(
i) != 0 && !
IsFixed(expr.vars(
i)))
return false;
268 int64_t result = expr.offset();
269 for (
int i = 0;
i < expr.vars_size(); ++
i) {
270 if (expr.coeffs(
i) == 0)
continue;
277 const LinearExpressionProto& expr)
const {
278 int64_t result = expr.offset();
279 for (
int i = 0;
i < expr.vars_size(); ++
i) {
280 if (expr.coeffs(
i) == 0)
continue;
281 const Domain& domain = domains_[expr.vars(
i)];
282 if (!domain.
IsFixed())
return std::nullopt;
283 result += expr.coeffs(
i) * domain.
Min();
289 const LinearExpressionProto& expr)
const {
290 Domain result(expr.offset());
291 for (
int i = 0;
i < expr.vars_size(); ++
i) {
299 const LinearExpressionProto& expr)
const {
300 if (expr.vars().size() != 1)
return false;
305 const LinearExpressionProto& expr)
const {
306 const int ref = expr.vars(0);
311 const LinearExpressionProto& expr)
const {
312 return expr.offset() == 0 && expr.vars_size() == 1 && expr.coeffs(0) == 1;
316 int* literal)
const {
317 if (expr.vars_size() != 1)
return false;
318 const int ref = expr.vars(0);
320 if (
MinOf(var) < 0 ||
MaxOf(var) > 1)
return false;
322 if (expr.offset() == 0 && expr.coeffs(0) == 1 &&
RefIsPositive(ref)) {
323 if (literal !=
nullptr) *literal = ref;
326 if (expr.offset() == 1 && expr.coeffs(0) == -1 &&
RefIsPositive(ref)) {
327 if (literal !=
nullptr) *literal =
NegatedRef(ref);
330 if (expr.offset() == 1 && expr.coeffs(0) == 1 && !
RefIsPositive(ref)) {
331 if (literal !=
nullptr) *literal = ref;
339 const ConstraintProto& proto =
working_model->constraints(ct_ref);
340 if (!proto.enforcement_literal().empty())
return false;
341 if (!
IsFixed(proto.interval().start()))
return false;
342 if (!
IsFixed(proto.interval().size()))
return false;
343 if (!
IsFixed(proto.interval().end()))
return false;
349 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
"..",
355 return absl::StrCat(
"interval_", ct_ref,
"(lit=", literal,
", ",
359 return absl::StrCat(
"interval_", ct_ref,
"(lit=", literal,
", ",
364 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
367 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
374 const IntervalConstraintProto& interval =
376 return MinOf(interval.start());
380 const IntervalConstraintProto& interval =
382 return MaxOf(interval.start());
386 const IntervalConstraintProto& interval =
388 return MinOf(interval.end());
392 const IntervalConstraintProto& interval =
394 return MaxOf(interval.end());
398 const IntervalConstraintProto& interval =
400 return MinOf(interval.size());
404 const IntervalConstraintProto& interval =
406 return MaxOf(interval.size());
416 return var_to_constraints_[var].size() == 1;
420 return !params_.keep_all_feasible_solutions_in_presolve() &&
427 return var_to_constraints_[var].size() == 2 &&
438 return !params_.keep_all_feasible_solutions_in_presolve() &&
446 return var_to_constraints_[
PositiveRef(ref)].empty();
458 if (
IsFixed(ref))
return false;
459 if (!removed_variables_.contains(
PositiveRef(ref)))
return false;
460 if (!var_to_constraints_[
PositiveRef(ref)].empty()) {
462 " was removed, yet it appears in some constraints!");
465 for (
const int c : var_to_constraints_[
PositiveRef(ref)]) {
479 if (var_to_num_linear1_[var] == 0)
return false;
480 return var_to_num_linear1_[var] == var_to_constraints_[var].size() ||
482 var_to_num_linear1_[var] + 1 == var_to_constraints_[var].size());
488 if (var_to_num_linear1_[var] == 0)
return false;
490 return var_to_num_linear1_[var] + 1 == var_to_constraints_[var].size();
496 result = domains_[ref];
505 return domains_[
PositiveRef(ref)].Contains(-value);
507 return domains_[ref].
Contains(value);
511 int64_t value)
const {
512 CHECK_LE(expr.vars_size(), 1);
516 if (value >
MaxOf(expr))
return false;
517 if (value <
MinOf(expr))
return false;
521 if ((value - expr.offset()) % expr.coeffs(0) != 0)
return false;
522 return DomainContains(expr.vars(0), (value - expr.offset()) / expr.coeffs(0));
526 int ref,
const Domain& domain,
bool* domain_modified) {
531 if (domains_[var].IsIncludedIn(domain)) {
534 domains_[var] = domains_[var].IntersectionWith(domain);
537 if (domains_[var].IsIncludedIn(temp)) {
543 if (domain_modified !=
nullptr) {
544 *domain_modified =
true;
547 if (domains_[var].IsEmpty()) {
549 absl::StrCat(
"var #", ref,
" as empty domain after intersecting with ",
553 solution_crush_.SetOrUpdateVarToDomain(var, domains_[var]);
562 .InverseMultiplicationBy(r.
coeff),
567 const LinearExpressionProto& expr,
const Domain& domain,
568 bool* domain_modified) {
569 if (expr.vars().empty()) {
570 if (domain.
Contains(expr.offset())) {
575 " as empty domain after intersecting with ", domain.
ToString()));
578 if (expr.vars().size() == 1) {
600 const ConstraintProto& ct =
working_model->constraints(index);
601 if (ct.constraint_case() ==
602 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
605 for (
const int literal : ct.enforcement_literal()) {
612 const ConstraintProto& ct =
working_model->constraints(ct_ref);
613 bool contains_one_free_literal =
false;
614 for (
const int literal : ct.enforcement_literal()) {
616 if (!
LiteralIsTrue(literal)) contains_one_free_literal =
true;
618 return contains_one_free_literal;
624 const bool is_todo = name.size() >= 4 && name.substr(0, 4) ==
"TODO";
627 if (logger_->LoggingIsEnabled()) {
629 int level = is_todo ? 3 : 2;
631 params_.debug_max_num_presolve_operations()) <= 100) {
637 stats_by_rule_name_[name] += num_times;
641void PresolveContext::UpdateLinear1Usage(
const ConstraintProto& ct,
int c) {
642 const int old_var = constraint_to_linear1_var_[c];
644 var_to_num_linear1_[old_var]--;
645 DCHECK_GE(var_to_num_linear1_[old_var], 0);
647 if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
648 ct.linear().vars().size() == 1) {
650 constraint_to_linear1_var_[c] = var;
651 var_to_num_linear1_[var]++;
653 constraint_to_linear1_var_[c] = -1;
657void PresolveContext::MaybeResizeIntervalData() {
659 const int num_constraints = constraint_to_vars_.size();
660 if (constraint_to_intervals_.size() != num_constraints) {
661 constraint_to_intervals_.resize(num_constraints);
662 interval_usage_.resize(num_constraints);
666void PresolveContext::AddVariableUsage(
int c) {
670 for (
const int v : constraint_to_vars_[c]) {
671 DCHECK_LT(v, var_to_constraints_.size());
673 var_to_constraints_[v].insert(c);
677 if (!used_interval.empty()) {
678 MaybeResizeIntervalData();
679 constraint_to_intervals_[
c].swap(used_interval);
680 for (
const int i : constraint_to_intervals_[c]) interval_usage_[
i]++;
683 UpdateLinear1Usage(ct, c);
689 solution_crush_.SolutionIsLoaded() &&
691 solution_crush_.GetVarValues())) {
692 LOG(FATAL) <<
"Hint infeasible for constraint #" <<
c <<
" : "
693 << ct.ShortDebugString();
698void PresolveContext::EraseFromVarToConstraint(
int var,
int c) {
699 var_to_constraints_[var].erase(c);
700 if (var_to_constraints_[var].size() <= 3) {
706 if (is_unsat_)
return;
707 DCHECK_EQ(constraint_to_vars_.size(),
working_model->constraints_size());
712 if (c < constraint_to_intervals_.size() || !used_interval.empty()) {
713 MaybeResizeIntervalData();
714 for (
const int i : constraint_to_intervals_[c]) interval_usage_[
i]--;
715 constraint_to_intervals_[c].swap(used_interval);
716 for (
const int i : constraint_to_intervals_[c]) interval_usage_[
i]++;
722 const absl::Span<const int> old_usage = constraint_to_vars_[c];
723 const int old_size = old_usage.size();
725 for (
const int var : new_usage) {
727 while (
i < old_size && old_usage[
i] < var) {
728 EraseFromVarToConstraint(old_usage[
i], c);
731 if (
i < old_size && old_usage[
i] == var) {
734 var_to_constraints_[var].insert(c);
737 for (;
i < old_size; ++
i) {
738 EraseFromVarToConstraint(old_usage[
i], c);
740 constraint_to_vars_[c].swap(new_usage);
742 UpdateLinear1Usage(ct, c);
748 solution_crush_.SolutionIsLoaded() &&
750 solution_crush_.GetVarValues())) {
751 LOG(FATAL) <<
"Hint infeasible for constraint #" << c <<
" : "
752 << ct.ShortDebugString();
758 if (is_unsat_)
return true;
759 return constraint_to_vars_.size() ==
working_model->constraints_size();
763 if (is_unsat_)
return;
764 const int old_size = constraint_to_vars_.size();
766 DCHECK_LE(old_size, new_size);
767 constraint_to_vars_.resize(new_size);
768 constraint_to_linear1_var_.resize(new_size, -1);
769 for (
int c = old_size; c < new_size; ++c) {
775 if (is_unsat_)
return false;
776 if (params_.keep_all_feasible_solutions_in_presolve())
return false;
779 if (time_limit_->LimitReached())
return false;
781 for (
int var = 0; var <
working_model->variables_size(); ++var) {
785 if (constraints.size() == 1 &&
796 if (is_unsat_)
return true;
797 if (var_to_constraints_.size() !=
working_model->variables_size()) {
798 LOG(INFO) <<
"Wrong var_to_constraints_ size!";
801 if (constraint_to_vars_.size() !=
working_model->constraints_size()) {
802 LOG(INFO) <<
"Wrong constraint_to_vars size!";
805 std::vector<int> linear1_count(var_to_constraints_.size(), 0);
806 for (
int c = 0; c < constraint_to_vars_.size(); ++c) {
809 LOG(INFO) <<
"Wrong variables usage for constraint: \n"
811 <<
" old_size: " << constraint_to_vars_[c].size();
814 if (ct.constraint_case() == ConstraintProto::kLinear &&
815 ct.linear().vars().size() == 1) {
817 if (constraint_to_linear1_var_[c] !=
PositiveRef(ct.linear().vars(0))) {
818 LOG(INFO) <<
"Wrong variables for linear1: \n"
820 <<
" saved_var: " << constraint_to_linear1_var_[c];
825 int num_in_objective = 0;
826 for (
int v = 0; v < var_to_constraints_.size(); ++v) {
827 if (linear1_count[v] != var_to_num_linear1_[v]) {
828 LOG(INFO) <<
"Variable " << v <<
" has wrong linear1 count!"
829 <<
" stored: " << var_to_num_linear1_[v]
830 <<
" actual: " << linear1_count[v];
835 if (!objective_map_.contains(v)) {
836 LOG(INFO) <<
"Variable " << v
837 <<
" is marked as part of the objective but isn't.";
842 if (num_in_objective != objective_map_.size()) {
843 LOG(INFO) <<
"Not all variables are marked as part of the objective";
860bool PresolveContext::AddRelation(
int x,
int y, int64_t c, int64_t o,
864 if (std::abs(c) != 1)
return repo->
TryAdd(x, y, c, o);
879 const int64_t m_x = std::max(std::abs(
MinOf(rep_x)), std::abs(
MaxOf(rep_x)));
880 const int64_t m_y = std::max(std::abs(
MinOf(rep_y)), std::abs(
MaxOf(rep_y)));
881 bool allow_rep_x = m_x < m_y;
882 bool allow_rep_y = m_y < m_x;
889 if (allow_rep_x && allow_rep_y) {
899 return repo->
TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
919 .AdditionWith(
Domain(-offset))
920 .InverseMultiplicationBy(coeff))) {
925 DomainOf(rep).MultiplicationBy(coeff).AdditionWith(
Domain(offset)))) {
933 for (
auto& ref_map : var_to_constraints_) {
950 auto* arg = ct->mutable_linear();
954 arg->add_coeffs(-r.
coeff);
955 arg->add_domain(r.
offset);
956 arg->add_domain(r.
offset);
968 CHECK_EQ(var_to_constraints_[var].size(), 1);
978 affine_relations_.IgnoreFromClassSize(var);
982 if (affine_relations_.ClassSize(rep) == 1) {
993 const int64_t min =
MinOf(var);
994 if (min == 0 ||
IsFixed(var))
return;
1004 DCHECK(proto->has_floating_point_objective());
1005 DCHECK(!proto->has_objective());
1006 const auto& objective = proto->floating_point_objective();
1007 std::vector<std::pair<int, double>> terms;
1008 for (
int i = 0;
i < objective.vars_size(); ++
i) {
1010 terms.push_back({objective.vars(
i), objective.coeffs(
i)});
1012 const double offset = objective.offset();
1013 const bool maximize = objective.maximize();
1014 proto->clear_floating_point_objective();
1020 int64_t mod, int64_t rhs) {
1024 const int64_t gcd = std::gcd(coeff, mod);
1026 if (rhs % gcd != 0) {
1028 absl::StrCat(
"Infeasible ", coeff,
" * X = ", rhs,
" % ", mod));
1036 if (std::abs(mod) == 1)
return true;
1050 const Domain new_domain =
1054 "Empty domain in CanonicalizeAffineVariable()");
1070 const int64_t min_value = new_domain.
Min();
1085 bool debug_no_recursion) {
1088 DCHECK_NE(coeff, 0);
1089 if (is_unsat_)
return false;
1093 solution_crush_.SetVarToLinearConstraintSolution(1, {var_x, var_y},
1094 {1, -coeff}, offset);
1104 if (lhs % std::abs(coeff) != 0) {
1127 const int64_t a = coeff * ry.
coeff - rx.
coeff;
1137 const int64_t unique_value = -
b / a;
1157 int64_t a = rx.
coeff;
1158 int64_t
b = -coeff * ry.
coeff;
1163 const int64_t gcd = std::gcd(std::abs(a), std::abs(
b));
1174 if (std::abs(a) > 1 && std::abs(
b) > 1) {
1189 bool negate =
false;
1190 if (std::abs(a) == 1) {
1196 CHECK_EQ(std::abs(
b), 1);
1211 y,
DomainOf(x).AdditionWith(
Domain(-o)).InverseMultiplicationBy(c))) {
1216 DomainOf(y).ContinuousMultiplicationBy(c).AdditionWith(
Domain(o)))) {
1226 if (std::abs(o) > std::max(std::abs(
MinOf(x)), std::abs(
MaxOf(x)))) {
1229 CHECK(!debug_no_recursion);
1235 CHECK(AddRelation(x, y, c, o, &affine_relations_));
1262 int ref_a,
int ref_b) {
1263 if (is_unsat_)
return false;
1272 if (ref_a == ref_b)
return true;
1309 DCHECK_NE(positive_possible, negative_possible);
1343 var_to_constraints_.clear();
1344 var_to_num_linear1_.clear();
1345 objective_map_.clear();
1346 DCHECK(!solution_crush_.SolutionIsLoaded());
1352 DCHECK_GE(new_size, domains_.size());
1353 if (domains_.size() == new_size)
return;
1357 var_to_constraints_.resize(new_size);
1358 var_to_num_linear1_.resize(new_size);
1362 const int old_size = domains_.size();
1363 domains_.resize(new_size);
1364 for (
int i = old_size;
i < new_size; ++
i) {
1367 if (domains_[
i].IsEmpty()) {
1374 solution_crush_.Resize(new_size);
1378 absl::flat_hash_map<int, int64_t> hint_values;
1381 int num_changes = 0;
1382 for (
int i = 0;
i < hint_proto.vars().size(); ++
i) {
1383 const int var = hint_proto.vars(
i);
1385 const int64_t hint_value = hint_proto.values(
i);
1387 if (clamped_hint_value != hint_value) {
1390 hint_values[var] = clamped_hint_value;
1392 if (num_changes > 0) {
1393 UpdateRuleStats(
"hint: moved var hint within its domain.", num_changes);
1396 if (!hint_values.contains(
i) &&
IsFixed(
i)) {
1401 solution_crush_.LoadSolution(
working_model->variables().size(), hint_values);
1407 const int64_t var_min =
MinOf(var);
1408 const int64_t var_max =
MaxOf(var);
1410 if (is_unsat_)
return;
1412 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1415 auto min_it = var_map.find(var_min);
1416 if (min_it != var_map.end()) {
1417 const int old_var =
PositiveRef(min_it->second.Get(
this));
1418 if (removed_variables_.contains(old_var)) {
1419 var_map.erase(min_it);
1420 min_it = var_map.end();
1425 auto max_it = var_map.find(var_max);
1426 if (max_it != var_map.end()) {
1427 const int old_var =
PositiveRef(max_it->second.Get(
this));
1428 if (removed_variables_.contains(old_var)) {
1429 var_map.erase(max_it);
1430 max_it = var_map.end();
1437 if (min_it != var_map.end() && max_it != var_map.end()) {
1438 min_literal = min_it->second.Get(
this);
1439 max_literal = max_it->second.Get(
this);
1440 if (min_literal !=
NegatedRef(max_literal)) {
1449 }
else if (min_it != var_map.end() && max_it == var_map.end()) {
1451 min_literal = min_it->second.Get(
this);
1454 }
else if (min_it == var_map.end() && max_it != var_map.end()) {
1456 max_literal = max_it->second.Get(
this);
1461 max_literal =
NewBoolVar(
"var with 2 values");
1462 solution_crush_.MaybeSetLiteralToValueEncoding(max_literal, var, var_max);
1484 var_max - var_min, var_min);
1487 var_min - var_max, var_max);
1492bool PresolveContext::InsertVarValueEncodingInternal(
int literal,
int var,
1494 bool add_constraints) {
1498 if (is_unsat_)
return false;
1499 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1502 if (!
DomainOf(var).Contains(value)) {
1516 const auto [it, inserted] =
1517 var_map.insert(std::make_pair(value, SavedLiteral(literal)));
1519 const int previous_literal = it->second.Get(
this);
1526 it->second = SavedLiteral(literal);
1528 if (literal != previous_literal) {
1530 "variables: merge equivalent var value encoding literals");
1544 if (is_unsat_)
return false;
1555 CanonicalizeEncoding(&var, &value);
1567 }
else if (add_constraints) {
1577bool PresolveContext::InsertHalfVarValueEncoding(
int literal,
int var,
1578 int64_t value,
bool imply_eq) {
1579 if (is_unsat_)
return false;
1584 auto& direct_set = imply_eq ? eq_half_encoding_ : neq_half_encoding_;
1585 auto insert_result = direct_set.insert({{literal, var}, value});
1586 if (!insert_result.second) {
1587 if (insert_result.first->second != value && imply_eq) {
1595 auto negated_encoding = direct_set.find({
NegatedRef(literal), var});
1596 if (negated_encoding != direct_set.end()) {
1597 if (negated_encoding->second == value) {
1599 "variables: both boolean and its negation imply same equality");
1604 const int64_t other_value = negated_encoding->second;
1609 "variables: both boolean and its negation fix the same variable");
1619 VLOG(2) <<
"Collect lit(" << literal <<
") implies var(" << var
1620 << (imply_eq ?
") == " :
") != ") << value;
1625 auto& other_set = imply_eq ? neq_half_encoding_ : eq_half_encoding_;
1626 auto it = other_set.find({
NegatedRef(literal), var});
1627 if (it != other_set.end() && it->second == value) {
1629 const int imply_eq_literal = imply_eq ? literal :
NegatedRef(literal);
1630 if (!InsertVarValueEncodingInternal(imply_eq_literal, var, value,
1639bool PresolveContext::CanonicalizeEncoding(
int* ref, int64_t* value) {
1641 if ((*value - r.offset) % r.coeff != 0)
return false;
1642 *ref = r.representative;
1643 *value = (*value - r.offset) / r.coeff;
1649 if (!CanonicalizeEncoding(&var, &value) || !
DomainOf(var).Contains(value)) {
1653 if (!InsertVarValueEncodingInternal(literal, var, value,
1658 eq_half_encoding_.insert({{literal, var}, value});
1659 neq_half_encoding_.insert({{
NegatedRef(literal), var}, value});
1661 solution_crush_.MaybeSetLiteralToValueEncoding(literal, var, value);
1667 if (!CanonicalizeEncoding(&var, &value) || !
DomainOf(var).Contains(value)) {
1672 return InsertHalfVarValueEncoding(literal, var, value,
true);
1677 if (!CanonicalizeEncoding(&var, &value) || !
DomainOf(var).Contains(value)) {
1682 return InsertHalfVarValueEncoding(literal, var, value,
false);
1688 if (!CanonicalizeEncoding(&ref, &value))
return false;
1691 if (!
DomainOf(ref).Contains(value)) {
1697 if (literal !=
nullptr) {
1698 *literal = value == 1 ? ref :
NegatedRef(ref);
1703 const auto first_it = encoding_.find(ref);
1704 if (first_it == encoding_.end())
return false;
1705 const auto it = first_it->second.find(value);
1706 if (it == first_it->second.end())
return false;
1709 if (literal !=
nullptr) {
1710 *literal = it->second.Get(
this);
1717 const int64_t size = domains_[var].Size();
1718 if (size <= 2)
return true;
1719 const auto& it = encoding_.find(var);
1720 return it == encoding_.end() ? false : size <= it->second.size();
1724 CHECK_LE(expr.vars_size(), 1);
1725 if (
IsFixed(expr))
return true;
1734 const int var = ref;
1737 if (!domains_[var].Contains(value)) {
1747 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1748 auto it = var_map.find(value);
1749 if (it != var_map.end()) {
1750 const int lit = it->second.Get(
this);
1754 var_map.erase(value);
1761 if (domains_[var].Size() == 1) {
1764 return true_literal;
1768 const int64_t var_min =
MinOf(var);
1769 const int64_t var_max =
MaxOf(var);
1770 if (domains_[var].Size() == 2) {
1772 const int64_t other_value = value == var_min ? var_max : var_min;
1773 auto other_it = var_map.find(other_value);
1774 if (other_it != var_map.end()) {
1775 const int literal =
NegatedRef(other_it->second.Get(
this));
1779 var_map.erase(other_value);
1788 if (var_min == 0 && var_max == 1) {
1792 return value == 1 ? representative :
NegatedRef(representative);
1794 const int literal =
NewBoolVar(
"integer encoding");
1799 return value == var_max ? representative :
NegatedRef(representative);
1803 const int literal =
NewBoolVar(
"integer encoding");
1811 const LinearExpressionProto& expr, int64_t value) {
1812 DCHECK_LE(expr.vars_size(), 1);
1821 if ((value - expr.offset()) % expr.coeffs(0) != 0) {
1826 (value - expr.offset()) / expr.coeffs(0));
1833 objective_proto_is_up_to_date_ =
false;
1835 objective_offset_ = obj.offset();
1836 objective_scaling_factor_ = obj.scaling_factor();
1837 if (objective_scaling_factor_ == 0.0) {
1838 objective_scaling_factor_ = 1.0;
1841 objective_integer_before_offset_ = obj.integer_before_offset();
1842 objective_integer_after_offset_ = obj.integer_after_offset();
1843 objective_integer_scaling_factor_ = obj.integer_scaling_factor();
1844 if (objective_integer_scaling_factor_ == 0) {
1845 objective_integer_scaling_factor_ = 1;
1848 if (!obj.domain().empty()) {
1851 objective_domain_is_constraining_ =
true;
1854 objective_domain_is_constraining_ =
false;
1861 objective_overflow_detection_ = std::abs(objective_integer_before_offset_);
1862 int64_t fixed_terms = 0;
1864 objective_map_.clear();
1865 for (
int i = 0;
i < obj.vars_size(); ++
i) {
1866 int var = obj.vars(
i);
1867 int64_t coeff = obj.coeffs(
i);
1882 const int64_t var_max_magnitude =
1883 std::max(std::abs(
MinOf(var)), std::abs(
MaxOf(var)));
1884 objective_overflow_detection_ += var_max_magnitude * std::abs(coeff);
1887 if (objective_map_[var] == 0) {
1894 if (fixed_terms != 0) {
1900 const auto it = objective_map_.find(var);
1901 if (it == objective_map_.end())
return true;
1902 const int64_t coeff = it->second;
1907 if (params_.cp_model_presolve() &&
1908 !params_.keep_all_feasible_solutions_in_presolve() &&
1909 !objective_domain_is_constraining_ &&
1911 var_to_constraints_[var].size() == 1 &&
1945 if (new_coeff == 0) {
1958 objective_proto_is_up_to_date_ =
false;
1964 tmp_entries_.clear();
1965 for (
const auto& entry : objective_map_) {
1966 tmp_entries_.push_back(entry);
1972 for (
const auto& entry : tmp_entries_) {
1978 Domain implied_domain(0);
1982 tmp_entries_.clear();
1983 for (
const auto& entry : objective_map_) {
1984 tmp_entries_.push_back(entry);
1986 std::sort(tmp_entries_.begin(), tmp_entries_.end());
1987 for (
const auto& entry : tmp_entries_) {
1988 const int var = entry.first;
1989 const int64_t coeff = entry.second;
1990 gcd = std::gcd(gcd, std::abs(coeff));
1998 objective_domain_ = objective_domain_.IntersectionWith(implied_domain);
2001 if (simplify_domain) {
2003 objective_domain_.SimplifyUsingImpliedDomain(implied_domain);
2008 for (
auto& entry : objective_map_) {
2009 entry.second /= gcd;
2011 objective_domain_ = objective_domain_.InverseMultiplicationBy(gcd);
2012 if (objective_domain_.IsEmpty()) {
2016 objective_offset_ /=
static_cast<double>(gcd);
2017 objective_scaling_factor_ *=
static_cast<double>(gcd);
2020 absl::int128 offset = absl::int128(objective_integer_before_offset_) *
2021 absl::int128(objective_integer_scaling_factor_) +
2022 absl::int128(objective_integer_after_offset_);
2024 if (objective_domain_.IsFixed()) {
2028 objective_integer_scaling_factor_ = 1;
2030 absl::int128(gcd - 1) * absl::int128(objective_domain_.FixedValue());
2032 objective_integer_scaling_factor_ *= gcd;
2035 objective_integer_before_offset_ =
static_cast<int64_t
>(
2036 offset / absl::int128(objective_integer_scaling_factor_));
2037 objective_integer_after_offset_ =
static_cast<int64_t
>(
2038 offset % absl::int128(objective_integer_scaling_factor_));
2045 if (objective_domain_.IsEmpty()) {
2052 objective_domain_is_constraining_ =
2055 objective_domain_.Max()))
2061 CHECK_EQ(objective_map_.size(), 1);
2062 const int var = objective_map_.begin()->first;
2063 const int64_t coeff = objective_map_.begin()->second;
2067 objective_domain_.InverseMultiplicationBy(coeff))) {
2072 objective_proto_is_up_to_date_ =
false;
2074 objective_domain_is_constraining_ =
false;
2079 objective_proto_is_up_to_date_ =
false;
2081 objective_map_.erase(var);
2087 objective_proto_is_up_to_date_ =
false;
2088 int64_t& map_ref = objective_map_[var];
2098 objective_proto_is_up_to_date_ =
false;
2100 int64_t& map_ref = objective_map_[var];
2115 objective_proto_is_up_to_date_ =
false;
2116 const int64_t temp =
CapAdd(objective_integer_before_offset_, delta);
2117 if (temp == std::numeric_limits<int64_t>::min())
return false;
2118 if (temp == std::numeric_limits<int64_t>::max())
return false;
2119 objective_integer_before_offset_ = temp;
2122 objective_offset_ +=
static_cast<double>(delta);
2123 objective_domain_ = objective_domain_.AdditionWith(
Domain(-delta));
2128 int var_in_equality, int64_t coeff_in_equality,
2129 const ConstraintProto& equality) {
2130 objective_proto_is_up_to_date_ =
false;
2131 CHECK(equality.enforcement_literal().empty());
2136 const int64_t coeff_in_objective = objective_map_.at(var_in_equality);
2137 CHECK_NE(coeff_in_equality, 0);
2138 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
2140 const int64_t multiplier = coeff_in_objective / coeff_in_equality;
2144 for (
int i = 0;
i < equality.linear().vars().size(); ++
i) {
2145 int var = equality.linear().vars(
i);
2146 if (
PositiveRef(var) == var_in_equality)
continue;
2147 int64_t coeff = equality.linear().coeffs(
i);
2149 std::abs(coeff) * std::max(std::abs(
MinOf(var)), std::abs(
MaxOf(var)));
2151 const int64_t new_value =
2153 objective_overflow_detection_ -
2154 std::abs(coeff_in_equality) *
2155 std::max(std::abs(
MinOf(var_in_equality)),
2156 std::abs(
MaxOf(var_in_equality))));
2157 if (new_value == std::numeric_limits<int64_t>::max())
return false;
2158 objective_overflow_detection_ = new_value;
2162 DCHECK_EQ(offset.
Min(), offset.
Max());
2172 for (
int i = 0;
i < equality.linear().vars().size(); ++
i) {
2173 int var = equality.linear().vars(
i);
2174 int64_t coeff = equality.linear().coeffs(
i);
2179 if (var == var_in_equality)
continue;
2181 int64_t& map_ref = objective_map_[var];
2182 map_ref -= coeff * multiplier;
2196 objective_domain_is_constraining_ =
true;
2198 if (objective_domain_.IsEmpty()) {
2205 absl::Span<const int> exactly_one) {
2206 if (objective_map_.empty())
return false;
2207 if (exactly_one.empty())
return false;
2209 int64_t min_coeff = std::numeric_limits<int64_t>::max();
2210 for (
const int ref : exactly_one) {
2211 const auto it = objective_map_.find(
PositiveRef(ref));
2212 if (it == objective_map_.end())
return false;
2214 const int64_t coeff = it->second;
2216 min_coeff = std::min(min_coeff, coeff);
2219 min_coeff = std::min(min_coeff, -coeff);
2228 if (shift == 0)
return true;
2236 int64_t new_sum = 0;
2237 for (
const int ref : exactly_one) {
2240 sum =
CapAdd(sum, std::abs(obj));
2242 const int64_t new_obj =
RefIsPositive(ref) ? obj - shift : obj + shift;
2243 new_sum =
CapAdd(new_sum, std::abs(new_obj));
2246 if (new_sum > sum) {
2247 const int64_t new_value =
2248 CapAdd(objective_overflow_detection_, new_sum - sum);
2250 objective_overflow_detection_ = new_value;
2253 int64_t offset = shift;
2254 objective_proto_is_up_to_date_ =
false;
2255 for (
const int ref : exactly_one) {
2259 int64_t& map_ref = objective_map_[var];
2290 if (!objective_domain_is_constraining_) {
2292 Domain(std::numeric_limits<int64_t>::min(), objective_domain_.Max());
2299 if (objective_proto_is_up_to_date_)
return;
2300 objective_proto_is_up_to_date_ =
true;
2304 tmp_entries_.clear();
2305 tmp_entries_.reserve(objective_map_.size());
2306 for (
const auto& entry : objective_map_) {
2307 tmp_entries_.push_back(entry);
2309 std::sort(tmp_entries_.begin(), tmp_entries_.end(),
2310 [](
const std::pair<int, int64_t>& a,
2311 const std::pair<int, int64_t>&
b) { return a.first < b.first; });
2313 CpObjectiveProto* mutable_obj =
working_model->mutable_objective();
2314 mutable_obj->set_offset(objective_offset_);
2315 mutable_obj->set_scaling_factor(objective_scaling_factor_);
2316 mutable_obj->set_integer_before_offset(objective_integer_before_offset_);
2317 mutable_obj->set_integer_after_offset(objective_integer_after_offset_);
2318 if (objective_integer_scaling_factor_ == 1) {
2319 mutable_obj->set_integer_scaling_factor(0);
2321 mutable_obj->set_integer_scaling_factor(objective_integer_scaling_factor_);
2324 mutable_obj->clear_vars();
2325 mutable_obj->clear_coeffs();
2326 for (
const auto& entry : tmp_entries_) {
2327 mutable_obj->add_vars(entry.first);
2328 mutable_obj->add_coeffs(entry.second);
2339 const LinearExpressionProto& time_i,
const LinearExpressionProto& time_j,
2340 int active_i,
int active_j) {
2346 const std::tuple<int, int64_t, int, int64_t, int64_t, int, int> key =
2348 const auto& it = reified_precedences_cache_.find(key);
2349 if (it != reified_precedences_cache_.end())
return it->second;
2351 const int result =
NewBoolVar(
"precedences");
2352 reified_precedences_cache_[key] = result;
2354 solution_crush_.SetVarToReifiedPrecedenceLiteral(result, time_i, time_j,
2355 active_i, active_j);
2359 {time_i.vars(0), time_j.vars(0)},
2360 {-time_i.coeffs(0), time_j.coeffs(0)}));
2364 ConstraintProto*
const lesseq =
working_model->add_constraints();
2365 lesseq->add_enforcement_literal(result);
2367 lesseq->mutable_linear()->add_vars(time_i.vars(0));
2368 lesseq->mutable_linear()->add_coeffs(-time_i.coeffs(0));
2371 lesseq->mutable_linear()->add_vars(time_j.vars(0));
2372 lesseq->mutable_linear()->add_coeffs(time_j.coeffs(0));
2375 const int64_t offset =
2378 lesseq->mutable_linear()->add_domain(offset);
2379 lesseq->mutable_linear()->add_domain(std::numeric_limits<int64_t>::max());
2391 ConstraintProto*
const greater =
working_model->add_constraints();
2393 greater->mutable_linear()->add_vars(time_i.vars(0));
2394 greater->mutable_linear()->add_coeffs(-time_i.coeffs(0));
2397 greater->mutable_linear()->add_vars(time_j.vars(0));
2398 greater->mutable_linear()->add_coeffs(time_j.coeffs(0));
2400 greater->mutable_linear()->add_domain(std::numeric_limits<int64_t>::min());
2401 greater->mutable_linear()->add_domain(offset - 1);
2403 greater->add_enforcement_literal(
NegatedRef(result));
2405 greater->add_enforcement_literal(active_i);
2408 greater->add_enforcement_literal(active_j);
2418 const auto& rev_it = reified_precedences_cache_.find(
2420 if (rev_it != reified_precedences_cache_.end()) {
2421 auto*
const bool_or =
working_model->add_constraints()->mutable_bool_or();
2422 bool_or->add_literals(result);
2423 bool_or->add_literals(rev_it->second);
2435std::tuple<int, int64_t, int, int64_t, int64_t, int, int>
2437 const LinearExpressionProto& time_j,
2438 int active_i,
int active_j) {
2440 IsFixed(time_i) ? std::numeric_limits<int>::min() : time_i.vars(0);
2441 const int64_t coeff_i =
IsFixed(time_i) ? 0 : time_i.coeffs(0);
2443 IsFixed(time_j) ? std::numeric_limits<int>::min() : time_j.vars(0);
2444 const int64_t coeff_j =
IsFixed(time_j) ? 0 : time_j.coeffs(0);
2445 const int64_t offset =
2450 if (active_j < active_i) std::swap(active_i, active_j);
2451 return std::make_tuple(var_i, coeff_i, var_j, coeff_j, offset, active_i,
2456 reified_precedences_cache_.clear();
2463 " affine relations were detected.");
2464 absl::btree_map<std::string, int> sorted_rules(stats_by_rule_name_.begin(),
2465 stats_by_rule_name_.end());
2466 for (
const auto& entry : sorted_rules) {
2467 if (entry.second == 1) {
2468 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied 1 time.");
2470 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied ",
2492 const CpModelProto& model_proto = *(context->
working_model);
2494 SatParameters local_params = context->
params();
2495 local_params.set_use_implied_bounds(
false);
2497 local_model,
"probing");
2502 absl::string_view name_for_logging) {
2503 *local_model->
GetOrCreate<SatParameters>() = std::move(params);
2518 if (sat_solver->ModelIsUnsat()) {
2520 absl::StrCat(
"Initial loading for ", name_for_logging));
2522 for (
const ConstraintProto& ct : model_proto.constraints()) {
2523 if (mapping->ConstraintIsAlreadyLoaded(&ct))
continue;
2525 if (sat_solver->ModelIsUnsat()) {
2527 absl::StrCat(
"after loading constraint during ", name_for_logging,
2531 encoder->AddAllImplicationsBetweenAssociatedLiterals();
2532 if (sat_solver->ModelIsUnsat())
return false;
2533 if (!sat_solver->Propagate()) {
2535 "during probing initial propagation");
2541template <
typename ProtoWithVarsAndCoeffs,
typename PresolveContextT>
2543 absl::Span<const int> enforcements, ProtoWithVarsAndCoeffs* proto,
2544 int64_t* offset, std::vector<std::pair<int, int64_t>>* tmp_terms,
2545 PresolveContextT* context) {
2551 int64_t sum_of_fixed_terms = 0;
2552 bool remapped =
false;
2553 const int old_size = proto->vars().size();
2554 DCHECK_EQ(old_size, proto->coeffs().size());
2555 for (
int i = 0;
i < old_size; ++
i) {
2563 const int ref = proto->vars(
i);
2565 const int64_t coeff =
2567 if (coeff == 0)
continue;
2569 if (context->IsFixed(var)) {
2570 sum_of_fixed_terms += coeff * context->FixedValue(var);
2577 sum_of_fixed_terms += coeff * r.
offset;
2581 new_coeff = coeff * r.
coeff;
2586 bool removed =
false;
2587 for (
const int enf : enforcements) {
2591 sum_of_fixed_terms += new_coeff;
2600 context->UpdateRuleStats(
"linear: enforcement literal in expression");
2604 tmp_terms->push_back({new_var, new_coeff});
2606 proto->clear_vars();
2607 proto->clear_coeffs();
2608 std::sort(tmp_terms->begin(), tmp_terms->end());
2609 int current_var = 0;
2610 int64_t current_coeff = 0;
2611 for (
const auto& entry : *tmp_terms) {
2613 if (entry.first == current_var) {
2614 current_coeff += entry.second;
2616 if (current_coeff != 0) {
2617 proto->add_vars(current_var);
2618 proto->add_coeffs(current_coeff);
2620 current_var = entry.first;
2621 current_coeff = entry.second;
2624 if (current_coeff != 0) {
2625 proto->add_vars(current_var);
2626 proto->add_coeffs(current_coeff);
2629 context->UpdateRuleStats(
"linear: remapped using affine relations");
2631 if (proto->vars().size() < old_size) {
2632 context->UpdateRuleStats(
"linear: fixed or dup variables");
2634 *offset = sum_of_fixed_terms;
2635 return remapped || proto->vars().size() < old_size;
2639bool CanonicalizeLinearExpressionNoContext(absl::Span<const int> enforcements,
2640 LinearConstraintProto* proto) {
2641 struct DummyContext {
2642 bool IsFixed(
int )
const {
return false; }
2643 int64_t FixedValue(
int )
const {
return 0; }
2644 AffineRelation::Relation GetAffineRelation(
int var)
const {
2647 void UpdateRuleStats(absl::string_view )
const {}
2650 std::vector<std::pair<int, int64_t>> tmp_terms;
2652 enforcements, proto, &offset, &tmp_terms, &dummy_context);
2664 ct->enforcement_literal(), ct->mutable_linear(), &offset, &tmp_terms_,
2669 ct->mutable_linear());
2675 absl::Span<const int> enforcements, LinearExpressionProto* expr) {
2678 enforcements, expr, &offset, &tmp_terms_,
this);
2679 expr->set_offset(expr->offset() + offset);
2687 if (absl::GetFlag(FLAGS_cp_model_debug_postsolve)) {
2688 new_ct->set_name(absl::StrCat(
"#", c,
" ",
file,
":", line));
2694 const ConstraintProto& base_ct, absl::string_view
file,
int line) {
2698 if (absl::GetFlag(FLAGS_cp_model_debug_postsolve)) {
2699 new_ct->set_name(absl::StrCat(
"#c", c,
" ",
file,
":", line));
2706 std::vector<int>* variable_mapping,
2707 CpModelProto* mini_model) {
2708 mini_model->Clear();
2710 *mini_model->add_constraints() = ct;
2712 absl::flat_hash_map<int, int> inverse_interval_map;
2714 auto [it, inserted] =
2715 inverse_interval_map.insert({
i, mini_model->constraints_size()});
2717 const ConstraintProto& itv_ct = context->
working_model->constraints(
i);
2718 *mini_model->add_constraints() = itv_ct;
2722 ConstraintProto* linear = mini_model->add_constraints();
2723 *linear->mutable_enforcement_literal() = itv_ct.enforcement_literal();
2724 LinearConstraintProto* mutable_linear = linear->mutable_linear();
2725 const IntervalConstraintProto& itv = itv_ct.interval();
2727 mutable_linear->add_domain(0);
2728 mutable_linear->add_domain(0);
2732 CanonicalizeLinearExpressionNoContext(itv_ct.enforcement_literal(),
2737 absl::flat_hash_map<int, int> inverse_variable_map;
2738 for (
const ConstraintProto& cur_ct : mini_model->constraints()) {
2740 auto [it, inserted] =
2741 inverse_variable_map.insert({v, mini_model->variables_size()});
2748 variable_mapping->resize(inverse_variable_map.size());
2749 for (
const auto& [k, v] : inverse_variable_map) {
2750 (*variable_mapping)[v] = k;
2752 const auto mapping_function = [&inverse_variable_map](
int*
i) {
2754 const int positive_ref = is_positive ? *
i :
NegatedRef(*
i);
2756 const auto it = inverse_variable_map.find(positive_ref);
2757 DCHECK(it != inverse_variable_map.end());
2758 *
i = is_positive ? it->second :
NegatedRef(it->second);
2760 const auto interval_mapping_function = [&inverse_interval_map](
int*
i) {
2761 const auto it = inverse_interval_map.find(*
i);
2762 DCHECK(it != inverse_interval_map.end());
2765 for (ConstraintProto& ct : *mini_model->mutable_constraints()) {
2774 const absl::Span<const int64_t> hint = solution_crush_.GetVarValues();
2775 if (hint.size() !=
working_model->variables().size())
return false;
bool TryAdd(int x, int y, int64_t coeff, int64_t offset)
Relation Get(int x) const
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Domain IntersectionWith(const Domain &domain) const
Domain ContinuousMultiplicationBy(int64_t coeff) const
bool Contains(int64_t value) const
Domain AdditionWith(const Domain &domain) const
bool IsIncludedIn(const Domain &domain) const
int64_t FixedValue() const
std::string ToString() const
static Domain AllValues()
Domain RelaxIfTooComplex() const
Domain InverseMultiplicationBy(int64_t coeff) const
int64_t ClosestValue(int64_t wanted) const
void DisableImplicationBetweenLiteral()
void Register(T *non_owned_class)
Domain DomainOf(int ref) const
const SatParameters & params() const
ABSL_MUST_USE_RESULT bool CanonicalizeObjective(bool simplify_domain=true)
bool ExpressionIsSingleVariable(const LinearExpressionProto &expr) const
Returns true iff the expr is of the form 1 * var + 0.
bool CanonicalizeAffineVariable(int ref, int64_t coeff, int64_t mod, int64_t rhs)
bool AddToObjectiveOffset(int64_t delta)
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
bool ExpressionIsALiteral(const LinearExpressionProto &expr, int *literal=nullptr) const
Returns true iff the expr is a literal (x or not(x)).
CpModelProto * working_model
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
bool VariableIsUniqueAndRemovable(int ref) const
void ClearStats()
Clears the "rules" statistics.
bool StoreAffineRelation(int var_x, int var_y, int64_t coeff, int64_t offset, bool debug_no_recursion=false)
SparseBitset< int > modified_domains
Each time a domain is modified this is set to true.
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
bool LiteralIsFalse(int lit) const
void CanonicalizeVariable(int ref)
bool VariableWithCostIsUniqueAndRemovable(int ref) const
int NewIntVarWithDefinition(const Domain &domain, absl::Span< const std::pair< int, int64_t > > definition, bool append_constraint_to_mapping_model=false)
bool DomainIsEmpty(int ref) const
Helpers to query the current domain of a variable.
ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality)
int64_t FixedValue(int ref) const
void AddImplication(int a, int b)
a => b.
bool IntervalIsConstant(int ct_ref) const
Helper to query the state of an interval.
int NewIntVar(const Domain &domain)
Helpers to adds new variables to the presolved model.
bool DebugTestHintFeasibility()
bool ShiftCostInExactlyOne(absl::Span< const int > exactly_one, int64_t shift)
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
Returns false if the 'lit' doesn't have the desired value in the domain.
int64_t ObjectiveCoeff(int var) const
void ReadObjectiveFromProto()
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(absl::string_view message="")
bool HasUnusedAffineVariable() const
void CanonicalizeDomainOfSizeTwo(int var)
int GetOrCreateVarValueEncoding(int ref, int64_t value)
int64_t num_presolve_operations
int64_t SizeMax(int ct_ref) const
ABSL_MUST_USE_RESULT bool CanonicalizeOneObjectiveVariable(int var)
std::string IntervalDebugString(int ct_ref) const
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
void WriteObjectiveToProto() const
bool IsFixed(int ref) const
int64_t EndMin(int ct_ref) const
bool VariableIsOnlyUsedInLinear1AndOneExtraConstraint(int var) const
int64_t EndMax(int ct_ref) const
bool StoreBooleanEqualityRelation(int ref_a, int ref_b)
void AddImplyInDomain(int b, int x, const Domain &domain)
b => (x ∈ domain).
int64_t SizeMin(int ct_ref) const
bool RecomputeSingletonObjectiveDomain()
bool VariableIsOnlyUsedInEncodingAndMaybeInObjective(int var) const
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)
int NewBoolVar(absl::string_view source)
void LogInfo()
Logs stats to the logger.
void RemoveNonRepresentativeAffineVariableIfUnused(int var)
void ClearPrecedenceCache()
Clear the precedence cache.
bool IsFullyEncoded(int ref) const
bool ConstraintIsInactive(int ct_index) const
int NewBoolVarWithClause(absl::Span< const int > clause)
bool HasVarValueEncoding(int ref, int64_t value, int *literal=nullptr)
void RemoveVariableFromObjective(int ref)
Allows to manipulate the objective coefficients.
std::optional< int64_t > FixedValueOrNullopt(const LinearExpressionProto &expr) const
This is faster than testing IsFixed() + FixedValue().
bool ConstraintIsOptional(int ct_ref) const
void RemoveAllVariablesFromAffineRelationConstraint()
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
Return a super-set of the domain of the linear expression.
bool PropagateAffineRelation(int var)
void InitializeNewDomains()
Creates the internal structure for any new variables in working_model.
bool VariableIsNotUsedAnymore(int ref) const
Returns true if this ref no longer appears in the model.
bool VariableIsUnique(int ref) const
Returns true if this ref only appear in one constraint.
bool VariableWasRemoved(int ref) const
ConstraintProto * NewMappingConstraint(absl::string_view file, int line)
bool ConstraintVariableGraphIsUpToDate() const
int64_t StartMax(int ct_ref) const
int GetLiteralRepresentative(int ref) const
Returns the representative of a literal.
int GetOrCreateAffineValueEncoding(const LinearExpressionProto &expr, int64_t value)
int LiteralForExpressionMax(const LinearExpressionProto &expr) const
void UpdateConstraintVariableUsage(int c)
SparseBitset< int > var_with_reduced_small_degree
bool CanonicalizeLinearExpression(absl::Span< const int > enforcements, LinearExpressionProto *expr)
void UpdateRuleStats(const std::string &name, int num_times=1)
AffineRelation::Relation GetAffineRelation(int ref) const
Returns the representative of ref under the affine relations.
void AddToObjective(int var, int64_t value)
const absl::flat_hash_set< int > & VarToConstraints(int var) const
std::string RefDebugString(int ref) const
To facilitate debugging.
void MarkVariableAsRemoved(int ref)
void AddLiteralToObjective(int ref, int64_t value)
void WriteVariableDomainsToProto() const
bool InsertVarValueEncoding(int literal, int var, int64_t value)
void UpdateNewConstraintsVariableUsage()
Calls UpdateConstraintVariableUsage() on all newly created constraints.
int NumAffineRelations() const
Used for statistics.
bool CanBeUsedAsLiteral(int ref) const
bool ConstraintVariableUsageIsConsistent()
int64_t StartMin(int ct_ref) const
std::string AffineRelationDebugString(int ref) const
int64_t MinOf(int ref) const
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
int64_t MaxOf(int ref) const
void RemoveVariableFromAffineRelation(int var)
ModelRandomGenerator * random()
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
bool VariableWithCostIsUnique(int ref) const
CpModelProto * mapping_model
bool DomainContains(int ref, int64_t value) const
bool ModelIsUnsat() const
int NewBoolVarWithConjunction(absl::Span< const int > conjunction)
int GetOrCreateReifiedPrecedenceLiteral(const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
bool CanonicalizeLinearConstraint(ConstraintProto *ct)
bool LiteralIsTrue(int lit) const
int Get(PresolveContext *context) const
bool CanonicalizeLinearExpressionInternal(absl::Span< const int > enforcements, ProtoWithVarsAndCoeffs *proto, int64_t *offset, std::vector< std::pair< int, int64_t > > *tmp_terms, PresolveContextT *context)
bool LoadModelForProbing(PresolveContext *context, Model *local_model)
bool RefIsPositive(int ref)
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
bool ConstraintIsFeasible(const CpModelProto &model, const ConstraintProto &constraint, absl::Span< const int64_t > variable_values)
void ApplyToAllIntervalIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
bool ScaleFloatingPointObjective(const SatParameters ¶ms, SolverLogger *logger, CpModelProto *proto)
bool ScaleAndSetObjective(const SatParameters ¶ms, absl::Span< const std::pair< int, double > > objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
bool ExpressionIsAffine(const LinearExpressionProto &expr)
Checks if the expression is affine or constant.
constexpr int kAffineRelationConstraint
std::vector< int > UsedVariables(const ConstraintProto &ct)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
Returns the sorted list of interval used by a constraint.
std::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
bool PossibleIntegerOverflow(const CpModelProto &model, absl::Span< const int > vars, absl::Span< const int64_t > coeffs, int64_t offset)
constexpr int kObjectiveConstraint
We use some special constraint index in our variable <-> constraint graph.
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
void ApplyToAllLiteralIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
void AddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
void CreateValidModelWithSingleConstraint(const ConstraintProto &ct, const PresolveContext *context, std::vector< int > *variable_mapping, CpModelProto *mini_model)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
void ApplyToAllVariableIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
bool LoadModelForPresolve(const CpModelProto &model_proto, SatParameters params, PresolveContext *context, Model *local_model, absl::string_view name_for_logging)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
In SWIG mode, we don't want anything besides these top-level includes.
bool AtMinOrMaxInt64(int64_t x)
Checks if x is equal to the min or the max value of an int64_t.
int64_t CapAdd(int64_t x, int64_t y)
std::string ProtobufShortDebugString(const P &message)
int64_t CapProd(int64_t x, int64_t y)
std::string ProtobufDebugString(const P &message)
absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
ABSL_FLAG(bool, cp_model_debug_postsolve, false, "DEBUG ONLY. When set to true, the mapping_model.proto will contains " "file:line of the code that created that constraint. This is helpful " "for debugging postsolve")
#define SOLVER_LOG(logger,...)