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/meta/type_traits.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/types/span.h"
36 #include "ortools/sat/cp_model.pb.h"
43 #include "ortools/sat/sat_parameters.pb.h"
57 return context->GetLiteralRepresentative(ref_);
74 if (!true_literal_is_defined_) {
75 true_literal_is_defined_ =
true;
86 ct->add_enforcement_literal(
a);
87 ct->mutable_bool_and()->add_literals(
b);
92 ConstraintProto*
const imply =
working_model->add_constraints();
96 imply->mutable_enforcement_literal()->Resize(1,
b);
97 LinearConstraintProto* mutable_linear = imply->mutable_linear();
98 mutable_linear->mutable_vars()->Resize(1, x);
99 mutable_linear->mutable_coeffs()->Resize(1, 1);
115 return domains[
var].Min() >= 0 && domains[
var].Max() <= 1;
121 return domains[lit].Min() == 1;
130 return domains[lit].Max() == 0;
156 int64_t result = expr.offset();
157 for (
int i = 0; i < expr.vars_size(); ++i) {
158 const int64_t coeff = expr.coeffs(i);
160 result += coeff *
MinOf(expr.vars(i));
162 result += coeff *
MaxOf(expr.vars(i));
169 int64_t result = expr.offset();
170 for (
int i = 0; i < expr.vars_size(); ++i) {
171 const int64_t coeff = expr.coeffs(i);
173 result += coeff *
MaxOf(expr.vars(i));
175 result += coeff *
MinOf(expr.vars(i));
182 for (
int i = 0; i < expr.vars_size(); ++i) {
183 if (expr.coeffs(i) != 0 && !
IsFixed(expr.vars(i)))
return false;
189 int64_t result = expr.offset();
190 for (
int i = 0; i < expr.vars_size(); ++i) {
191 if (expr.coeffs(i) == 0)
continue;
192 result += expr.coeffs(i) *
FixedValue(expr.vars(i));
198 const LinearExpressionProto& expr)
const {
199 Domain result(expr.offset());
200 for (
int i = 0; i < expr.vars_size(); ++i) {
208 const LinearExpressionProto& expr)
const {
209 if (expr.vars().size() != 1)
return false;
214 const LinearExpressionProto& expr)
const {
215 const int ref = expr.vars(0);
220 const LinearExpressionProto& expr)
const {
221 return expr.offset() == 0 && expr.vars_size() == 1 && expr.coeffs(0) == 1;
226 if (expr.vars_size() != 1)
return false;
227 const int ref = expr.vars(0);
231 if (expr.offset() == 0 && expr.coeffs(0) == 1 &&
RefIsPositive(ref)) {
235 if (expr.offset() == 1 && expr.coeffs(0) == -1 &&
RefIsPositive(ref)) {
239 if (expr.offset() == 1 && expr.coeffs(0) == 1 && !
RefIsPositive(ref)) {
249 if (!
proto.enforcement_literal().empty())
return false;
258 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
"..",
264 return absl::StrCat(
"interval_", ct_ref,
"(lit=",
literal,
", ",
268 return absl::StrCat(
"interval_", ct_ref,
"(lit=",
literal,
", ",
273 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
276 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
283 const IntervalConstraintProto&
interval =
289 const IntervalConstraintProto&
interval =
295 const IntervalConstraintProto&
interval =
301 const IntervalConstraintProto&
interval =
307 const IntervalConstraintProto&
interval =
313 const IntervalConstraintProto&
interval =
332 var_to_constraints_[
var].size() == 2;
350 return var_to_constraints_[
PositiveRef(ref)].empty();
362 if (
IsFixed(ref))
return false;
363 if (!removed_variables_.contains(
PositiveRef(ref)))
return false;
364 if (!var_to_constraints_[
PositiveRef(ref)].empty()) {
366 " was removed, yet it appears in some constraints!");
369 for (
const int c : var_to_constraints_[
PositiveRef(ref)]) {
371 logger_,
"constraint #", c,
" : ",
372 c >= 0 ?
working_model->constraints(c).ShortDebugString() :
"");
382 return var_to_num_linear1_[
var] == var_to_constraints_[
var].size() ||
384 var_to_num_linear1_[
var] + 1 == var_to_constraints_[
var].size());
390 result = domains[ref];
401 return domains[ref].Contains(
value);
405 int64_t
value)
const {
406 CHECK_LE(expr.vars_size(), 1);
410 if ((
value - expr.offset()) % expr.coeffs(0) != 0)
return false;
415 int ref,
const Domain& domain,
bool* domain_modified) {
420 if (domains[
var].IsIncludedIn(domain)) {
423 domains[
var] = domains[
var].IntersectionWith(domain);
426 if (domains[
var].IsIncludedIn(temp)) {
429 domains[
var] = domains[
var].IntersectionWith(temp);
432 if (domain_modified !=
nullptr) {
433 *domain_modified =
true;
436 if (domains[
var].IsEmpty()) {
452 const LinearExpressionProto& expr,
const Domain& domain,
453 bool* domain_modified) {
454 if (expr.vars().empty()) {
455 if (domain.
Contains(expr.offset())) {
462 if (expr.vars().size() == 1) {
485 if (
ct.constraint_case() ==
486 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
489 for (
const int literal :
ct.enforcement_literal()) {
497 bool contains_one_free_literal =
false;
498 for (
const int literal :
ct.enforcement_literal()) {
502 return contains_one_free_literal;
508 const bool is_todo =
name.size() >= 4 &&
name.substr(0, 4) ==
"TODO";
513 stats_by_rule_name_[
name] += num_times;
517 void PresolveContext::UpdateLinear1Usage(
const ConstraintProto&
ct,
int c) {
518 const int old_var = constraint_to_linear1_var_[c];
520 var_to_num_linear1_[old_var]--;
521 DCHECK_GE(var_to_num_linear1_[old_var], 0);
523 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
524 ct.linear().vars().size() == 1) {
526 constraint_to_linear1_var_[c] =
var;
527 var_to_num_linear1_[
var]++;
529 constraint_to_linear1_var_[c] = -1;
533 void PresolveContext::AddVariableUsage(
int c) {
537 for (
const int v : constraint_to_vars_[c]) {
538 DCHECK_LT(v, var_to_constraints_.size());
540 var_to_constraints_[v].insert(c);
542 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
543 UpdateLinear1Usage(
ct, c);
546 void PresolveContext::EraseFromVarToConstraint(
int var,
int c) {
547 var_to_constraints_[
var].erase(c);
548 if (var_to_constraints_[
var].size() <= 3) {
554 if (is_unsat_)
return;
555 DCHECK_EQ(constraint_to_vars_.size(),
working_model->constraints_size());
559 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
561 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
566 const std::vector<int>& old_usage = constraint_to_vars_[c];
567 const int old_size = old_usage.size();
569 for (
const int var : tmp_new_usage_) {
571 while (i < old_size && old_usage[i] <
var) {
572 EraseFromVarToConstraint(old_usage[i], c);
575 if (i < old_size && old_usage[i] ==
var) {
578 var_to_constraints_[
var].insert(c);
581 for (; i < old_size; ++i) {
582 EraseFromVarToConstraint(old_usage[i], c);
584 constraint_to_vars_[c] = tmp_new_usage_;
586 UpdateLinear1Usage(
ct, c);
590 return constraint_to_vars_.size() ==
working_model->constraints_size();
594 if (is_unsat_)
return;
595 const int old_size = constraint_to_vars_.size();
597 CHECK_LE(old_size, new_size);
598 constraint_to_vars_.resize(new_size);
599 constraint_to_linear1_var_.resize(new_size, -1);
600 constraint_to_intervals_.resize(new_size);
601 interval_usage_.resize(new_size);
602 for (
int c = old_size; c < new_size; ++c) {
609 if (is_unsat_)
return true;
610 if (var_to_constraints_.size() !=
working_model->variables_size()) {
611 LOG(INFO) <<
"Wrong var_to_constraints_ size!";
614 if (constraint_to_vars_.size() !=
working_model->constraints_size()) {
615 LOG(INFO) <<
"Wrong constraint_to_vars size!";
618 std::vector<int> linear1_count(var_to_constraints_.size(), 0);
619 for (
int c = 0; c < constraint_to_vars_.size(); ++c) {
622 LOG(INFO) <<
"Wrong variables usage for constraint: \n"
624 <<
" old_size: " << constraint_to_vars_[c].size();
627 if (
ct.constraint_case() == ConstraintProto::kLinear &&
628 ct.linear().vars().size() == 1) {
630 if (constraint_to_linear1_var_[c] !=
PositiveRef(
ct.linear().vars(0))) {
631 LOG(INFO) <<
"Wrong variables for linear1: \n"
633 <<
" saved_var: " << constraint_to_linear1_var_[c];
638 int num_in_objective = 0;
639 for (
int v = 0; v < var_to_constraints_.size(); ++v) {
640 if (linear1_count[v] != var_to_num_linear1_[v]) {
641 LOG(INFO) <<
"Variable " << v <<
" has wrong linear1 count!"
642 <<
" stored: " << var_to_num_linear1_[v]
643 <<
" actual: " << linear1_count[v];
648 if (!objective_map_.contains(v)) {
649 LOG(INFO) <<
"Variable " << v
650 <<
" is marked as part of the objective but isn't.";
655 if (num_in_objective != objective_map_.size()) {
656 LOG(INFO) <<
"Not all variables are marked as part of the objective";
673 bool PresolveContext::AddRelation(
int x,
int y, int64_t c, int64_t o,
677 if (std::abs(c) != 1)
return repo->
TryAdd(x, y, c, o);
694 bool allow_rep_x = m_x < m_y;
695 bool allow_rep_y = m_y < m_x;
702 if (allow_rep_x && allow_rep_y) {
712 return repo->
TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
737 .AdditionWith(
Domain(-offset))
738 .InverseMultiplicationBy(coeff))) {
743 DomainOf(rep).MultiplicationBy(coeff).AdditionWith(
Domain(offset)))) {
751 for (
auto& ref_map : var_to_constraints_) {
764 CHECK_EQ(var_to_constraints_[
var].size(), 1);
778 if (affine_relations_.
ClassSize(rep) == 1) {
801 const auto& objective =
working_model->floating_point_objective();
802 std::vector<std::pair<int, double>> terms;
803 for (
int i = 0; i < objective.vars_size(); ++i) {
805 terms.push_back({objective.vars(i), objective.coeffs(i)});
807 const double offset = objective.offset();
808 const bool maximize = objective.maximize();
818 int64_t mod, int64_t rhs) {
822 const int64_t gcd = std::gcd(coeff, mod);
824 if (rhs % gcd != 0) {
826 absl::StrCat(
"Infeasible ", coeff,
" * X = ", rhs,
" % ", mod));
834 if (std::abs(mod) == 1)
return true;
852 "Empty domain in CanonicalizeAffineVariable()");
868 const int64_t min_value = new_domain.
Min();
883 bool debug_no_recursion) {
885 if (is_unsat_)
return false;
895 if (lhs % std::abs(coeff) != 0) {
928 const int64_t unique_value = -
b /
a;
949 int64_t
b = coeff * ry.
coeff;
965 if (std::abs(
a) > 1 && std::abs(
b) > 1) {
981 if (std::abs(
a) == 1) {
987 CHECK_EQ(std::abs(
b), 1);
1002 y,
DomainOf(x).AdditionWith(
Domain(-o)).InverseMultiplicationBy(c))) {
1007 DomainOf(y).ContinuousMultiplicationBy(c).AdditionWith(
Domain(o)))) {
1020 CHECK(!debug_no_recursion);
1026 CHECK(AddRelation(x, y, c, o, &affine_relations_));
1053 if (is_unsat_)
return false;
1062 if (ref_a == ref_b)
return true;
1076 const auto insert_status = abs_relations_.insert(
1078 if (!insert_status.second) {
1080 const int candidate = insert_status.first->second.Get();
1081 if (removed_variables_.contains(candidate)) {
1091 auto it = abs_relations_.find(target_ref);
1092 if (it == abs_relations_.end())
return false;
1099 const int candidate =
PositiveRef(it->second.Get());
1100 if (removed_variables_.contains(candidate)) {
1101 abs_relations_.erase(it);
1130 DCHECK_NE(positive_possible, negative_possible);
1162 for (
int i = domains.size(); i < working_model->variables_size(); ++i) {
1164 if (domains.back().IsEmpty()) {
1171 var_to_constraints_.resize(domains.size());
1172 var_to_num_linear1_.resize(domains.size());
1178 const int64_t var_min =
MinOf(
var);
1179 const int64_t var_max =
MaxOf(
var);
1181 if (is_unsat_)
return;
1183 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1186 auto min_it = var_map.find(var_min);
1187 if (min_it != var_map.end()) {
1188 const int old_var =
PositiveRef(min_it->second.Get(
this));
1189 if (removed_variables_.contains(old_var)) {
1190 var_map.erase(min_it);
1191 min_it = var_map.end();
1196 auto max_it = var_map.find(var_max);
1197 if (max_it != var_map.end()) {
1198 const int old_var =
PositiveRef(max_it->second.Get(
this));
1199 if (removed_variables_.contains(old_var)) {
1200 var_map.erase(max_it);
1201 max_it = var_map.end();
1208 if (min_it != var_map.end() && max_it != var_map.end()) {
1209 min_literal = min_it->second.Get(
this);
1210 max_literal = max_it->second.Get(
this);
1211 if (min_literal !=
NegatedRef(max_literal)) {
1214 if (is_unsat_)
return;
1219 }
else if (min_it != var_map.end() && max_it == var_map.end()) {
1221 min_literal = min_it->second.Get(
this);
1224 }
else if (min_it == var_map.end() && max_it != var_map.end()) {
1226 max_literal = max_it->second.Get(
this);
1253 var_max - var_min, var_min);
1256 var_min - var_max, var_max);
1261 void PresolveContext::InsertVarValueEncodingInternal(
int literal,
int var,
1263 bool add_constraints) {
1267 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1273 const auto [it, inserted] =
1276 const int previous_literal = it->second.Get(
this);
1285 if (
literal != previous_literal) {
1287 "variables: merge equivalent var value encoding literals");
1300 <<
") == " <<
value;
1303 if (add_constraints) {
1311 bool PresolveContext::InsertHalfVarValueEncoding(
int literal,
int var,
1312 int64_t
value,
bool imply_eq) {
1313 if (is_unsat_)
return false;
1320 if (!direct_set.insert(
literal).second)
return false;
1323 << (imply_eq ?
") == " :
") != ") <<
value;
1330 for (
const int other : other_set) {
1335 InsertVarValueEncodingInternal(imply_eq_literal,
var,
value,
1343 bool PresolveContext::CanonicalizeEncoding(
int* ref, int64_t*
value) {
1345 if ((*
value - r.offset) % r.coeff != 0)
return false;
1346 *ref = r.representative;
1353 if (!CanonicalizeEncoding(&ref, &
value)) {
1357 InsertVarValueEncodingInternal(
literal, ref,
value,
true);
1363 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1370 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1378 if (!CanonicalizeEncoding(&ref, &
value))
return false;
1379 const absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[ref];
1380 const auto it = var_map.find(
value);
1381 if (it != var_map.end()) {
1384 *
literal = it->second.Get(
this);
1393 const int64_t size = domains[
var].Size();
1394 if (size <= 2)
return true;
1395 const auto& it = encoding_.find(
var);
1396 return it == encoding_.end() ? false : size <= it->second.size();
1400 CHECK_LE(expr.vars_size(), 1);
1401 if (
IsFixed(expr))
return true;
1410 const int var = ref;
1413 if (!domains[
var].Contains(
value)) {
1418 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1419 auto it = var_map.find(
value);
1420 if (it != var_map.end()) {
1421 const int lit = it->second.Get(
this);
1425 var_map.erase(
value);
1432 if (domains[
var].Size() == 1) {
1435 return true_literal;
1439 const int64_t var_min =
MinOf(
var);
1440 const int64_t var_max =
MaxOf(
var);
1441 if (domains[
var].Size() == 2) {
1443 const int64_t other_value =
value == var_min ? var_max : var_min;
1444 auto other_it = var_map.find(other_value);
1445 if (other_it != var_map.end()) {
1450 var_map.erase(other_value);
1459 if (var_min == 0 && var_max == 1) {
1478 const LinearExpressionProto& expr, int64_t
value) {
1479 DCHECK_LE(expr.vars_size(), 1);
1488 if ((
value - expr.offset()) % expr.coeffs(0) != 0) {
1493 (
value - expr.offset()) / expr.coeffs(0));
1499 objective_offset_ = obj.offset();
1500 objective_scaling_factor_ = obj.scaling_factor();
1501 if (objective_scaling_factor_ == 0.0) {
1502 objective_scaling_factor_ = 1.0;
1505 objective_integer_before_offset_ = obj.integer_before_offset();
1506 objective_integer_after_offset_ = obj.integer_after_offset();
1507 objective_integer_scaling_factor_ = obj.integer_scaling_factor();
1508 if (objective_integer_scaling_factor_ == 0) {
1509 objective_integer_scaling_factor_ = 1;
1512 if (!obj.domain().empty()) {
1515 objective_domain_is_constraining_ =
true;
1518 objective_domain_is_constraining_ =
false;
1525 objective_overflow_detection_ = std::abs(objective_integer_before_offset_);
1527 objective_map_.clear();
1528 for (
int i = 0; i < obj.vars_size(); ++i) {
1529 const int ref = obj.vars(i);
1530 const int64_t var_max_magnitude =
1535 if (var_max_magnitude == 0)
continue;
1537 const int64_t coeff = obj.coeffs(i);
1538 objective_overflow_detection_ += var_max_magnitude * std::abs(coeff);
1542 if (objective_map_[
var] == 0) {
1551 const auto it = objective_map_.find(
var);
1552 if (it == objective_map_.end())
return true;
1553 const int64_t coeff = it->second;
1560 var_to_constraints_[
var].size() == 1 &&
1583 objective_map_.erase(
var);
1591 if (new_coeff == 0) {
1608 tmp_entries_.clear();
1609 for (
const auto& entry : objective_map_) {
1610 tmp_entries_.push_back(entry);
1616 for (
const auto& entry : tmp_entries_) {
1620 Domain implied_domain(0);
1624 tmp_entries_.clear();
1625 for (
const auto& entry : objective_map_) {
1626 tmp_entries_.push_back(entry);
1628 std::sort(tmp_entries_.begin(), tmp_entries_.end());
1629 for (
const auto& entry : tmp_entries_) {
1630 const int var = entry.first;
1631 const int64_t coeff = entry.second;
1643 if (simplify_domain) {
1650 for (
auto& entry : objective_map_) {
1651 entry.second /= gcd;
1654 if (objective_domain_.
IsEmpty())
return false;
1656 objective_offset_ /=
static_cast<double>(gcd);
1657 objective_scaling_factor_ *=
static_cast<double>(gcd);
1660 const absl::int128 offset =
1661 absl::int128(objective_integer_before_offset_) *
1662 absl::int128(objective_integer_scaling_factor_) +
1663 absl::int128(objective_integer_after_offset_);
1669 objective_integer_scaling_factor_ = 1;
1671 objective_integer_scaling_factor_ *= gcd;
1674 objective_integer_before_offset_ =
static_cast<int64_t
>(
1675 offset / absl::int128(objective_integer_scaling_factor_));
1676 objective_integer_after_offset_ =
static_cast<int64_t
>(
1677 offset % absl::int128(objective_integer_scaling_factor_));
1680 if (objective_domain_.
IsEmpty())
return false;
1685 objective_domain_is_constraining_ =
1688 objective_domain_.
Max()))
1689 .IsIncludedIn(objective_domain_);
1694 CHECK_EQ(objective_map_.size(), 1);
1695 const int var = objective_map_.begin()->first;
1696 const int64_t coeff = objective_map_.begin()->second;
1706 objective_domain_is_constraining_ =
false;
1712 objective_map_.erase(
var);
1718 int64_t& map_ref = objective_map_[
var];
1729 int64_t& map_ref = objective_map_[
var];
1744 const int64_t temp =
CapAdd(objective_integer_before_offset_,
delta);
1747 objective_integer_before_offset_ = temp;
1750 objective_offset_ +=
static_cast<double>(
delta);
1756 int var_in_equality, int64_t coeff_in_equality,
1757 const ConstraintProto& equality) {
1758 CHECK(equality.enforcement_literal().empty());
1763 const int64_t coeff_in_objective = objective_map_.at(var_in_equality);
1764 CHECK_NE(coeff_in_equality, 0);
1765 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1767 const int64_t multiplier = coeff_in_objective / coeff_in_equality;
1771 for (
int i = 0; i < equality.linear().vars().size(); ++i) {
1772 int var = equality.linear().vars(i);
1774 int64_t coeff = equality.linear().coeffs(i);
1778 const int64_t new_value =
1780 objective_overflow_detection_ -
1781 std::abs(coeff_in_equality) *
1783 std::abs(
MaxOf(var_in_equality))));
1785 objective_overflow_detection_ = new_value;
1789 DCHECK_EQ(offset.
Min(), offset.
Max());
1799 for (
int i = 0; i < equality.linear().vars().size(); ++i) {
1800 int var = equality.linear().vars(i);
1801 int64_t coeff = equality.linear().coeffs(i);
1806 if (
var == var_in_equality)
continue;
1808 int64_t& map_ref = objective_map_[
var];
1809 map_ref -= coeff * multiplier;
1823 objective_domain_is_constraining_ =
true;
1825 if (objective_domain_.
IsEmpty()) {
1832 absl::Span<const int> exactly_one) {
1833 if (objective_map_.empty())
return false;
1834 if (exactly_one.empty())
return false;
1837 for (
const int ref : exactly_one) {
1838 const auto it = objective_map_.find(
PositiveRef(ref));
1839 if (it == objective_map_.end())
return false;
1841 const int64_t coeff = it->second;
1843 min_coeff =
std::min(min_coeff, coeff);
1846 min_coeff =
std::min(min_coeff, -coeff);
1855 if (shift == 0)
return true;
1863 int64_t new_sum = 0;
1864 for (
const int ref : exactly_one) {
1867 sum =
CapAdd(sum, std::abs(obj));
1869 const int64_t new_obj =
RefIsPositive(ref) ? obj - shift : obj + shift;
1870 new_sum =
CapAdd(new_sum, std::abs(new_obj));
1873 if (new_sum > sum) {
1874 const int64_t new_value =
1875 CapAdd(objective_overflow_detection_, new_sum - sum);
1877 objective_overflow_detection_ = new_value;
1880 int64_t offset = shift;
1881 for (
const int ref : exactly_one) {
1885 int64_t& map_ref = objective_map_[
var];
1914 std::vector<std::pair<int, int64_t>> entries;
1915 for (
const auto& entry : objective_map_) {
1916 entries.push_back(entry);
1918 std::sort(entries.begin(), entries.end());
1920 CpObjectiveProto* mutable_obj =
working_model->mutable_objective();
1921 mutable_obj->set_offset(objective_offset_);
1922 mutable_obj->set_scaling_factor(objective_scaling_factor_);
1923 mutable_obj->set_integer_before_offset(objective_integer_before_offset_);
1924 mutable_obj->set_integer_after_offset(objective_integer_after_offset_);
1925 if (objective_integer_scaling_factor_ == 1) {
1926 mutable_obj->set_integer_scaling_factor(0);
1928 mutable_obj->set_integer_scaling_factor(objective_integer_scaling_factor_);
1931 mutable_obj->clear_vars();
1932 mutable_obj->clear_coeffs();
1933 for (
const auto& entry : entries) {
1934 mutable_obj->add_vars(entry.first);
1935 mutable_obj->add_coeffs(entry.second);
1946 const LinearExpressionProto& time_i,
const LinearExpressionProto& time_j,
1947 int active_i,
int active_j) {
1953 const std::tuple<int, int64_t, int, int64_t, int64_t, int, int> key =
1955 const auto& it = reified_precedences_cache_.find(key);
1956 if (it != reified_precedences_cache_.end())
return it->second;
1959 reified_precedences_cache_[key] = result;
1962 ConstraintProto*
const lesseq =
working_model->add_constraints();
1963 lesseq->add_enforcement_literal(result);
1965 lesseq->mutable_linear()->add_vars(time_i.vars(0));
1966 lesseq->mutable_linear()->add_coeffs(-time_i.coeffs(0));
1969 lesseq->mutable_linear()->add_vars(time_j.vars(0));
1970 lesseq->mutable_linear()->add_coeffs(time_j.coeffs(0));
1973 const int64_t offset =
1976 lesseq->mutable_linear()->add_domain(offset);
1986 ConstraintProto*
const greater =
working_model->add_constraints();
1988 greater->mutable_linear()->add_vars(time_i.vars(0));
1989 greater->mutable_linear()->add_coeffs(-time_i.coeffs(0));
1992 greater->mutable_linear()->add_vars(time_j.vars(0));
1993 greater->mutable_linear()->add_coeffs(time_j.coeffs(0));
1996 greater->mutable_linear()->add_domain(offset - 1);
1999 greater->add_enforcement_literal(
NegatedRef(result));
2001 greater->add_enforcement_literal(active_i);
2004 greater->add_enforcement_literal(active_j);
2012 const auto& rev_it = reified_precedences_cache_.find(
2014 if (rev_it != reified_precedences_cache_.end()) {
2015 auto*
const bool_or =
working_model->add_constraints()->mutable_bool_or();
2016 bool_or->add_literals(result);
2017 bool_or->add_literals(rev_it->second);
2025 std::tuple<int, int64_t, int, int64_t, int64_t, int, int>
2027 const LinearExpressionProto& time_j,
2028 int active_i,
int active_j) {
2031 const int64_t coeff_i =
IsFixed(time_i) ? 0 : time_i.coeffs(0);
2034 const int64_t coeff_j =
IsFixed(time_j) ? 0 : time_j.coeffs(0);
2035 const int64_t offset =
2040 if (active_j < active_i)
std::swap(active_i, active_j);
2041 return std::make_tuple(var_i, coeff_i, var_j, coeff_j, offset, active_i,
2046 reified_precedences_cache_.clear();
2053 " affine relations were detected.");
2054 absl::btree_map<std::string, int> sorted_rules(stats_by_rule_name_.begin(),
2055 stats_by_rule_name_.end());
2056 for (
const auto& entry : sorted_rules) {
2057 if (entry.second == 1) {
2058 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied 1 time.");
2060 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied ",
2061 entry.second,
" times.");
2067 if (
context->ModelIsUnsat())
return false;
2070 context->WriteVariableDomainsToProto();
2088 auto* local_param = local_model->
GetOrCreate<SatParameters>();
2089 *local_param =
context->params();
2090 local_param->set_use_implied_bounds(
false);
2096 encoder->DisableImplicationBetweenLiteral();
2106 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2107 if (mapping->ConstraintIsAlreadyLoaded(&
ct))
continue;
2109 if (sat_solver->ModelIsUnsat()) {
2110 return context->NotifyThatModelIsUnsat(absl::StrCat(
2111 "after loading constraint during probing ",
ct.ShortDebugString()));
2114 encoder->AddAllImplicationsBetweenAssociatedLiterals();
2115 if (!sat_solver->Propagate()) {
2116 return context->NotifyThatModelIsUnsat(
2117 "during probing initial propagation");
2124 const IntervalConstraintProto&
interval =
2126 const auto [it, inserted] =
2127 interval_representative_.insert({
interval.SerializeAsString(),
index});
2128 if (!inserted &&
index != it->second) {
2130 if (
working_model->constraints(it->second).SerializeAsString() !=
void IgnoreFromClassSize(int x)
bool TryAdd(int x, int y, int64_t coeff, int64_t offset)
int ClassSize(int x) const
Relation Get(int x) const
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 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)
bool LoggingIsEnabled() const
void Set(IntegerType index)
void Resize(IntegerType size)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Class that owns everything related to a particular optimization model.
void Register(T *non_owned_class)
Register a non-owned class that will be "singleton" in the model.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
int64_t MaxOf(int ref) const
bool CanonicalizeAffineVariable(int ref, int64_t coeff, int64_t mod, int64_t rhs)
bool ExpressionIsALiteral(const LinearExpressionProto &expr, int *literal=nullptr) const
bool IsFullyEncoded(int ref) const
bool StoreAbsRelation(int target_ref, int ref)
bool VariableWithCostIsUnique(int ref) const
ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality)
bool ConstraintIsInactive(int ct_index) const
bool ConstraintVariableUsageIsConsistent()
void AddImplication(int a, int b)
void AddToObjective(int var, int64_t value)
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
bool ConstraintVariableGraphIsUpToDate() const
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
bool RecomputeSingletonObjectiveDomain()
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)
int64_t StartMin(int ct_ref) const
int64_t ObjectiveCoeff(int var) const
bool VariableWithCostIsUniqueAndRemovable(int ref) const
void WriteObjectiveToProto() const
bool ExpressionIsSingleVariable(const LinearExpressionProto &expr) const
int GetLiteralRepresentative(int ref) const
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
int GetOrCreateAffineValueEncoding(const LinearExpressionProto &expr, int64_t value)
ABSL_MUST_USE_RESULT bool ScaleFloatingPointObjective()
ABSL_MUST_USE_RESULT bool CanonicalizeOneObjectiveVariable(int var)
int GetOrCreateVarValueEncoding(int ref, int64_t value)
void UpdateNewConstraintsVariableUsage()
bool VariableIsUniqueAndRemovable(int ref) const
void RemoveVariableFromAffineRelation(int var)
void RemoveVariableFromObjective(int ref)
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
bool PropagateAffineRelation(int ref)
int64_t EndMin(int ct_ref) const
SparseBitset< int > modified_domains
Domain DomainOf(int ref) const
int64_t num_presolve_operations
void InitializeNewDomains()
std::string AffineRelationDebugString(int ref) const
int NewIntVar(const Domain &domain)
void MarkVariableAsRemoved(int ref)
bool InsertVarValueEncoding(int literal, int ref, int64_t value)
void WriteVariableDomainsToProto() const
bool AddToObjectiveOffset(int64_t delta)
bool DomainIsEmpty(int ref) const
int GetIntervalRepresentative(int index)
void CanonicalizeVariable(int ref)
void CanonicalizeDomainOfSizeTwo(int var)
SparseBitset< int > var_with_reduced_small_degree
int64_t StartMax(int ct_ref) const
int64_t FixedValue(int ref) const
bool LiteralIsTrue(int lit) 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)
CpModelProto * working_model
bool HasVarValueEncoding(int ref, int64_t value, int *literal=nullptr)
bool IntervalIsConstant(int ct_ref) const
bool DomainContains(int ref, int64_t value) const
bool LiteralIsFalse(int lit) const
bool ShiftCostInExactlyOne(absl::Span< const int > exactly_one, int64_t shift)
void UpdateRuleStats(const std::string &name, int num_times=1)
void RemoveAllVariablesFromAffineRelationConstraint()
AffineRelation::Relation GetAffineRelation(int ref) const
bool VariableIsNotUsedAnymore(int ref) const
void UpdateConstraintVariableUsage(int c)
bool keep_all_feasible_solutions
void AddLiteralToObjective(int ref, int64_t value)
bool IsFixed(int ref) const
int NumAffineRelations() 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
std::string RefDebugString(int ref) const
int64_t EndMax(int ct_ref) const
bool ConstraintIsOptional(int ct_ref) const
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
int64_t SizeMax(int ct_ref) const
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
void ClearPrecedenceCache()
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
void ReadObjectiveFromProto()
int64_t SizeMin(int ct_ref) const
void AddImplyInDomain(int b, int x, const Domain &domain)
bool CanBeUsedAsLiteral(int ref) const
bool VariableWasRemoved(int ref) const
int64_t MinOf(int ref) const
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
CpModelProto const * model_proto
GurobiMPCallbackContext * context
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
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)
bool ScaleAndSetObjective(const SatParameters ¶ms, const std::vector< std::pair< int, double >> &objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
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.
bool AtMinOrMaxInt64(int64_t x)
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)
#define SOLVER_LOG(logger,...)
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)