24 return context->GetLiteralRepresentative(ref_);
28 return context->GetVariableRepresentative(ref_);
45 IntegerVariableProto*
const var_proto =
working_model->add_variables();
46 var_proto->add_domain(cst);
47 var_proto->add_domain(cst);
50 return constant_to_ref_[cst].Get(
this);
56 ct->add_enforcement_literal(
a);
57 ct->mutable_bool_and()->add_literals(
b);
62 ConstraintProto*
const imply =
working_model->add_constraints();
66 imply->mutable_enforcement_literal()->Resize(1,
b);
67 LinearConstraintProto* mutable_linear = imply->mutable_linear();
68 mutable_linear->mutable_vars()->Resize(1, x);
69 mutable_linear->mutable_coeffs()->Resize(1, 1);
85 return domains[
var].Min() >= 0 && domains[
var].Max() <= 1;
91 return domains[lit].Min() == 1;
100 return domains[lit].Max() == 0;
119 int64 result = expr.offset();
120 for (
int i = 0; i < expr.vars_size(); ++i) {
121 const int64 coeff = expr.coeffs(i);
123 result += coeff *
MinOf(expr.vars(i));
125 result += coeff *
MaxOf(expr.vars(i));
132 int64 result = expr.offset();
133 for (
int i = 0; i < expr.vars_size(); ++i) {
134 const int64 coeff = expr.coeffs(i);
136 result += coeff *
MaxOf(expr.vars(i));
138 result += coeff *
MinOf(expr.vars(i));
146 bool PresolveContext::VariableIsNotRepresentativeOfEquivalenceClass(
167 return var_to_constraints_[
var].size() == 1 &&
168 VariableIsNotRepresentativeOfEquivalenceClass(
var) &&
178 var_to_constraints_[
var].size() == 2 &&
179 VariableIsNotRepresentativeOfEquivalenceClass(
var);
186 return var_to_constraints_[
PositiveRef(ref)].empty();
198 if (
IsFixed(ref))
return false;
199 if (!removed_variables_.contains(
PositiveRef(ref)))
return false;
200 if (!var_to_constraints_[
PositiveRef(ref)].empty()) {
203 <<
" was removed, yet it appears in some constraints!";
206 for (
const int c : var_to_constraints_[
PositiveRef(ref)]) {
207 LOG(INFO) <<
"constraint #" << c <<
" : "
208 << (c >= 0 ?
working_model->constraints(c).ShortDebugString()
218 return var_to_num_linear1_[
var] == var_to_constraints_[
var].size();
224 result = domains[ref];
235 return domains[ref].Contains(
value);
239 int ref,
const Domain& domain,
bool* domain_modified) {
244 if (domains[
var].IsIncludedIn(domain)) {
247 domains[
var] = domains[
var].IntersectionWith(domain);
250 if (domains[
var].IsIncludedIn(temp)) {
253 domains[
var] = domains[
var].IntersectionWith(temp);
256 if (domain_modified !=
nullptr) {
257 *domain_modified =
true;
260 if (domains[
var].IsEmpty()) {
293 void PresolveContext::UpdateLinear1Usage(
const ConstraintProto&
ct,
int c) {
294 const int old_var = constraint_to_linear1_var_[c];
296 var_to_num_linear1_[old_var]--;
298 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
299 ct.linear().vars().size() == 1) {
301 constraint_to_linear1_var_[c] =
var;
302 var_to_num_linear1_[
var]++;
306 void PresolveContext::AddVariableUsage(
int c) {
310 for (
const int v : constraint_to_vars_[c]) {
312 var_to_constraints_[v].insert(c);
314 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
315 UpdateLinear1Usage(
ct, c);
319 if (is_unsat)
return;
320 DCHECK_EQ(constraint_to_vars_.size(),
working_model->constraints_size());
324 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
326 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
331 const std::vector<int>& old_usage = constraint_to_vars_[c];
332 const int old_size = old_usage.size();
334 for (
const int var : tmp_new_usage_) {
336 while (i < old_size && old_usage[i] <
var) {
337 var_to_constraints_[old_usage[i]].erase(c);
340 if (i < old_size && old_usage[i] ==
var) {
343 var_to_constraints_[
var].insert(c);
346 for (; i < old_size; ++i) var_to_constraints_[old_usage[i]].erase(c);
347 constraint_to_vars_[c] = tmp_new_usage_;
349 UpdateLinear1Usage(
ct, c);
353 return constraint_to_vars_.size() ==
working_model->constraints_size();
357 if (is_unsat)
return;
358 const int old_size = constraint_to_vars_.size();
360 CHECK_LE(old_size, new_size);
361 constraint_to_vars_.resize(new_size);
362 constraint_to_linear1_var_.resize(new_size, -1);
363 constraint_to_intervals_.resize(new_size);
364 interval_usage_.resize(new_size);
365 for (
int c = old_size; c < new_size; ++c) {
372 if (is_unsat)
return true;
373 if (constraint_to_vars_.size() !=
working_model->constraints_size()) {
374 LOG(INFO) <<
"Wrong constraint_to_vars size!";
377 for (
int c = 0; c < constraint_to_vars_.size(); ++c) {
378 if (constraint_to_vars_[c] !=
380 LOG(INFO) <<
"Wrong variables usage for constraint: \n"
382 <<
"old_size: " << constraint_to_vars_[c].size();
386 int num_in_objective = 0;
387 for (
int v = 0; v < var_to_constraints_.size(); ++v) {
390 if (!objective_map.contains(v)) {
391 LOG(INFO) <<
"Variable " << v
392 <<
" is marked as part of the objective but isn't.";
397 if (num_in_objective != objective_map.size()) {
398 LOG(INFO) <<
"Not all variables are marked as part of the objective";
415 bool PresolveContext::AddRelation(
int x,
int y,
int64 c,
int64 o,
419 if (std::abs(c) != 1)
return repo->
TryAdd(x, y, c, o);
436 bool allow_rep_x = m_x < m_y;
437 bool allow_rep_y = m_y < m_x;
444 return repo->
TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
452 const int rep = constant_to_ref_[
min].Get(
this);
455 AddRelation(
var, rep, 1, 0, &affine_relations_);
456 AddRelation(
var, rep, 1, 0, &var_equiv_relations_);
494 for (
auto& ref_map : var_to_constraints_) {
507 CHECK_EQ(var_to_constraints_[
var].size(), 1);
520 if (affine_relations_.
ClassSize(rep) == 1 &&
521 var_equiv_relations_.
ClassSize(rep) == 1) {
527 LOG(INFO) <<
"Removing affine relation for " <<
var <<
" : "
529 <<
DomainOf(r.representative) <<
" + " << r.offset
530 <<
" ( rep : " << rep <<
").";
537 if (is_unsat)
return false;
546 if (lhs % std::abs(coeff) != 0) {
575 if (
b != 0) is_unsat =
true;
583 const int64 unique_value = -
b /
a;
604 bool added = AddRelation(x, y, c, o, &affine_relations_);
605 if ((c == 1 || c == -1) && o == 0) {
606 added |= AddRelation(x, y, c, o, &var_equiv_relations_);
619 if (x != rep) encoding_remap_queue_.push_back(x);
620 if (y != rep) encoding_remap_queue_.push_back(y);
636 LOG(INFO) <<
"Cannot add relation " <<
DomainOf(ref_x) <<
" = " << coeff
637 <<
" * " <<
DomainOf(ref_y) <<
" + " << offset
638 <<
" because of incompatibilities with existing relation: ";
639 for (
const int ref : {ref_x, ref_y}) {
641 LOG(INFO) <<
DomainOf(ref) <<
" = " << r.coeff <<
" * "
642 <<
DomainOf(r.representative) <<
" + " << r.offset;
650 if (is_unsat)
return;
659 if (ref_a == ref_b)
return;
676 const auto insert_status = abs_relations_.insert(
678 if (!insert_status.second) {
680 const int candidate = insert_status.first->second.Get(
this);
681 if (removed_variables_.contains(candidate)) {
691 auto it = abs_relations_.find(target_ref);
692 if (it == abs_relations_.end())
return false;
699 const int candidate = it->second.Get(
this);
700 if (removed_variables_.contains(candidate)) {
701 abs_relations_.erase(it);
729 DCHECK_NE(positive_possible, negative_possible);
739 CHECK_EQ(std::abs(r.
coeff), 1);
761 for (
int i = domains.size(); i < working_model->variables_size(); ++i) {
763 if (domains.back().IsEmpty()) {
770 var_to_constraints_.resize(domains.size());
771 var_to_num_linear1_.resize(domains.size());
776 bool PresolveContext::RemapEncodingMaps() {
785 encoding_remap_queue_.clear();
791 for (
const int var : encoding_remap_queue_) {
795 int num_remapping = 0;
799 const absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
800 for (
const auto& entry : var_map) {
801 const int lit = entry.second.Get(
this);
802 if (removed_variables_.contains(
PositiveRef(lit)))
continue;
803 if ((entry.first - r.
offset) % r.
coeff != 0)
continue;
808 if (is_unsat)
return false;
810 encoding_.erase(
var);
815 const absl::flat_hash_map<int64, absl::flat_hash_set<int>>& var_map =
816 eq_half_encoding_[
var];
817 for (
const auto& entry : var_map) {
818 if ((entry.first - r.
offset) % r.
coeff != 0)
continue;
820 for (
int literal : entry.second) {
825 if (is_unsat)
return false;
828 eq_half_encoding_.erase(
var);
833 const absl::flat_hash_map<int64, absl::flat_hash_set<int>>& var_map =
834 neq_half_encoding_[
var];
835 for (
const auto& entry : var_map) {
836 if ((entry.first - r.
offset) % r.
coeff != 0)
continue;
838 for (
int literal : entry.second) {
843 if (is_unsat)
return false;
846 neq_half_encoding_.erase(
var);
849 if (num_remapping > 0) {
850 VLOG(1) <<
"Remapped " << num_remapping <<
" encodings due to " <<
var
854 encoding_remap_queue_.clear();
864 if (is_unsat)
return;
866 absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
869 auto min_it = var_map.find(var_min);
870 if (min_it != var_map.end()) {
871 const int old_var =
PositiveRef(min_it->second.Get(
this));
872 if (removed_variables_.contains(old_var)) {
873 var_map.erase(min_it);
874 min_it = var_map.end();
879 auto max_it = var_map.find(var_max);
880 if (max_it != var_map.end()) {
881 const int old_var =
PositiveRef(max_it->second.Get(
this));
882 if (removed_variables_.contains(old_var)) {
883 var_map.erase(max_it);
884 max_it = var_map.end();
891 if (min_it != var_map.end() && max_it != var_map.end()) {
892 min_literal = min_it->second.Get(
this);
893 max_literal = max_it->second.Get(
this);
897 if (is_unsat)
return;
902 }
else if (min_it != var_map.end() && max_it == var_map.end()) {
904 min_literal = min_it->second.Get(
this);
907 }
else if (min_it == var_map.end() && max_it != var_map.end()) {
909 max_literal = max_it->second.Get(
this);
936 var_max - var_min, var_min));
939 var_min - var_max, var_max));
944 void PresolveContext::InsertVarValueEncodingInternal(
int literal,
int var,
946 bool add_constraints) {
949 absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
955 const auto it = var_map.find(
value);
956 if (it != var_map.end()) {
957 const int old_var =
PositiveRef(it->second.Get(
this));
958 if (removed_variables_.contains(old_var)) {
964 var_map.insert(std::make_pair(
value, SavedLiteral(
literal)));
967 if (!insert.second) {
968 UpdateRuleStats(
"variables: merge equivalent var value encoding literals");
969 const int previous_literal = insert.first->second.Get(
this);
971 if (
literal != previous_literal) {
980 VLOG(2) <<
"Insert lit(" <<
literal <<
") <=> var(" <<
var
984 if (add_constraints) {
992 bool PresolveContext::InsertHalfVarValueEncoding(
int literal,
int var,
994 if (is_unsat)
return false;
1001 if (!direct_set.insert(
literal).second)
return false;
1003 VLOG(2) <<
"Collect lit(" <<
literal <<
") implies var(" <<
var
1004 << (imply_eq ?
") == " :
") != ") <<
value;
1011 for (
const int other : other_set) {
1016 InsertVarValueEncodingInternal(imply_eq_literal,
var,
value,
1024 bool PresolveContext::CanonicalizeEncoding(
int* ref,
int64*
value) {
1026 if ((*
value - r.offset) % r.coeff != 0)
return false;
1027 *ref = r.representative;
1034 if (!RemapEncodingMaps())
return;
1035 if (!CanonicalizeEncoding(&ref, &
value))
return;
1037 InsertVarValueEncodingInternal(
literal, ref,
value,
true);
1042 if (!RemapEncodingMaps())
return false;
1043 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1050 if (!RemapEncodingMaps())
return false;
1051 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1057 if (!RemapEncodingMaps())
return false;
1058 if (!CanonicalizeEncoding(&ref, &
value))
return false;
1059 const absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[ref];
1060 const auto it = var_map.find(
value);
1061 if (it != var_map.end()) {
1063 *
literal = it->second.Get(
this);
1075 const int var = ref;
1078 if (!domains[
var].Contains(
value)) {
1083 absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
1084 auto it = var_map.find(
value);
1085 if (it != var_map.end()) {
1086 return it->second.Get(
this);
1090 if (domains[
var].Size() == 1) {
1093 return true_literal;
1099 if (domains[
var].Size() == 2) {
1101 const int64 other_value =
value == var_min ? var_max : var_min;
1102 auto other_it = var_map.find(other_value);
1103 if (other_it != var_map.end()) {
1111 if (var_min == 0 && var_max == 1) {
1132 objective_offset = obj.offset();
1133 objective_scaling_factor = obj.scaling_factor();
1134 if (objective_scaling_factor == 0.0) {
1135 objective_scaling_factor = 1.0;
1137 if (!obj.domain().empty()) {
1140 objective_domain_is_constraining =
true;
1143 objective_domain_is_constraining =
false;
1147 objective_map.clear();
1148 for (
int i = 0; i < obj.vars_size(); ++i) {
1149 const int ref = obj.vars(i);
1150 int64 coeff = obj.coeffs(i);
1154 objective_map[
var] += coeff;
1155 if (objective_map[
var] == 0) {
1156 objective_map.erase(
var);
1165 int64 offset_change = 0;
1171 tmp_entries.clear();
1172 for (
const auto& entry : objective_map) {
1173 tmp_entries.push_back(entry);
1179 for (
const auto& entry : tmp_entries) {
1180 const int var = entry.first;
1181 const auto it = objective_map.find(
var);
1182 if (it == objective_map.end())
continue;
1183 const int64 coeff = it->second;
1190 var_to_constraints_[
var].size() == 1 &&
1205 offset_change += coeff *
MinOf(
var);
1207 objective_map.erase(
var);
1214 objective_map.erase(
var);
1218 offset_change += coeff * r.
offset;
1222 if (new_coeff == 0) {
1235 Domain implied_domain(0);
1239 tmp_entries.clear();
1240 for (
const auto& entry : objective_map) {
1241 tmp_entries.push_back(entry);
1243 std::sort(tmp_entries.begin(), tmp_entries.end());
1244 for (
const auto& entry : tmp_entries) {
1245 const int var = entry.first;
1246 const int64 coeff = entry.second;
1261 objective_offset += offset_change;
1265 for (
auto& entry : objective_map) {
1266 entry.second /= gcd;
1269 objective_offset /=
static_cast<double>(gcd);
1270 objective_scaling_factor *=
static_cast<double>(gcd);
1273 if (objective_domain.
IsEmpty())
return false;
1278 objective_domain_is_constraining =
1286 int var_in_equality,
int64 coeff_in_equality,
1287 const ConstraintProto& equality, std::vector<int>* new_vars_in_objective) {
1288 CHECK(equality.enforcement_literal().empty());
1291 if (new_vars_in_objective !=
nullptr) new_vars_in_objective->clear();
1295 const int64 coeff_in_objective =
1297 CHECK_NE(coeff_in_equality, 0);
1298 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1299 const int64 multiplier = coeff_in_objective / coeff_in_equality;
1301 for (
int i = 0; i < equality.linear().vars().size(); ++i) {
1302 int var = equality.linear().vars(i);
1303 int64 coeff = equality.linear().coeffs(i);
1308 if (
var == var_in_equality)
continue;
1310 int64& map_ref = objective_map[
var];
1311 if (map_ref == 0 && new_vars_in_objective !=
nullptr) {
1312 new_vars_in_objective->push_back(
var);
1314 map_ref -= coeff * multiplier;
1317 objective_map.erase(
var);
1324 objective_map.erase(var_in_equality);
1329 DCHECK_EQ(offset.
Min(), offset.
Max());
1335 objective_offset +=
static_cast<double>(offset.
Min());
1341 objective_domain_is_constraining =
true;
1345 if (objective_domain.
IsEmpty()) {
1350 std::vector<std::pair<int, int64>> entries;
1351 for (
const auto& entry : objective_map) {
1352 entries.push_back(entry);
1354 std::sort(entries.begin(), entries.end());
1356 CpObjectiveProto* mutable_obj =
working_model->mutable_objective();
1357 mutable_obj->set_offset(objective_offset);
1358 mutable_obj->set_scaling_factor(objective_scaling_factor);
1360 mutable_obj->clear_vars();
1361 mutable_obj->clear_coeffs();
1362 for (
const auto& entry : entries) {
1363 mutable_obj->add_vars(entry.first);
1364 mutable_obj->add_coeffs(entry.second);