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"
37#include "ortools/sat/cp_model.pb.h"
44#include "ortools/sat/sat_parameters.pb.h"
58 return context->GetLiteralRepresentative(ref_);
62 return context->GetVariableRepresentative(ref_);
77 if (!constant_to_ref_.contains(cst)) {
79 IntegerVariableProto*
const var_proto =
working_model->add_variables();
80 var_proto->add_domain(cst);
81 var_proto->add_domain(cst);
84 return constant_to_ref_[cst].Get(
this);
90 ct->add_enforcement_literal(
a);
91 ct->mutable_bool_and()->add_literals(
b);
96 ConstraintProto*
const imply =
working_model->add_constraints();
100 imply->mutable_enforcement_literal()->Resize(1,
b);
101 LinearConstraintProto* mutable_linear = imply->mutable_linear();
102 mutable_linear->mutable_vars()->Resize(1, x);
103 mutable_linear->mutable_coeffs()->Resize(1, 1);
119 return domains[
var].Min() >= 0 && domains[
var].Max() <= 1;
125 return domains[lit].Min() == 1;
134 return domains[lit].Max() == 0;
160 int64_t result = expr.offset();
161 for (
int i = 0; i < expr.vars_size(); ++i) {
162 const int64_t
coeff = expr.coeffs(i);
173 int64_t result = expr.offset();
174 for (
int i = 0; i < expr.vars_size(); ++i) {
175 const int64_t
coeff = expr.coeffs(i);
186 for (
int i = 0; i < expr.vars_size(); ++i) {
187 if (expr.coeffs(i) != 0 && !
IsFixed(expr.vars(i)))
return false;
193 int64_t result = expr.offset();
194 for (
int i = 0; i < expr.vars_size(); ++i) {
195 if (expr.coeffs(i) == 0)
continue;
196 result += expr.coeffs(i) *
FixedValue(expr.vars(i));
202 const LinearExpressionProto& expr)
const {
203 Domain result(expr.offset());
204 for (
int i = 0; i < expr.vars_size(); ++i) {
212 const LinearExpressionProto& expr)
const {
213 if (expr.vars().size() != 1)
return false;
218 const LinearExpressionProto& expr)
const {
219 const int ref = expr.vars(0);
224 const LinearExpressionProto& expr)
const {
225 return expr.offset() == 0 && expr.vars_size() == 1 && expr.coeffs(0) == 1;
230 if (expr.vars_size() != 1)
return false;
231 const int ref = expr.vars(0);
235 if (expr.offset() == 0 && expr.coeffs(0) == 1 &&
RefIsPositive(ref)) {
239 if (expr.offset() == 1 && expr.coeffs(0) == -1 &&
RefIsPositive(ref)) {
243 if (expr.offset() == 1 && expr.coeffs(0) == 1 && !
RefIsPositive(ref)) {
253 if (!
proto.enforcement_literal().empty())
return false;
262 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
"..",
268 return absl::StrCat(
"interval_", ct_ref,
"(lit=",
literal,
", ",
272 return absl::StrCat(
"interval_", ct_ref,
"(lit=",
literal,
", ",
277 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
280 return absl::StrCat(
"interval_", ct_ref,
"(",
StartMin(ct_ref),
" --(",
287 const IntervalConstraintProto&
interval =
293 const IntervalConstraintProto&
interval =
299 const IntervalConstraintProto&
interval =
305 const IntervalConstraintProto&
interval =
311 const IntervalConstraintProto&
interval =
317 const IntervalConstraintProto&
interval =
324bool PresolveContext::VariableIsNotRepresentativeOfEquivalenceClass(
340 return VariableIsNotRepresentativeOfEquivalenceClass(
var) &&
357 return VariableIsNotRepresentativeOfEquivalenceClass(
var) &&
359 var_to_constraints_[
var].size() == 2;
377 return var_to_constraints_[
PositiveRef(ref)].empty();
389 if (
IsFixed(ref))
return false;
390 if (!removed_variables_.contains(
PositiveRef(ref)))
return false;
391 if (!var_to_constraints_[
PositiveRef(ref)].empty()) {
393 " was removed, yet it appears in some constraints!");
396 for (
const int c : var_to_constraints_[
PositiveRef(ref)]) {
398 logger_,
"constraint #", c,
" : ",
399 c >= 0 ?
working_model->constraints(c).ShortDebugString() :
"");
409 return var_to_num_linear1_[
var] == var_to_constraints_[
var].size() ||
411 var_to_num_linear1_[
var] + 1 == var_to_constraints_[
var].size());
417 result = domains[ref];
428 return domains[ref].Contains(
value);
432 int64_t
value)
const {
437 if ((
value - expr.offset()) % expr.coeffs(0) != 0)
return false;
442 int ref,
const Domain& domain,
bool* domain_modified) {
447 if (domains[
var].IsIncludedIn(domain)) {
450 domains[
var] = domains[
var].IntersectionWith(domain);
453 if (domains[
var].IsIncludedIn(temp)) {
456 domains[
var] = domains[
var].IntersectionWith(temp);
459 if (domain_modified !=
nullptr) {
460 *domain_modified =
true;
463 if (domains[
var].IsEmpty()) {
479 const LinearExpressionProto& expr,
const Domain& domain,
480 bool* domain_modified) {
481 if (expr.vars().empty()) {
482 if (domain.
Contains(expr.offset())) {
489 if (expr.vars().size() == 1) {
512 if (
ct.constraint_case() ==
513 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
516 for (
const int literal :
ct.enforcement_literal()) {
524 bool contains_one_free_literal =
false;
525 for (
const int literal :
ct.enforcement_literal()) {
529 return contains_one_free_literal;
536 stats_by_rule_name_[
name] += num_times;
541void PresolveContext::UpdateLinear1Usage(
const ConstraintProto&
ct,
int c) {
542 const int old_var = constraint_to_linear1_var_[c];
544 var_to_num_linear1_[old_var]--;
546 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
547 ct.linear().vars().size() == 1) {
549 constraint_to_linear1_var_[c] =
var;
550 var_to_num_linear1_[
var]++;
554void PresolveContext::AddVariableUsage(
int c) {
558 for (
const int v : constraint_to_vars_[c]) {
560 var_to_constraints_[v].insert(c);
562 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
563 UpdateLinear1Usage(
ct, c);
566void PresolveContext::EraseFromVarToConstraint(
int var,
int c) {
567 var_to_constraints_[
var].erase(c);
568 if (var_to_constraints_[
var].size() <= 2) {
574 if (is_unsat_)
return;
579 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
581 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
586 const std::vector<int>& old_usage = constraint_to_vars_[c];
587 const int old_size = old_usage.size();
589 for (
const int var : tmp_new_usage_) {
591 while (i < old_size && old_usage[i] <
var) {
592 EraseFromVarToConstraint(old_usage[i], c);
595 if (i < old_size && old_usage[i] ==
var) {
598 var_to_constraints_[
var].insert(c);
601 for (; i < old_size; ++i) {
602 EraseFromVarToConstraint(old_usage[i], c);
604 constraint_to_vars_[c] = tmp_new_usage_;
606 UpdateLinear1Usage(
ct, c);
610 return constraint_to_vars_.size() ==
working_model->constraints_size();
614 if (is_unsat_)
return;
615 const int old_size = constraint_to_vars_.size();
618 constraint_to_vars_.resize(new_size);
619 constraint_to_linear1_var_.resize(new_size, -1);
620 constraint_to_intervals_.resize(new_size);
621 interval_usage_.resize(new_size);
622 for (
int c = old_size; c < new_size; ++c) {
629 if (is_unsat_)
return true;
630 if (constraint_to_vars_.size() !=
working_model->constraints_size()) {
631 LOG(
INFO) <<
"Wrong constraint_to_vars size!";
634 for (
int c = 0; c < constraint_to_vars_.size(); ++c) {
635 if (constraint_to_vars_[c] !=
637 LOG(
INFO) <<
"Wrong variables usage for constraint: \n"
639 <<
"old_size: " << constraint_to_vars_[c].size();
643 int num_in_objective = 0;
644 for (
int v = 0; v < var_to_constraints_.size(); ++v) {
647 if (!objective_map_.contains(v)) {
649 <<
" is marked as part of the objective but isn't.";
654 if (num_in_objective != objective_map_.size()) {
655 LOG(
INFO) <<
"Not all variables are marked as part of the objective";
672bool PresolveContext::AddRelation(
int x,
int y, int64_t c, int64_t o,
676 if (std::abs(c) != 1)
return repo->
TryAdd(x, y, c, o);
693 bool allow_rep_x = m_x < m_y;
694 bool allow_rep_y = m_y < m_x;
701 if (allow_rep_x && allow_rep_y) {
711 return repo->
TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
725 if (constant_to_ref_.contains(
min)) {
726 const int rep = constant_to_ref_[
min].Get(
this);
729 AddRelation(
var, rep, 1, 0, &var_equiv_relations_);
766 for (
auto& ref_map : var_to_constraints_) {
794 if (affine_relations_.
ClassSize(rep) == 1 &&
795 var_equiv_relations_.
ClassSize(rep) == 1) {
818 const auto& objective =
working_model->floating_point_objective();
819 std::vector<std::pair<int, double>> terms;
820 for (
int i = 0; i < objective.vars_size(); ++i) {
822 terms.push_back({objective.vars(i), objective.coeffs(i)});
824 const double offset = objective.offset();
825 const bool maximize = objective.maximize();
835 int64_t mod, int64_t rhs) {
839 const int64_t gcd = std::gcd(
coeff, mod);
841 if (rhs % gcd != 0) {
843 absl::StrCat(
"Infeasible ",
coeff,
" * X = ", rhs,
" % ", mod));
851 if (std::abs(mod) == 1)
return true;
869 "Empty domain in CanonicalizeAffineVariable()");
885 const int64_t min_value = new_domain.
Min();
896 bool debug_no_recursion) {
898 if (is_unsat_)
return false;
907 if (lhs % std::abs(
coeff) != 0) {
940 const int64_t unique_value = -
b /
a;
977 if (std::abs(
a) > 1 && std::abs(
b) > 1) {
993 if (std::abs(
a) == 1) {
1014 y,
DomainOf(x).AdditionWith(
Domain(-o)).InverseMultiplicationBy(c))) {
1019 DomainOf(y).ContinuousMultiplicationBy(c).AdditionWith(
Domain(o)))) {
1032 CHECK(!debug_no_recursion);
1038 CHECK(AddRelation(x, y, c, o, &affine_relations_));
1039 if ((c == 1 || c == -1) && o == 0) {
1040 CHECK(AddRelation(x, y, c, o, &var_equiv_relations_));
1069 if (is_unsat_)
return false;
1078 if (ref_a == ref_b)
return true;
1092 const auto insert_status = abs_relations_.insert(
1094 if (!insert_status.second) {
1096 const int candidate = insert_status.first->second.Get(
this);
1097 if (removed_variables_.contains(candidate)) {
1107 auto it = abs_relations_.find(target_ref);
1108 if (it == abs_relations_.end())
return false;
1115 const int candidate =
PositiveRef(it->second.Get(
this));
1116 if (removed_variables_.contains(candidate)) {
1117 abs_relations_.erase(it);
1146 DCHECK_NE(positive_possible, negative_possible);
1189 for (
int i = domains.size(); i < working_model->variables_size(); ++i) {
1191 if (domains.back().IsEmpty()) {
1199 var_to_constraints_.resize(domains.size());
1200 var_to_num_linear1_.resize(domains.size());
1208 const int64_t var_min =
MinOf(
var);
1209 const int64_t var_max =
MaxOf(
var);
1211 if (is_unsat_)
return;
1213 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1216 auto min_it = var_map.find(var_min);
1217 if (min_it != var_map.end()) {
1218 const int old_var =
PositiveRef(min_it->second.Get(
this));
1219 if (removed_variables_.contains(old_var)) {
1220 var_map.erase(min_it);
1221 min_it = var_map.end();
1226 auto max_it = var_map.find(var_max);
1227 if (max_it != var_map.end()) {
1228 const int old_var =
PositiveRef(max_it->second.Get(
this));
1229 if (removed_variables_.contains(old_var)) {
1230 var_map.erase(max_it);
1231 max_it = var_map.end();
1238 if (min_it != var_map.end() && max_it != var_map.end()) {
1239 min_literal = min_it->second.Get(
this);
1240 max_literal = max_it->second.Get(
this);
1241 if (min_literal !=
NegatedRef(max_literal)) {
1244 if (is_unsat_)
return;
1249 }
else if (min_it != var_map.end() && max_it == var_map.end()) {
1251 min_literal = min_it->second.Get(
this);
1254 }
else if (min_it == var_map.end() && max_it != var_map.end()) {
1256 max_literal = max_it->second.Get(
this);
1283 var_max - var_min, var_min);
1286 var_min - var_max, var_max);
1291void PresolveContext::InsertVarValueEncodingInternal(
int literal,
int var,
1293 bool add_constraints) {
1297 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1303 const auto [it, inserted] =
1306 const int previous_literal = it->second.Get(
this);
1315 if (
literal != previous_literal) {
1317 "variables: merge equivalent var value encoding literals");
1330 <<
") == " <<
value;
1333 if (add_constraints) {
1341bool PresolveContext::InsertHalfVarValueEncoding(
int literal,
int var,
1342 int64_t
value,
bool imply_eq) {
1343 if (is_unsat_)
return false;
1350 if (!direct_set.insert(
literal).second)
return false;
1353 << (imply_eq ?
") == " :
") != ") <<
value;
1360 for (
const int other : other_set) {
1365 InsertVarValueEncodingInternal(imply_eq_literal,
var,
value,
1373bool PresolveContext::CanonicalizeEncoding(
int* ref, int64_t*
value) {
1375 if ((*
value - r.offset) % r.coeff != 0)
return false;
1376 *ref = r.representative;
1383 if (!CanonicalizeEncoding(&ref, &
value)) {
1387 InsertVarValueEncodingInternal(
literal, ref,
value,
true);
1393 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1400 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1408 if (!CanonicalizeEncoding(&ref, &
value))
return false;
1409 const absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[ref];
1410 const auto it = var_map.find(
value);
1411 if (it != var_map.end()) {
1413 *
literal = it->second.Get(
this);
1422 const int64_t size = domains[
var].Size();
1423 if (size <= 2)
return true;
1424 const auto& it = encoding_.find(
var);
1425 return it == encoding_.end() ? false : size <= it->second.size();
1430 if (
IsFixed(expr))
return true;
1439 const int var = ref;
1442 if (!domains[
var].Contains(
value)) {
1447 absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[
var];
1448 auto it = var_map.find(
value);
1449 if (it != var_map.end()) {
1450 const int lit = it->second.Get(
this);
1454 var_map.erase(
value);
1461 if (domains[
var].Size() == 1) {
1464 return true_literal;
1468 const int64_t var_min =
MinOf(
var);
1469 const int64_t var_max =
MaxOf(
var);
1470 if (domains[
var].Size() == 2) {
1472 const int64_t other_value =
value == var_min ? var_max : var_min;
1473 auto other_it = var_map.find(other_value);
1474 if (other_it != var_map.end()) {
1479 var_map.erase(other_value);
1488 if (var_min == 0 && var_max == 1) {
1507 const LinearExpressionProto& expr, int64_t
value) {
1517 if ((
value - expr.offset()) % expr.coeffs(0) != 0) {
1522 (
value - expr.offset()) / expr.coeffs(0));
1528 objective_offset_ = obj.offset();
1529 objective_scaling_factor_ = obj.scaling_factor();
1530 if (objective_scaling_factor_ == 0.0) {
1531 objective_scaling_factor_ = 1.0;
1534 objective_integer_offset_ = obj.integer_offset();
1535 objective_integer_scaling_factor_ = obj.integer_scaling_factor();
1536 if (objective_integer_scaling_factor_ == 0) {
1537 objective_integer_scaling_factor_ = 1;
1540 if (!obj.domain().empty()) {
1543 objective_domain_is_constraining_ =
true;
1546 objective_domain_is_constraining_ =
false;
1553 objective_overflow_detection_ = 0;
1555 objective_map_.clear();
1556 for (
int i = 0; i < obj.vars_size(); ++i) {
1557 const int ref = obj.vars(i);
1558 const int64_t var_max_magnitude =
1563 if (var_max_magnitude == 0)
continue;
1565 const int64_t
coeff = obj.coeffs(i);
1566 objective_overflow_detection_ += var_max_magnitude * std::abs(
coeff);
1570 if (objective_map_[
var] == 0) {
1579 const auto it = objective_map_.find(
var);
1580 if (it == objective_map_.end())
return true;
1581 const int64_t
coeff = it->second;
1588 var_to_constraints_[
var].size() == 1 &&
1611 objective_map_.erase(
var);
1619 if (new_coeff == 0) {
1636 tmp_entries_.clear();
1637 for (
const auto& entry : objective_map_) {
1638 tmp_entries_.push_back(entry);
1644 for (
const auto& entry : tmp_entries_) {
1648 Domain implied_domain(0);
1652 tmp_entries_.clear();
1653 for (
const auto& entry : objective_map_) {
1654 tmp_entries_.push_back(entry);
1656 std::sort(tmp_entries_.begin(), tmp_entries_.end());
1657 for (
const auto& entry : tmp_entries_) {
1658 const int var = entry.first;
1659 const int64_t
coeff = entry.second;
1671 if (simplify_domain) {
1678 for (
auto& entry : objective_map_) {
1679 entry.second /= gcd;
1682 objective_offset_ /=
static_cast<double>(gcd);
1683 objective_scaling_factor_ *=
static_cast<double>(gcd);
1684 objective_integer_scaling_factor_ *= gcd;
1687 if (objective_domain_.
IsEmpty())
return false;
1692 objective_domain_is_constraining_ =
1695 objective_domain_.
Max()))
1701 objective_map_.erase(
var);
1706 int64_t& map_ref = objective_map_[
var];
1717 objective_offset_ +=
static_cast<double>(
delta);
1718 objective_integer_offset_ +=
delta * objective_integer_scaling_factor_;
1723 int var_in_equality, int64_t coeff_in_equality,
1724 const ConstraintProto& equality, std::vector<int>* new_vars_in_objective) {
1725 CHECK(equality.enforcement_literal().empty());
1728 if (new_vars_in_objective !=
nullptr) new_vars_in_objective->clear();
1732 const int64_t coeff_in_objective =
1735 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1737 const int64_t multiplier = coeff_in_objective / coeff_in_equality;
1741 for (
int i = 0; i < equality.linear().vars().size(); ++i) {
1742 int var = equality.linear().vars(i);
1744 int64_t
coeff = equality.linear().coeffs(i);
1748 const int64_t new_value =
1750 objective_overflow_detection_ -
1751 std::abs(coeff_in_equality) *
1753 std::abs(
MaxOf(var_in_equality))));
1755 objective_overflow_detection_ = new_value;
1767 int64_t temp =
CapProd(offset.
Min(), objective_integer_scaling_factor_);
1770 temp =
CapAdd(temp, objective_integer_offset_);
1776 for (
int i = 0; i < equality.linear().vars().size(); ++i) {
1777 int var = equality.linear().vars(i);
1778 int64_t
coeff = equality.linear().coeffs(i);
1783 if (
var == var_in_equality)
continue;
1785 int64_t& map_ref = objective_map_[
var];
1786 if (map_ref == 0 && new_vars_in_objective !=
nullptr) {
1787 new_vars_in_objective->push_back(
var);
1789 map_ref -=
coeff * multiplier;
1801 objective_offset_ +=
static_cast<double>(offset.
Min());
1802 objective_integer_offset_ += offset.
Min() * objective_integer_scaling_factor_;
1808 objective_domain_is_constraining_ =
true;
1810 if (objective_domain_.
IsEmpty()) {
1817 absl::Span<const int> exactly_one) {
1818 if (objective_map_.empty())
return false;
1821 for (
const int ref : exactly_one) {
1822 const auto it = objective_map_.find(
PositiveRef(ref));
1823 if (it == objective_map_.end())
return false;
1825 const int64_t
coeff = it->second;
1834 int64_t offset = min_coeff;
1835 for (
const int ref : exactly_one) {
1837 int64_t& map_ref = objective_map_.at(
var);
1839 map_ref -= min_coeff;
1848 map_ref += min_coeff;
1852 offset -= min_coeff;
1858 objective_offset_ += offset;
1859 objective_integer_offset_ += offset * objective_integer_scaling_factor_;
1868 std::vector<std::pair<int, int64_t>> entries;
1869 for (
const auto& entry : objective_map_) {
1870 entries.push_back(entry);
1872 std::sort(entries.begin(), entries.end());
1874 CpObjectiveProto* mutable_obj =
working_model->mutable_objective();
1875 mutable_obj->set_offset(objective_offset_);
1876 mutable_obj->set_scaling_factor(objective_scaling_factor_);
1877 mutable_obj->set_integer_offset(objective_integer_offset_);
1878 if (objective_integer_scaling_factor_ == 1) {
1879 mutable_obj->set_integer_scaling_factor(0);
1881 mutable_obj->set_integer_scaling_factor(objective_integer_scaling_factor_);
1884 mutable_obj->clear_vars();
1885 mutable_obj->clear_coeffs();
1886 for (
const auto& entry : entries) {
1887 mutable_obj->add_vars(entry.first);
1888 mutable_obj->add_coeffs(entry.second);
1899 const LinearExpressionProto& time_i,
const LinearExpressionProto& time_j,
1900 int active_i,
int active_j) {
1906 const std::tuple<int, int64_t, int, int64_t, int64_t, int, int> key =
1908 const auto& it = reified_precedences_cache_.find(key);
1909 if (it != reified_precedences_cache_.end())
return it->second;
1912 reified_precedences_cache_[key] = result;
1915 ConstraintProto*
const lesseq =
working_model->add_constraints();
1916 lesseq->add_enforcement_literal(result);
1918 lesseq->mutable_linear()->add_vars(time_i.vars(0));
1919 lesseq->mutable_linear()->add_coeffs(-time_i.coeffs(0));
1922 lesseq->mutable_linear()->add_vars(time_j.vars(0));
1923 lesseq->mutable_linear()->add_coeffs(time_j.coeffs(0));
1926 const int64_t offset =
1929 lesseq->mutable_linear()->add_domain(offset);
1939 ConstraintProto*
const greater =
working_model->add_constraints();
1941 greater->mutable_linear()->add_vars(time_i.vars(0));
1942 greater->mutable_linear()->add_coeffs(-time_i.coeffs(0));
1945 greater->mutable_linear()->add_vars(time_j.vars(0));
1946 greater->mutable_linear()->add_coeffs(time_j.coeffs(0));
1949 greater->mutable_linear()->add_domain(offset - 1);
1952 greater->add_enforcement_literal(
NegatedRef(result));
1954 greater->add_enforcement_literal(active_i);
1957 greater->add_enforcement_literal(active_j);
1965 const auto& rev_it = reified_precedences_cache_.find(
1967 if (rev_it != reified_precedences_cache_.end()) {
1968 auto*
const bool_or =
working_model->add_constraints()->mutable_bool_or();
1969 bool_or->add_literals(result);
1970 bool_or->add_literals(rev_it->second);
1978std::tuple<int, int64_t, int, int64_t, int64_t, int, int>
1980 const LinearExpressionProto& time_j,
1981 int active_i,
int active_j) {
1984 const int64_t coeff_i =
IsFixed(time_i) ? 0 : time_i.coeffs(0);
1987 const int64_t coeff_j =
IsFixed(time_j) ? 0 : time_j.coeffs(0);
1988 const int64_t offset =
1993 if (active_j < active_i)
std::swap(active_i, active_j);
1994 return std::make_tuple(var_i, coeff_i, var_j, coeff_j, offset, active_i,
1999 reified_precedences_cache_.clear();
2006 " affine relations were detected.");
2007 absl::btree_map<std::string, int> sorted_rules(stats_by_rule_name_.begin(),
2008 stats_by_rule_name_.end());
2009 for (
const auto& entry : sorted_rules) {
2010 if (entry.second == 1) {
2011 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied 1 time.");
2013 SOLVER_LOG(logger_,
" - rule '", entry.first,
"' was applied ",
2014 entry.second,
" times.");
2020 if (
context->ModelIsUnsat())
return false;
2023 context->WriteVariableDomainsToProto();
2041 auto* local_param = local_model->
GetOrCreate<SatParameters>();
2042 *local_param =
context->params();
2043 local_param->set_use_implied_bounds(
false);
2049 encoder->DisableImplicationBetweenLiteral();
2059 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2060 if (mapping->ConstraintIsAlreadyLoaded(&
ct))
continue;
2062 if (sat_solver->IsModelUnsat()) {
2063 return context->NotifyThatModelIsUnsat(absl::StrCat(
2064 "after loading constraint during probing ",
ct.ShortDebugString()));
2067 encoder->AddAllImplicationsBetweenAssociatedLiterals();
2068 if (!sat_solver->Propagate()) {
2069 return context->NotifyThatModelIsUnsat(
2070 "during probing initial propagation");
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
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 IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
Domain ContinuousMultiplicationBy(int64_t coeff) const
Returns a superset of MultiplicationBy() to avoid the explosion in the representation size.
int64_t FixedValue() const
Returns the value of a fixed domain.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
bool IsFixed() const
Returns true iff the domain is reduced to a single value.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t Max() const
Returns the max value of the domain.
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
Advanced usage.
static int64_t GCD64(int64_t x, int64_t y)
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).
bool VariableIsRemovable(int ref) const
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
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)
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
bool ConstraintVariableGraphIsUpToDate() const
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
int GetOrCreateReifiedPrecedenceLiteral(const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
ABSL_MUST_USE_RESULT bool CanonicalizeObjective(bool simplify_domain=true)
bool StoreBooleanEqualityRelation(int ref_a, int ref_b)
int64_t StartMin(int ct_ref) 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()
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
ABSL_MUST_USE_RESULT bool CanonicalizeOneObjectiveVariable(int var)
ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
int GetOrCreateVarValueEncoding(int ref, int64_t value)
void UpdateNewConstraintsVariableUsage()
bool VariableIsUniqueAndRemovable(int ref) const
void RemoveVariableFromAffineRelation(int var)
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()
int GetVariableRepresentative(int ref) const
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 DomainIsEmpty(int ref) const
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
void UpdateRuleStats(const std::string &name, int num_times=1)
void RemoveAllVariablesFromAffineRelationConstraint()
AffineRelation::Relation GetAffineRelation(int ref) const
void RemoveVariableFromObjective(int var)
bool VariableIsNotUsedAnymore(int ref) const
void UpdateConstraintVariableUsage(int c)
bool keep_all_feasible_solutions
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
int GetOrCreateConstantVar(int64_t cst)
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 AddToObjectiveOffset(int64_t delta)
void AddImplyInDomain(int b, int x, const Domain &domain)
bool CanBeUsedAsLiteral(int ref) const
bool VariableWasRemoved(int ref) const
void ExploitFixedDomain(int var)
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
int Get(PresolveContext *context) const
CpModelProto const * model_proto
GurobiMPCallbackContext * context
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
bool ScaleAndSetObjective(const SatParameters ¶ms, const std::vector< std::pair< int, double > > &objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool RefIsPositive(int ref)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
constexpr int kAffineRelationConstraint
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
bool ExpressionIsAffine(const LinearExpressionProto &expr)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
bool LoadModelForProbing(PresolveContext *context, Model *local_model)
constexpr int kObjectiveConstraint
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
int64_t CapProd(int64_t x, int64_t y)
std::string ProtobufDebugString(const P &message)
#define SOLVER_LOG(logger,...)
#define VLOG_IS_ON(verboselevel)