26#include "absl/base/attributes.h"
27#include "absl/container/btree_map.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/flags/flag.h"
31#include "absl/log/check.h"
32#include "absl/meta/type_traits.h"
33#include "absl/numeric/int128.h"
34#include "absl/strings/str_cat.h"
35#include "absl/types/span.h"
39#include "ortools/sat/cp_model.pb.h"
46#include "ortools/sat/sat_parameters.pb.h"
57 "DEBUG ONLY. When set to true, the mappin_model.proto will contains "
58 "file:line of the code that created that constraint. This is helpful "
59 "for debugging postsolve");
65 return context->GetLiteralRepresentative(ref_);
81 absl::Span<
const std::pair<int, int64_t>> definition) {
86 if (hint_is_loaded_) {
87 int64_t new_value = 0;
88 for (
const auto [
var, coeff] : definition) {
90 CHECK_LE(
var, hint_.size());
91 if (!hint_has_value_[
var])
return new_var;
92 new_value += coeff * hint_[
var];
94 hint_has_value_[new_var] =
true;
95 hint_[new_var] = new_value;
104 if (hint_is_loaded_) {
105 bool all_have_hint =
true;
106 for (
const int literal : clause) {
108 if (!hint_has_value_[
var]) {
109 all_have_hint =
false;
113 if (hint_[
var] == 1) {
114 hint_has_value_[new_var] =
true;
119 if (hint_[
var] == 0) {
120 hint_has_value_[new_var] =
true;
129 if (all_have_hint && !hint_has_value_[new_var]) {
130 hint_has_value_[new_var] =
true;
138 if (!true_literal_is_defined_) {
139 true_literal_is_defined_ =
true;
142 return true_literal_;
151 ct->add_enforcement_literal(
a);
152 ct->mutable_bool_and()->add_literals(
b);
157 ConstraintProto*
const imply =
working_model->add_constraints();
161 imply->mutable_enforcement_literal()->Resize(1,
b);
162 LinearConstraintProto* mutable_linear = imply->mutable_linear();
163 mutable_linear->mutable_vars()->Resize(1,
x);
164 mutable_linear->mutable_coeffs()->Resize(1, 1);
180 return domains[
var].Min() >= 0 && domains[
var].Max() <= 1;
186 return domains[
lit].Min() == 1;
195 return domains[
lit].Max() == 0;
221 int64_t result = expr.offset();
222 for (
int i = 0;
i < expr.vars_size(); ++
i) {
223 const int64_t coeff = expr.coeffs(
i);
225 result += coeff *
MinOf(expr.vars(
i));
227 result += coeff *
MaxOf(expr.vars(
i));
234 int64_t result = expr.offset();
235 for (
int i = 0;
i < expr.vars_size(); ++
i) {
236 const int64_t coeff = expr.coeffs(
i);
238 result += coeff *
MaxOf(expr.vars(
i));
240 result += coeff *
MinOf(expr.vars(
i));
247 for (
int i = 0;
i < expr.vars_size(); ++
i) {
248 if (expr.coeffs(
i) != 0 && !
IsFixed(expr.vars(
i)))
return false;
254 int64_t result = expr.offset();
255 for (
int i = 0;
i < expr.vars_size(); ++
i) {
256 if (expr.coeffs(
i) == 0)
continue;
263 const LinearExpressionProto& expr)
const {
264 Domain result(expr.offset());
265 for (
int i = 0;
i < expr.vars_size(); ++
i) {
273 const LinearExpressionProto& expr)
const {
274 if (expr.vars().size() != 1)
return false;
279 const LinearExpressionProto& expr)
const {
280 const int ref = expr.vars(0);
285 const LinearExpressionProto& expr)
const {
286 return expr.offset() == 0 && expr.vars_size() == 1 && expr.coeffs(0) == 1;
291 if (expr.vars_size() != 1)
return false;
292 const int ref = expr.vars(0);
296 if (expr.offset() == 0 && expr.coeffs(0) == 1 &&
RefIsPositive(ref)) {
300 if (expr.offset() == 1 && expr.coeffs(0) == -1 &&
RefIsPositive(ref)) {
304 if (expr.offset() == 1 && expr.coeffs(0) == 1 && !
RefIsPositive(ref)) {
314 if (!
proto.enforcement_literal().empty())
return false;
323 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
"..",
329 return absl::StrCat(
"interval_", ct_ref,
"(lit=",
literal,
", ",
333 return absl::StrCat(
"interval_", ct_ref,
"(lit=",
literal,
", ",
338 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
341 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
348 const IntervalConstraintProto&
interval =
354 const IntervalConstraintProto&
interval =
360 const IntervalConstraintProto&
interval =
366 const IntervalConstraintProto&
interval =
372 const IntervalConstraintProto&
interval =
378 const IntervalConstraintProto&
interval =
390 return var_to_constraints_[
var].size() == 1;
400 return var_to_constraints_[
var].size() == 2 &&
419 return var_to_constraints_[
PositiveRef(ref)].empty();
431 if (
IsFixed(ref))
return false;
432 if (!removed_variables_.contains(
PositiveRef(ref)))
return false;
433 if (!var_to_constraints_[
PositiveRef(ref)].empty()) {
435 " was removed, yet it appears in some constraints!");
438 for (
const int c : var_to_constraints_[
PositiveRef(ref)]) {
452 if (var_to_num_linear1_[
var] == 0)
return false;
453 return var_to_num_linear1_[
var] == var_to_constraints_[
var].size() ||
455 var_to_num_linear1_[
var] + 1 == var_to_constraints_[
var].
size());
461 if (var_to_num_linear1_[
var] == 0)
return false;
463 return var_to_num_linear1_[
var] + 1 == var_to_constraints_[
var].size();
469 result = domains[ref];
480 return domains[ref].Contains(
value);
484 int64_t
value)
const {
485 CHECK_LE(expr.vars_size(), 1);
489 if ((
value - expr.offset()) % expr.coeffs(0) != 0)
return false;
494 int ref,
const Domain& domain,
bool* domain_modified) {
499 if (domains[
var].IsIncludedIn(domain)) {
502 domains[
var] = domains[
var].IntersectionWith(domain);
505 if (domains[
var].IsIncludedIn(temp)) {
508 domains[
var] = domains[
var].IntersectionWith(temp);
511 if (domain_modified !=
nullptr) {
512 *domain_modified =
true;
515 if (domains[
var].IsEmpty()) {
517 absl::StrCat(
"var #", ref,
" as empty domain after intersecting with ",
522 if (!domains[
var].Contains(hint_[
var])) {
523 LOG(FATAL) <<
"Hint with value " << hint_[
var]
524 <<
" infeasible when changing domain of " <<
var <<
" to "
540 const LinearExpressionProto& expr,
const Domain& domain,
541 bool* domain_modified) {
542 if (expr.vars().empty()) {
543 if (domain.
Contains(expr.offset())) {
548 " as empty domain after intersecting with ", domain.
ToString()));
551 if (expr.vars().size() == 1) {
574 if (
ct.constraint_case() ==
575 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
578 for (
const int literal :
ct.enforcement_literal()) {
586 bool contains_one_free_literal =
false;
587 for (
const int literal :
ct.enforcement_literal()) {
591 return contains_one_free_literal;
597 const bool is_todo =
name.size() >= 4 &&
name.substr(0, 4) ==
"TODO";
602 stats_by_rule_name_[
name] += num_times;
606void PresolveContext::UpdateLinear1Usage(
const ConstraintProto&
ct,
int c) {
607 const int old_var = constraint_to_linear1_var_[c];
609 var_to_num_linear1_[old_var]--;
610 DCHECK_GE(var_to_num_linear1_[old_var], 0);
612 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
613 ct.linear().vars().size() == 1) {
615 constraint_to_linear1_var_[c] =
var;
616 var_to_num_linear1_[
var]++;
618 constraint_to_linear1_var_[c] = -1;
622void PresolveContext::MaybeResizeIntervalData() {
624 const int num_constraints = constraint_to_vars_.size();
625 if (constraint_to_intervals_.size() != num_constraints) {
626 constraint_to_intervals_.resize(num_constraints);
627 interval_usage_.resize(num_constraints);
631void PresolveContext::AddVariableUsage(
int c) {
635 for (
const int v : constraint_to_vars_[c]) {
636 DCHECK_LT(v, var_to_constraints_.size());
638 var_to_constraints_[v].insert(c);
642 if (!used_interval.empty()) {
643 MaybeResizeIntervalData();
644 constraint_to_intervals_[
c].swap(used_interval);
645 for (
const int i : constraint_to_intervals_[c]) interval_usage_[
i]++;
648 UpdateLinear1Usage(
ct, c);
654 LOG(FATAL) <<
"Hint infeasible for constraint #" <<
c <<
" : "
655 <<
ct.ShortDebugString();
660void PresolveContext::EraseFromVarToConstraint(
int var,
int c) {
661 var_to_constraints_[
var].erase(c);
662 if (var_to_constraints_[
var].
size() <= 3) {
668 if (is_unsat_)
return;
669 DCHECK_EQ(constraint_to_vars_.size(),
working_model->constraints_size());
674 if (c < constraint_to_intervals_.size() || !used_interval.empty()) {
675 MaybeResizeIntervalData();
676 for (
const int i : constraint_to_intervals_[c]) interval_usage_[
i]--;
677 constraint_to_intervals_[c].swap(used_interval);
678 for (
const int i : constraint_to_intervals_[c]) interval_usage_[
i]++;
684 const absl::Span<const int> old_usage = constraint_to_vars_[c];
685 const int old_size = old_usage.size();
687 for (
const int var : new_usage) {
689 while (
i < old_size && old_usage[
i] <
var) {
690 EraseFromVarToConstraint(old_usage[
i], c);
693 if (
i < old_size && old_usage[
i] ==
var) {
696 var_to_constraints_[
var].insert(c);
699 for (;
i < old_size; ++
i) {
700 EraseFromVarToConstraint(old_usage[
i], c);
702 constraint_to_vars_[c].swap(new_usage);
704 UpdateLinear1Usage(
ct, c);
710 LOG(FATAL) <<
"Hint infeasible for constraint #" << c <<
" : "
711 <<
ct.ShortDebugString();
717 return constraint_to_vars_.size() ==
working_model->constraints_size();
721 if (is_unsat_)
return;
722 const int old_size = constraint_to_vars_.size();
724 DCHECK_LE(old_size, new_size);
725 constraint_to_vars_.resize(new_size);
726 constraint_to_linear1_var_.resize(new_size, -1);
727 for (
int c = old_size; c < new_size; ++c) {
734 if (is_unsat_)
return true;
735 if (var_to_constraints_.size() !=
working_model->variables_size()) {
736 LOG(INFO) <<
"Wrong var_to_constraints_ size!";
739 if (constraint_to_vars_.size() !=
working_model->constraints_size()) {
740 LOG(INFO) <<
"Wrong constraint_to_vars size!";
743 std::vector<int> linear1_count(var_to_constraints_.size(), 0);
744 for (
int c = 0; c < constraint_to_vars_.size(); ++c) {
747 LOG(INFO) <<
"Wrong variables usage for constraint: \n"
749 <<
" old_size: " << constraint_to_vars_[c].size();
752 if (
ct.constraint_case() == ConstraintProto::kLinear &&
753 ct.linear().vars().size() == 1) {
755 if (constraint_to_linear1_var_[c] !=
PositiveRef(
ct.linear().vars(0))) {
756 LOG(INFO) <<
"Wrong variables for linear1: \n"
758 <<
" saved_var: " << constraint_to_linear1_var_[c];
763 int num_in_objective = 0;
764 for (
int v = 0; v < var_to_constraints_.size(); ++v) {
765 if (linear1_count[v] != var_to_num_linear1_[v]) {
766 LOG(INFO) <<
"Variable " << v <<
" has wrong linear1 count!"
767 <<
" stored: " << var_to_num_linear1_[v]
768 <<
" actual: " << linear1_count[v];
773 if (!objective_map_.contains(v)) {
774 LOG(INFO) <<
"Variable " << v
775 <<
" is marked as part of the objective but isn't.";
780 if (num_in_objective != objective_map_.size()) {
781 LOG(INFO) <<
"Not all variables are marked as part of the objective";
798bool PresolveContext::AddRelation(
int x,
int y, int64_t c, int64_t o,
802 if (std::abs(c) != 1)
return repo->
TryAdd(
x,
y, c, o);
817 const int64_t m_x = std::max(std::abs(
MinOf(rep_x)), std::abs(
MaxOf(rep_x)));
818 const int64_t m_y = std::max(std::abs(
MinOf(rep_y)), std::abs(
MaxOf(rep_y)));
819 bool allow_rep_x = m_x < m_y;
820 bool allow_rep_y = m_y < m_x;
827 if (allow_rep_x && allow_rep_y) {
837 return repo->
TryAdd(
x,
y, c, o, allow_rep_x, allow_rep_y);
864 .AdditionWith(
Domain(-offset))
865 .InverseMultiplicationBy(coeff))) {
870 DomainOf(rep).MultiplicationBy(coeff).AdditionWith(
Domain(offset)))) {
878 for (
auto& ref_map : var_to_constraints_) {
891 CHECK_EQ(var_to_constraints_[
var].
size(), 1);
905 if (affine_relations_.
ClassSize(rep) == 1) {
928 const auto& objective =
working_model->floating_point_objective();
929 std::vector<std::pair<int, double>> terms;
930 for (
int i = 0;
i < objective.vars_size(); ++
i) {
932 terms.push_back({objective.vars(
i), objective.coeffs(
i)});
934 const double offset = objective.offset();
935 const bool maximize = objective.maximize();
945 int64_t mod, int64_t rhs) {
949 const int64_t gcd = std::gcd(coeff, mod);
951 if (rhs % gcd != 0) {
953 absl::StrCat(
"Infeasible ", coeff,
" * X = ", rhs,
" % ", mod));
961 if (std::abs(mod) == 1)
return true;
979 "Empty domain in CanonicalizeAffineVariable()");
995 const int64_t min_value = new_domain.
Min();
1010 bool debug_no_recursion) {
1012 if (is_unsat_)
return false;
1014 if (hint_is_loaded_) {
1017 if (!hint_has_value_[var_y] && hint_has_value_[var_x]) {
1018 hint_has_value_[var_y] =
true;
1021 hint_[var_y] = (hint_[var_x] * x_mult - offset) / coeff * y_mult;
1022 if (hint_[var_y] * coeff * y_mult + offset != hint_[var_x] * x_mult) {
1026 "Warning: hint didn't satisfy affine relation and was corrected");
1036 if (vx != vy * coeff + offset) {
1037 LOG(FATAL) <<
"Affine relation incompatible with hint: " << vx
1038 <<
" != " << vy <<
" * " << coeff <<
" + " << offset;
1050 if (lhs % std::abs(coeff) != 0) {
1083 const int64_t unique_value = -
b /
a;
1104 int64_t
b = -coeff * ry.
coeff;
1120 if (std::abs(
a) > 1 && std::abs(
b) > 1) {
1135 bool negate =
false;
1136 if (std::abs(
a) == 1) {
1142 CHECK_EQ(std::abs(
b), 1);
1172 if (std::abs(o) > std::max(std::abs(
MinOf(
x)), std::abs(
MaxOf(
x)))) {
1175 CHECK(!debug_no_recursion);
1181 CHECK(AddRelation(
x,
y, c, o, &affine_relations_));
1208 if (is_unsat_)
return false;
1217 if (ref_a == ref_b)
return true;
1251 DCHECK_NE(positive_possible, negative_possible);
1284 DCHECK_GE(new_size, domains.size());
1285 if (domains.size() == new_size)
return;
1289 var_to_constraints_.resize(new_size);
1290 var_to_num_linear1_.resize(new_size);
1294 for (
int i = domains.size();
i < new_size; ++
i) {
1297 if (domains.back().IsEmpty()) {
1304 hint_.resize(new_size, 0);
1305 hint_has_value_.resize(new_size,
false);
1309 CHECK(!hint_is_loaded_);
1310 hint_is_loaded_ =
true;
1313 const int num_terms = hint_proto.vars().size();
1314 for (
int i = 0;
i < num_terms; ++
i) {
1315 const int var = hint_proto.vars(
i);
1317 if (
var < hint_.size()) {
1318 hint_has_value_[
var] =
true;
1319 hint_[
var] = hint_proto.values(
i);
1328 const int64_t var_min =
MinOf(
var);
1329 const int64_t var_max =
MaxOf(
var);
1331 if (is_unsat_)
return;
1333 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1336 auto min_it = var_map.find(var_min);
1337 if (min_it != var_map.end()) {
1338 const int old_var =
PositiveRef(min_it->second.Get(
this));
1339 if (removed_variables_.contains(old_var)) {
1340 var_map.erase(min_it);
1341 min_it = var_map.end();
1346 auto max_it = var_map.find(var_max);
1347 if (max_it != var_map.end()) {
1348 const int old_var =
PositiveRef(max_it->second.Get(
this));
1349 if (removed_variables_.contains(old_var)) {
1350 var_map.erase(max_it);
1351 max_it = var_map.end();
1358 if (min_it != var_map.end() && max_it != var_map.end()) {
1359 min_literal = min_it->second.Get(
this);
1360 max_literal = max_it->second.Get(
this);
1361 if (min_literal !=
NegatedRef(max_literal)) {
1364 if (is_unsat_)
return;
1369 }
else if (min_it != var_map.end() && max_it == var_map.end()) {
1371 min_literal = min_it->second.Get(
this);
1374 }
else if (min_it == var_map.end() && max_it != var_map.end()) {
1376 max_literal = max_it->second.Get(
this);
1403 var_max - var_min, var_min);
1406 var_min - var_max, var_max);
1411void PresolveContext::InsertVarValueEncodingInternal(
int literal,
int var,
1413 bool add_constraints) {
1417 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1423 const auto [it, inserted] =
1426 const int previous_literal = it->second.Get(
this);
1435 if (
literal != previous_literal) {
1437 "variables: merge equivalent var value encoding literals");
1449 VLOG(2) <<
"Insert lit(" <<
literal <<
") <=> var(" <<
var
1450 <<
") == " <<
value;
1453 if (add_constraints) {
1461bool PresolveContext::InsertHalfVarValueEncoding(
int literal,
int var,
1462 int64_t
value,
bool imply_eq) {
1463 if (is_unsat_)
return false;
1473 if (!direct_set.insert(
literal).second)
return false;
1475 VLOG(2) <<
"Collect lit(" <<
literal <<
") implies var(" <<
var
1476 << (imply_eq ?
") == " :
") != ") <<
value;
1483 for (
const int other : other_set) {
1488 InsertVarValueEncodingInternal(imply_eq_literal,
var,
value,
1496bool PresolveContext::CanonicalizeEncoding(
int* ref, int64_t*
value) {
1498 if ((*
value - r.offset) % r.coeff != 0)
return false;
1499 *ref = r.representative;
1512 if (hint_is_loaded_) {
1515 if (!hint_has_value_[bool_var] && hint_has_value_[
var]) {
1516 const int64_t bool_value = hint_[
var] ==
value ? 1 : 0;
1517 hint_has_value_[bool_var] =
true;
1535 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1543 if (!CanonicalizeEncoding(&ref, &
value))
return false;
1544 const absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[ref];
1545 const auto it = var_map.find(
value);
1546 if (it != var_map.end()) {
1549 *
literal = it->second.Get(
this);
1558 const int64_t
size = domains[
var].Size();
1559 if (
size <= 2)
return true;
1560 const auto& it = encoding_.find(
var);
1561 return it == encoding_.end() ? false :
size <= it->second.size();
1565 CHECK_LE(expr.vars_size(), 1);
1566 if (
IsFixed(expr))
return true;
1575 const int var = ref;
1578 if (!domains[
var].Contains(
value)) {
1583 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1584 auto it = var_map.find(
value);
1585 if (it != var_map.end()) {
1586 const int lit = it->second.Get(
this);
1590 var_map.erase(
value);
1597 if (domains[
var].Size() == 1) {
1600 return true_literal;
1604 const int64_t var_min =
MinOf(
var);
1605 const int64_t var_max =
MaxOf(
var);
1606 if (domains[
var].Size() == 2) {
1608 const int64_t other_value =
value == var_min ? var_max : var_min;
1609 auto other_it = var_map.find(other_value);
1610 if (other_it != var_map.end()) {
1615 var_map.erase(other_value);
1624 if (var_min == 0 && var_max == 1) {
1643 const LinearExpressionProto& expr, int64_t
value) {
1644 DCHECK_LE(expr.vars_size(), 1);
1653 if ((
value - expr.offset()) % expr.coeffs(0) != 0) {
1658 (
value - expr.offset()) / expr.coeffs(0));
1665 objective_proto_is_up_to_date_ =
false;
1667 objective_offset_ = obj.offset();
1668 objective_scaling_factor_ = obj.scaling_factor();
1669 if (objective_scaling_factor_ == 0.0) {
1670 objective_scaling_factor_ = 1.0;
1673 objective_integer_before_offset_ = obj.integer_before_offset();
1674 objective_integer_after_offset_ = obj.integer_after_offset();
1675 objective_integer_scaling_factor_ = obj.integer_scaling_factor();
1676 if (objective_integer_scaling_factor_ == 0) {
1677 objective_integer_scaling_factor_ = 1;
1680 if (!obj.domain().empty()) {
1683 objective_domain_is_constraining_ =
true;
1686 objective_domain_is_constraining_ =
false;
1693 objective_overflow_detection_ = std::abs(objective_integer_before_offset_);
1695 objective_map_.clear();
1696 for (
int i = 0;
i < obj.vars_size(); ++
i) {
1697 const int ref = obj.vars(
i);
1698 const int64_t var_max_magnitude =
1699 std::max(std::abs(
MinOf(ref)), std::abs(
MaxOf(ref)));
1703 if (var_max_magnitude == 0)
continue;
1705 const int64_t coeff = obj.coeffs(
i);
1706 objective_overflow_detection_ += var_max_magnitude * std::abs(coeff);
1710 if (objective_map_[
var] == 0) {
1719 const auto it = objective_map_.find(
var);
1720 if (it == objective_map_.end())
return true;
1721 const int64_t coeff = it->second;
1728 var_to_constraints_[
var].
size() == 1 &&
1758 if (new_coeff == 0) {
1771 objective_proto_is_up_to_date_ =
false;
1777 tmp_entries_.clear();
1778 for (
const auto& entry : objective_map_) {
1779 tmp_entries_.push_back(entry);
1785 for (
const auto& entry : tmp_entries_) {
1791 Domain implied_domain(0);
1795 tmp_entries_.clear();
1796 for (
const auto& entry : objective_map_) {
1797 tmp_entries_.push_back(entry);
1799 std::sort(tmp_entries_.begin(), tmp_entries_.end());
1800 for (
const auto& entry : tmp_entries_) {
1801 const int var = entry.first;
1802 const int64_t coeff = entry.second;
1814 if (simplify_domain) {
1821 for (
auto& entry : objective_map_) {
1822 entry.second /= gcd;
1825 if (objective_domain_.
IsEmpty()) {
1829 objective_offset_ /=
static_cast<double>(gcd);
1830 objective_scaling_factor_ *=
static_cast<double>(gcd);
1833 absl::int128 offset = absl::int128(objective_integer_before_offset_) *
1834 absl::int128(objective_integer_scaling_factor_) +
1835 absl::int128(objective_integer_after_offset_);
1837 if (objective_domain_.
IsFixed()) {
1841 objective_integer_scaling_factor_ = 1;
1843 absl::int128(gcd - 1) * absl::int128(objective_domain_.
FixedValue());
1845 objective_integer_scaling_factor_ *= gcd;
1848 objective_integer_before_offset_ =
static_cast<int64_t
>(
1849 offset / absl::int128(objective_integer_scaling_factor_));
1850 objective_integer_after_offset_ =
static_cast<int64_t
>(
1851 offset % absl::int128(objective_integer_scaling_factor_));
1858 if (objective_domain_.
IsEmpty()) {
1865 objective_domain_is_constraining_ =
1868 objective_domain_.
Max()))
1874 CHECK_EQ(objective_map_.size(), 1);
1875 const int var = objective_map_.begin()->first;
1876 const int64_t coeff = objective_map_.begin()->second;
1885 objective_proto_is_up_to_date_ =
false;
1887 objective_domain_is_constraining_ =
false;
1892 objective_proto_is_up_to_date_ =
false;
1894 objective_map_.erase(
var);
1900 objective_proto_is_up_to_date_ =
false;
1901 int64_t& map_ref = objective_map_[
var];
1911 objective_proto_is_up_to_date_ =
false;
1913 int64_t& map_ref = objective_map_[
var];
1928 objective_proto_is_up_to_date_ =
false;
1929 const int64_t temp =
CapAdd(objective_integer_before_offset_,
delta);
1930 if (temp == std::numeric_limits<int64_t>::min())
return false;
1931 if (temp == std::numeric_limits<int64_t>::max())
return false;
1932 objective_integer_before_offset_ = temp;
1935 objective_offset_ +=
static_cast<double>(
delta);
1941 int var_in_equality, int64_t coeff_in_equality,
1942 const ConstraintProto& equality) {
1943 objective_proto_is_up_to_date_ =
false;
1944 CHECK(equality.enforcement_literal().empty());
1949 const int64_t coeff_in_objective = objective_map_.at(var_in_equality);
1950 CHECK_NE(coeff_in_equality, 0);
1951 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1953 const int64_t multiplier = coeff_in_objective / coeff_in_equality;
1957 for (
int i = 0;
i < equality.linear().vars().
size(); ++
i) {
1958 int var = equality.linear().vars(
i);
1960 int64_t coeff = equality.linear().coeffs(
i);
1964 const int64_t new_value =
1966 objective_overflow_detection_ -
1967 std::abs(coeff_in_equality) *
1968 std::max(std::abs(
MinOf(var_in_equality)),
1969 std::abs(
MaxOf(var_in_equality))));
1970 if (new_value == std::numeric_limits<int64_t>::max())
return false;
1971 objective_overflow_detection_ = new_value;
1975 DCHECK_EQ(offset.
Min(), offset.
Max());
1985 for (
int i = 0;
i < equality.linear().vars().
size(); ++
i) {
1986 int var = equality.linear().vars(
i);
1987 int64_t coeff = equality.linear().coeffs(
i);
1992 if (
var == var_in_equality)
continue;
1994 int64_t& map_ref = objective_map_[
var];
1995 map_ref -= coeff * multiplier;
2009 objective_domain_is_constraining_ =
true;
2011 if (objective_domain_.
IsEmpty()) {
2018 absl::Span<const int> exactly_one) {
2019 if (objective_map_.empty())
return false;
2020 if (exactly_one.empty())
return false;
2022 int64_t min_coeff = std::numeric_limits<int64_t>::max();
2023 for (
const int ref : exactly_one) {
2024 const auto it = objective_map_.find(
PositiveRef(ref));
2025 if (it == objective_map_.end())
return false;
2027 const int64_t coeff = it->second;
2029 min_coeff = std::min(min_coeff, coeff);
2032 min_coeff = std::min(min_coeff, -coeff);
2041 if (shift == 0)
return true;
2049 int64_t new_sum = 0;
2050 for (
const int ref : exactly_one) {
2053 sum =
CapAdd(sum, std::abs(obj));
2055 const int64_t new_obj =
RefIsPositive(ref) ? obj - shift : obj + shift;
2056 new_sum =
CapAdd(new_sum, std::abs(new_obj));
2059 if (new_sum > sum) {
2060 const int64_t new_value =
2061 CapAdd(objective_overflow_detection_, new_sum - sum);
2063 objective_overflow_detection_ = new_value;
2066 int64_t offset = shift;
2067 objective_proto_is_up_to_date_ =
false;
2068 for (
const int ref : exactly_one) {
2072 int64_t& map_ref = objective_map_[
var];
2103 if (!objective_domain_is_constraining_) {
2105 Domain(std::numeric_limits<int64_t>::min(), objective_domain_.
Max());
2112 if (objective_proto_is_up_to_date_)
return;
2113 objective_proto_is_up_to_date_ =
true;
2117 tmp_entries_.clear();
2118 tmp_entries_.reserve(objective_map_.size());
2119 for (
const auto& entry : objective_map_) {
2120 tmp_entries_.push_back(entry);
2122 std::sort(tmp_entries_.begin(), tmp_entries_.end(),
2123 [](
const std::pair<int, int64_t>&
a,
2124 const std::pair<int, int64_t>&
b) { return a.first < b.first; });
2126 CpObjectiveProto* mutable_obj =
working_model->mutable_objective();
2127 mutable_obj->set_offset(objective_offset_);
2128 mutable_obj->set_scaling_factor(objective_scaling_factor_);
2129 mutable_obj->set_integer_before_offset(objective_integer_before_offset_);
2130 mutable_obj->set_integer_after_offset(objective_integer_after_offset_);
2131 if (objective_integer_scaling_factor_ == 1) {
2132 mutable_obj->set_integer_scaling_factor(0);
2134 mutable_obj->set_integer_scaling_factor(objective_integer_scaling_factor_);
2137 mutable_obj->clear_vars();
2138 mutable_obj->clear_coeffs();
2139 for (
const auto& entry : tmp_entries_) {
2140 mutable_obj->add_vars(entry.first);
2141 mutable_obj->add_coeffs(entry.second);
2152 const LinearExpressionProto& time_i,
const LinearExpressionProto& time_j,
2153 int active_i,
int active_j) {
2159 const std::tuple<int, int64_t, int, int64_t, int64_t, int, int> key =
2161 const auto& it = reified_precedences_cache_.find(key);
2162 if (it != reified_precedences_cache_.end())
return it->second;
2165 reified_precedences_cache_[key] = result;
2168 ConstraintProto*
const lesseq =
working_model->add_constraints();
2169 lesseq->add_enforcement_literal(result);
2171 lesseq->mutable_linear()->add_vars(time_i.vars(0));
2172 lesseq->mutable_linear()->add_coeffs(-time_i.coeffs(0));
2175 lesseq->mutable_linear()->add_vars(time_j.vars(0));
2176 lesseq->mutable_linear()->add_coeffs(time_j.coeffs(0));
2179 const int64_t offset =
2182 lesseq->mutable_linear()->add_domain(offset);
2183 lesseq->mutable_linear()->add_domain(std::numeric_limits<int64_t>::max());
2195 ConstraintProto*
const greater =
working_model->add_constraints();
2197 greater->mutable_linear()->add_vars(time_i.vars(0));
2198 greater->mutable_linear()->add_coeffs(-time_i.coeffs(0));
2201 greater->mutable_linear()->add_vars(time_j.vars(0));
2202 greater->mutable_linear()->add_coeffs(time_j.coeffs(0));
2204 greater->mutable_linear()->add_domain(std::numeric_limits<int64_t>::min());
2205 greater->mutable_linear()->add_domain(offset - 1);
2207 greater->add_enforcement_literal(
NegatedRef(result));
2209 greater->add_enforcement_literal(active_i);
2212 greater->add_enforcement_literal(active_j);
2222 const auto& rev_it = reified_precedences_cache_.find(
2224 if (rev_it != reified_precedences_cache_.end()) {
2225 auto*
const bool_or =
working_model->add_constraints()->mutable_bool_or();
2226 bool_or->add_literals(result);
2227 bool_or->add_literals(rev_it->second);
2239std::tuple<int, int64_t, int, int64_t, int64_t, int, int>
2241 const LinearExpressionProto& time_j,
2242 int active_i,
int active_j) {
2244 IsFixed(time_i) ? std::numeric_limits<int>::min() : time_i.vars(0);
2245 const int64_t coeff_i =
IsFixed(time_i) ? 0 : time_i.coeffs(0);
2247 IsFixed(time_j) ? std::numeric_limits<int>::min() : time_j.vars(0);
2248 const int64_t coeff_j =
IsFixed(time_j) ? 0 : time_j.coeffs(0);
2249 const int64_t offset =
2254 if (active_j < active_i) std::swap(active_i, active_j);
2255 return std::make_tuple(var_i, coeff_i, var_j, coeff_j, offset, active_i,
2260 reified_precedences_cache_.clear();
2267 " affine relations were detected.");
2268 absl::btree_map<std::string, int> sorted_rules(stats_by_rule_name_.begin(),
2269 stats_by_rule_name_.end());
2270 for (
const auto& entry : sorted_rules) {
2271 if (entry.second == 1) {
2272 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied 1 time.");
2274 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied ",
2292 if (
context->ModelIsUnsat())
return false;
2295 context->WriteVariableDomainsToProto();
2296 const CpModelProto& model_proto = *(
context->working_model);
2299 auto* local_param = local_model->
GetOrCreate<SatParameters>();
2300 *local_param =
context->params();
2301 local_param->set_use_implied_bounds(
false);
2317 if (sat_solver->ModelIsUnsat()) {
2318 return context->NotifyThatModelIsUnsat(
"Initial loading for probing");
2320 for (
const ConstraintProto&
ct : model_proto.constraints()) {
2321 if (mapping->ConstraintIsAlreadyLoaded(&
ct))
continue;
2323 if (sat_solver->ModelIsUnsat()) {
2324 return context->NotifyThatModelIsUnsat(
2325 absl::StrCat(
"after loading constraint during probing ",
2329 encoder->AddAllImplicationsBetweenAssociatedLiterals();
2330 if (sat_solver->ModelIsUnsat())
return false;
2331 if (!sat_solver->Propagate()) {
2332 return context->NotifyThatModelIsUnsat(
2333 "during probing initial propagation");
2339template <
typename ProtoWithVarsAndCoeffs>
2341 absl::Span<const int> enforcements, ProtoWithVarsAndCoeffs*
proto,
2342 int64_t* offset, std::vector<std::pair<int, int64_t>>* tmp_terms,
2349 int64_t sum_of_fixed_terms = 0;
2350 bool remapped =
false;
2351 const int old_size =
proto->vars().size();
2352 DCHECK_EQ(old_size,
proto->coeffs().size());
2353 for (
int i = 0;
i < old_size; ++
i) {
2361 const int ref =
proto->vars(
i);
2363 const int64_t coeff =
2365 if (coeff == 0)
continue;
2368 sum_of_fixed_terms += coeff *
context->FixedValue(
var);
2375 sum_of_fixed_terms += coeff * r.
offset;
2379 new_coeff = coeff * r.
coeff;
2384 bool removed =
false;
2385 for (
const int enf : enforcements) {
2389 sum_of_fixed_terms += new_coeff;
2398 context->UpdateRuleStats(
"linear: enforcement literal in expression");
2402 tmp_terms->push_back({new_var, new_coeff});
2404 proto->clear_vars();
2405 proto->clear_coeffs();
2406 std::sort(tmp_terms->begin(), tmp_terms->end());
2407 int current_var = 0;
2408 int64_t current_coeff = 0;
2409 for (
const auto& entry : *tmp_terms) {
2411 if (entry.first == current_var) {
2412 current_coeff += entry.second;
2414 if (current_coeff != 0) {
2415 proto->add_vars(current_var);
2416 proto->add_coeffs(current_coeff);
2418 current_var = entry.first;
2419 current_coeff = entry.second;
2422 if (current_coeff != 0) {
2423 proto->add_vars(current_var);
2424 proto->add_coeffs(current_coeff);
2427 context->UpdateRuleStats(
"linear: remapped using affine relations");
2429 if (
proto->vars().size() < old_size) {
2430 context->UpdateRuleStats(
"linear: fixed or dup variables");
2432 *offset = sum_of_fixed_terms;
2433 return remapped ||
proto->vars().size() < old_size;
2439 ct->enforcement_literal(),
ct->mutable_linear(), &offset, &tmp_terms_,
2444 ct->mutable_linear());
2450 absl::Span<const int> enforcements, LinearExpressionProto* expr) {
2453 enforcements, expr, &offset, &tmp_terms_,
this);
2454 expr->set_offset(expr->offset() + offset);
2462 if (absl::GetFlag(FLAGS_cp_model_debug_postsolve)) {
2463 new_ct->set_name(absl::StrCat(
"#", c,
" ",
file,
":",
line));
2469 const ConstraintProto& base_ct, absl::string_view
file,
int line) {
2473 if (absl::GetFlag(FLAGS_cp_model_debug_postsolve)) {
2474 new_ct->set_name(absl::StrCat(
"#c", c,
" ",
file,
":",
line));
bool TryAdd(int x, int y, int64_t coeff, int64_t offset)
Relation Get(int x) const
void IgnoreFromClassSize(int x)
int ClassSize(int x) const
Returns the size of the class of x.
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) 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
static int64_t GCD64(int64_t x, int64_t y)
bool LoggingIsEnabled() const
Returns true iff logging is enabled.
void Set(IntegerType index)
void Resize(IntegerType size)
void DisableImplicationBetweenLiteral()
void Register(T *non_owned_class)
Domain DomainOf(int ref) 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 StoreBooleanEqualityRelation(int ref_a, int ref_b)
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.
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
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)
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.
int NewIntVarWithDefinition(const Domain &domain, absl::Span< const std::pair< int, int64_t > > definition)
int64_t ObjectiveCoeff(int var) const
void ReadObjectiveFromProto()
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(absl::string_view message="")
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
void AddImplyInDomain(int b, int x, const Domain &domain)
b => x in [lb, ub].
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)
void LogInfo()
Logs stats to the logger.
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.
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.
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
ABSL_MUST_USE_RESULT bool ScaleFloatingPointObjective()
bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset, bool debug_no_recursion=false)
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 PropagateAffineRelation(int ref)
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)
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 keep_all_feasible_solutions
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)
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
bool VariableWithCostIsUnique(int ref) const
CpModelProto * mapping_model
bool DomainContains(int ref, int64_t value) const
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
CpModelProto proto
The output proto.
const std::string name
A name for logging purposes.
GurobiMPCallbackContext * context
bool LoadModelForProbing(PresolveContext *context, Model *local_model)
bool RefIsPositive(int ref)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
bool ScaleAndSetObjective(const SatParameters ¶ms, const std::vector< std::pair< int, double > > &objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
bool ConstraintIsFeasible(const CpModelProto &model, const ConstraintProto &constraint, absl::Span< const int64_t > variable_values)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
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.
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.
bool LoadConstraint(const ConstraintProto &ct, Model *m)
bool CanonicalizeLinearExpressionInternal(absl::Span< const int > enforcements, ProtoWithVarsAndCoeffs *proto, int64_t *offset, std::vector< std::pair< int, int64_t > > *tmp_terms, PresolveContext *context)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
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 mappin_model.proto will contains " "file:line of the code that created that constraint. This is helpful " "for debugging postsolve")
#define SOLVER_LOG(logger,...)