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()) {
202 <<
" was removed, yet it appears in some constraints!";
203 LOG(
INFO) <<
"affine relation: "
205 for (
const int c : var_to_constraints_[
PositiveRef(ref)]) {
206 LOG(
INFO) <<
"constraint #" << c <<
" : "
207 << (c >= 0 ?
working_model->constraints(c).ShortDebugString()
217 return var_to_num_linear1_[
var] == var_to_constraints_[
var].size();
223 result = domains[ref];
234 return domains[ref].Contains(
value);
238 int ref,
const Domain& domain,
bool* domain_modified) {
243 if (domains[
var].IsIncludedIn(domain)) {
246 domains[
var] = domains[
var].IntersectionWith(domain);
249 if (domains[
var].IsIncludedIn(temp)) {
252 domains[
var] = domains[
var].IntersectionWith(temp);
255 if (domain_modified !=
nullptr) {
256 *domain_modified =
true;
259 if (domains[
var].IsEmpty()) {
292 void PresolveContext::UpdateLinear1Usage(
const ConstraintProto&
ct,
int c) {
293 const int old_var = constraint_to_linear1_var_[c];
295 var_to_num_linear1_[old_var]--;
297 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
298 ct.linear().vars().size() == 1) {
300 constraint_to_linear1_var_[c] =
var;
301 var_to_num_linear1_[
var]++;
305 void PresolveContext::AddVariableUsage(
int c) {
309 for (
const int v : constraint_to_vars_[c]) {
311 var_to_constraints_[v].insert(c);
313 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
314 UpdateLinear1Usage(
ct, c);
318 if (is_unsat)
return;
323 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
325 for (
const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
330 const std::vector<int>& old_usage = constraint_to_vars_[c];
331 const int old_size = old_usage.size();
333 for (
const int var : tmp_new_usage_) {
335 while (i < old_size && old_usage[i] <
var) {
336 var_to_constraints_[old_usage[i]].erase(c);
339 if (i < old_size && old_usage[i] ==
var) {
342 var_to_constraints_[
var].insert(c);
345 for (; i < old_size; ++i) var_to_constraints_[old_usage[i]].erase(c);
346 constraint_to_vars_[c] = tmp_new_usage_;
348 UpdateLinear1Usage(
ct, c);
352 return constraint_to_vars_.size() ==
working_model->constraints_size();
356 if (is_unsat)
return;
357 const int old_size = constraint_to_vars_.size();
360 constraint_to_vars_.resize(new_size);
361 constraint_to_linear1_var_.resize(new_size, -1);
362 constraint_to_intervals_.resize(new_size);
363 interval_usage_.resize(new_size);
364 for (
int c = old_size; c < new_size; ++c) {
371 if (is_unsat)
return true;
372 if (constraint_to_vars_.size() !=
working_model->constraints_size()) {
373 LOG(
INFO) <<
"Wrong constraint_to_vars size!";
376 for (
int c = 0; c < constraint_to_vars_.size(); ++c) {
377 if (constraint_to_vars_[c] !=
379 LOG(
INFO) <<
"Wrong variables usage for constraint: \n"
381 <<
"old_size: " << constraint_to_vars_[c].size();
385 int num_in_objective = 0;
386 for (
int v = 0; v < var_to_constraints_.size(); ++v) {
389 if (!objective_map.contains(v)) {
391 <<
" is marked as part of the objective but isn't.";
396 if (num_in_objective != objective_map.size()) {
397 LOG(
INFO) <<
"Not all variables are marked as part of the objective";
414 bool PresolveContext::AddRelation(
int x,
int y,
int64 c,
int64 o,
418 if (std::abs(c) != 1)
return repo->
TryAdd(x, y, c, o);
435 bool allow_rep_x = m_x < m_y;
436 bool allow_rep_y = m_y < m_x;
443 return repo->
TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
451 const int rep = constant_to_ref_[
min].Get(
this);
454 AddRelation(
var, rep, 1, 0, &affine_relations_);
455 AddRelation(
var, rep, 1, 0, &var_equiv_relations_);
493 for (
auto& ref_map : var_to_constraints_) {
519 if (affine_relations_.
ClassSize(rep) == 1 &&
520 var_equiv_relations_.
ClassSize(rep) == 1) {
532 if (is_unsat)
return false;
541 if (lhs % std::abs(coeff) != 0) {
570 if (
b != 0) is_unsat =
true;
578 const int64 unique_value = -
b /
a;
599 bool added = AddRelation(x, y, c, o, &affine_relations_);
600 if ((c == 1 || c == -1) && o == 0) {
601 added |= AddRelation(x, y, c, o, &var_equiv_relations_);
614 if (x != rep) encoding_remap_queue_.push_back(x);
615 if (y != rep) encoding_remap_queue_.push_back(y);
631 LOG(
INFO) <<
"Cannot add relation " <<
DomainOf(ref_x) <<
" = " << coeff
632 <<
" * " <<
DomainOf(ref_y) <<
" + " << offset
633 <<
" because of incompatibilities with existing relation: ";
634 for (
const int ref : {ref_x, ref_y}) {
637 <<
DomainOf(r.representative) <<
" + " << r.offset;
645 if (is_unsat)
return;
654 if (ref_a == ref_b)
return;
671 const auto insert_status = abs_relations_.insert(
673 if (!insert_status.second) {
675 const int candidate = insert_status.first->second.Get(
this);
676 if (removed_variables_.contains(candidate)) {
686 auto it = abs_relations_.find(target_ref);
687 if (it == abs_relations_.end())
return false;
694 const int candidate = it->second.Get(
this);
695 if (removed_variables_.contains(candidate)) {
696 abs_relations_.erase(it);
724 DCHECK_NE(positive_possible, negative_possible);
767 for (
int i = domains.size(); i < working_model->variables_size(); ++i) {
769 if (domains.back().IsEmpty()) {
776 var_to_constraints_.resize(domains.size());
777 var_to_num_linear1_.resize(domains.size());
782 bool PresolveContext::RemapEncodingMaps() {
791 encoding_remap_queue_.clear();
797 for (
const int var : encoding_remap_queue_) {
801 int num_remapping = 0;
805 const absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
806 for (
const auto& entry : var_map) {
807 const int lit = entry.second.Get(
this);
808 if (removed_variables_.contains(
PositiveRef(lit)))
continue;
809 if ((entry.first - r.
offset) % r.
coeff != 0)
continue;
814 if (is_unsat)
return false;
816 encoding_.erase(
var);
821 const absl::flat_hash_map<int64, absl::flat_hash_set<int>>& var_map =
822 eq_half_encoding_[
var];
823 for (
const auto& entry : var_map) {
824 if ((entry.first - r.
offset) % r.
coeff != 0)
continue;
826 for (
int literal : entry.second) {
831 if (is_unsat)
return false;
834 eq_half_encoding_.erase(
var);
839 const absl::flat_hash_map<int64, absl::flat_hash_set<int>>& var_map =
840 neq_half_encoding_[
var];
841 for (
const auto& entry : var_map) {
842 if ((entry.first - r.
offset) % r.
coeff != 0)
continue;
844 for (
int literal : entry.second) {
849 if (is_unsat)
return false;
852 neq_half_encoding_.erase(
var);
855 if (num_remapping > 0) {
856 VLOG(1) <<
"Remapped " << num_remapping <<
" encodings due to " <<
var
860 encoding_remap_queue_.clear();
870 if (is_unsat)
return;
872 absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
875 auto min_it = var_map.find(var_min);
876 if (min_it != var_map.end()) {
877 const int old_var =
PositiveRef(min_it->second.Get(
this));
878 if (removed_variables_.contains(old_var)) {
879 var_map.erase(min_it);
880 min_it = var_map.end();
885 auto max_it = var_map.find(var_max);
886 if (max_it != var_map.end()) {
887 const int old_var =
PositiveRef(max_it->second.Get(
this));
888 if (removed_variables_.contains(old_var)) {
889 var_map.erase(max_it);
890 max_it = var_map.end();
897 if (min_it != var_map.end() && max_it != var_map.end()) {
898 min_literal = min_it->second.Get(
this);
899 max_literal = max_it->second.Get(
this);
903 if (is_unsat)
return;
908 }
else if (min_it != var_map.end() && max_it == var_map.end()) {
910 min_literal = min_it->second.Get(
this);
913 }
else if (min_it == var_map.end() && max_it != var_map.end()) {
915 max_literal = max_it->second.Get(
this);
942 var_max - var_min, var_min));
945 var_min - var_max, var_max));
950 void PresolveContext::InsertVarValueEncodingInternal(
int literal,
int var,
952 bool add_constraints) {
955 absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
961 const auto it = var_map.find(
value);
962 if (it != var_map.end()) {
963 const int old_var =
PositiveRef(it->second.Get(
this));
964 if (removed_variables_.contains(old_var)) {
970 var_map.insert(std::make_pair(
value, SavedLiteral(
literal)));
973 if (!insert.second) {
974 const int previous_literal = insert.first->second.Get(
this);
976 if (
literal != previous_literal) {
978 "variables: merge equivalent var value encoding literals");
991 if (add_constraints) {
999 bool PresolveContext::InsertHalfVarValueEncoding(
int literal,
int var,
1001 if (is_unsat)
return false;
1008 if (!direct_set.insert(
literal).second)
return false;
1011 << (imply_eq ?
") == " :
") != ") <<
value;
1018 for (
const int other : other_set) {
1023 InsertVarValueEncodingInternal(imply_eq_literal,
var,
value,
1031 bool PresolveContext::CanonicalizeEncoding(
int* ref,
int64*
value) {
1033 if ((*
value - r.offset) % r.coeff != 0)
return false;
1034 *ref = r.representative;
1041 if (!RemapEncodingMaps())
return;
1042 if (!CanonicalizeEncoding(&ref, &
value))
return;
1044 InsertVarValueEncodingInternal(
literal, ref,
value,
true);
1049 if (!RemapEncodingMaps())
return false;
1050 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1057 if (!RemapEncodingMaps())
return false;
1058 if (!CanonicalizeEncoding(&
var, &
value))
return false;
1064 if (!RemapEncodingMaps())
return false;
1065 if (!CanonicalizeEncoding(&ref, &
value))
return false;
1066 const absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[ref];
1067 const auto it = var_map.find(
value);
1068 if (it != var_map.end()) {
1070 *
literal = it->second.Get(
this);
1082 const int var = ref;
1085 if (!domains[
var].Contains(
value)) {
1090 absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[
var];
1091 auto it = var_map.find(
value);
1092 if (it != var_map.end()) {
1093 return it->second.Get(
this);
1097 if (domains[
var].Size() == 1) {
1100 return true_literal;
1106 if (domains[
var].Size() == 2) {
1108 const int64 other_value =
value == var_min ? var_max : var_min;
1109 auto other_it = var_map.find(other_value);
1110 if (other_it != var_map.end()) {
1118 if (var_min == 0 && var_max == 1) {
1139 objective_offset = obj.offset();
1140 objective_scaling_factor = obj.scaling_factor();
1141 if (objective_scaling_factor == 0.0) {
1142 objective_scaling_factor = 1.0;
1144 if (!obj.domain().empty()) {
1147 objective_domain_is_constraining =
true;
1150 objective_domain_is_constraining =
false;
1154 objective_map.clear();
1155 for (
int i = 0; i < obj.vars_size(); ++i) {
1156 const int ref = obj.vars(i);
1157 int64 coeff = obj.coeffs(i);
1161 objective_map[
var] += coeff;
1162 if (objective_map[
var] == 0) {
1163 objective_map.erase(
var);
1172 int64 offset_change = 0;
1178 tmp_entries.clear();
1179 for (
const auto& entry : objective_map) {
1180 tmp_entries.push_back(entry);
1186 for (
const auto& entry : tmp_entries) {
1187 const int var = entry.first;
1188 const auto it = objective_map.find(
var);
1189 if (it == objective_map.end())
continue;
1190 const int64 coeff = it->second;
1197 var_to_constraints_[
var].size() == 1 &&
1212 offset_change += coeff *
MinOf(
var);
1214 objective_map.erase(
var);
1221 objective_map.erase(
var);
1225 offset_change += coeff * r.
offset;
1229 if (new_coeff == 0) {
1242 Domain implied_domain(0);
1246 tmp_entries.clear();
1247 for (
const auto& entry : objective_map) {
1248 tmp_entries.push_back(entry);
1250 std::sort(tmp_entries.begin(), tmp_entries.end());
1251 for (
const auto& entry : tmp_entries) {
1252 const int var = entry.first;
1253 const int64 coeff = entry.second;
1268 objective_offset += offset_change;
1272 for (
auto& entry : objective_map) {
1273 entry.second /= gcd;
1276 objective_offset /=
static_cast<double>(gcd);
1277 objective_scaling_factor *=
static_cast<double>(gcd);
1280 if (objective_domain.
IsEmpty())
return false;
1285 objective_domain_is_constraining =
1293 int var_in_equality,
int64 coeff_in_equality,
1294 const ConstraintProto& equality, std::vector<int>* new_vars_in_objective) {
1295 CHECK(equality.enforcement_literal().empty());
1298 if (new_vars_in_objective !=
nullptr) new_vars_in_objective->clear();
1302 const int64 coeff_in_objective =
1305 CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1306 const int64 multiplier = coeff_in_objective / coeff_in_equality;
1308 for (
int i = 0; i < equality.linear().vars().size(); ++i) {
1309 int var = equality.linear().vars(i);
1310 int64 coeff = equality.linear().coeffs(i);
1315 if (
var == var_in_equality)
continue;
1317 int64& map_ref = objective_map[
var];
1318 if (map_ref == 0 && new_vars_in_objective !=
nullptr) {
1319 new_vars_in_objective->push_back(
var);
1321 map_ref -= coeff * multiplier;
1324 objective_map.erase(
var);
1331 objective_map.erase(var_in_equality);
1342 objective_offset +=
static_cast<double>(offset.
Min());
1348 objective_domain_is_constraining =
true;
1350 if (objective_domain.
IsEmpty()) {
1357 std::vector<std::pair<int, int64>> entries;
1358 for (
const auto& entry : objective_map) {
1359 entries.push_back(entry);
1361 std::sort(entries.begin(), entries.end());
1363 CpObjectiveProto* mutable_obj =
working_model->mutable_objective();
1364 mutable_obj->set_offset(objective_offset);
1365 mutable_obj->set_scaling_factor(objective_scaling_factor);
1367 mutable_obj->clear_vars();
1368 mutable_obj->clear_coeffs();
1369 for (
const auto& entry : entries) {
1370 mutable_obj->add_vars(entry.first);
1371 mutable_obj->add_coeffs(entry.second);