24#include "absl/algorithm/container.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/container/inlined_vector.h"
28#include "absl/log/check.h"
29#include "absl/log/log.h"
30#include "google/protobuf/repeated_field.h"
43 if (ct.linear().vars_size() != 1)
return false;
44 if (ct.linear().coeffs(0) != 1)
return false;
45 if (ct.enforcement_literal_size() != 1)
return false;
46 if (
PositiveRef(ct.enforcement_literal(0)) == ct.linear().vars(0)) {
71 std::vector<int> constraint_encoding_or;
74 absl::flat_hash_map<int, absl::InlinedVector<int, 1>> var_to_linear1;
80 constraint_encoding_or.push_back(
i);
83 if (!ConstraintIsEncodingBound(ct)) {
90 absl::erase_if(var_to_linear1, [context](
const auto& p) {
91 if (p.second.size() > 1)
return false;
95 if (var_to_linear1.empty())
return {};
97 absl::flat_hash_map<int, absl::InlinedVector<int, 2>> bool_to_var_encodings;
108 for (
const auto& [var, linear1_cts] : var_to_linear1) {
109 for (
const int c : linear1_cts) {
112 booleans_potentially_encoding_domain.
Set(bool_var);
113 bool_to_var_encodings[bool_var].push_back(var);
116 for (
auto& [bool_var, var_encodings] : bool_to_var_encodings) {
120 int new_encoding_or_count = 0;
121 for (
int i = 0;
i < constraint_encoding_or.size(); ++
i) {
122 const int c = constraint_encoding_or[
i];
130 if (absl::c_count_if(
132 [booleans_potentially_encoding_domain](
int ref) {
133 return booleans_potentially_encoding_domain[PositiveRef(ref)];
137 constraint_encoding_or[new_encoding_or_count++] = c;
139 constraint_encoding_or.resize(new_encoding_or_count);
143 struct VariableAndBoolInfo {
145 int linear1_count = 0;
148 int bool_or_count = 0;
150 absl::flat_hash_map<std::pair<int, int>, VariableAndBoolInfo> var_bool_counts;
154 absl::flat_hash_map<int, std::vector<int>> var_to_constraints_encoding_or;
157 absl::flat_hash_map<int, std::vector<int>> var_to_bools;
159 for (
const int c : constraint_encoding_or) {
160 var_to_bools.clear();
168 for (
const int ref : bool_ct.
literals()) {
170 if (!booleans_potentially_encoding_domain[bool_var])
continue;
171 for (
const int var : bool_to_var_encodings[bool_var]) {
172 var_to_bools[var].push_back(bool_var);
175 for (
const auto& [var, bools] : var_to_bools) {
176 if (bools.size() >= 2) {
179 var_to_constraints_encoding_or[var].push_back(c);
180 for (
const int bool_var : bools) {
181 var_bool_counts[{var, bool_var}].bool_or_count++;
187 std::vector<VariableEncodingLocalModel> local_models;
189 for (
const auto& [var, linear1_cts] : var_to_linear1) {
191 encoding_model.
var = var;
195 var_to_constraints_encoding_or[var];
199 for (
const int ct : linear1_cts) {
203 var_bool_counts[{var, bool_var}].linear1_count++;
206 [context, v = var, &var_bool_counts](
int bool_var) {
207 const auto& counts = var_bool_counts[{v, bool_var}];
209 counts.linear1_count + counts.bool_or_count;
213 encoding_model.variable_coeff_in_objective = it->second;
216 absl::c_sort(local_models, [](
const VariableEncodingLocalModel& a,
217 const VariableEncodingLocalModel&
b) {
218 return a.var <
b.var;
225 absl::flat_hash_map<int, Domain>* result,
bool* changed) {
227 absl::flat_hash_map<int, int> ref_to_linear1;
233 DCHECK(ConstraintIsEncodingBound(*ct_proto));
241 "variables: linear1 with domain not included in variable domain");
251 auto [it, inserted] = ref_to_linear1.insert({ref, ct});
262 "variables: linear1 with same variable and enforcement and "
263 "non-overlapping domain, setting enforcement to false");
267 old_ct_proto->
Clear();
269 ref_to_linear1.erase(ref);
273 "variables: merged two linear1 with same variable and enforcement");
279 int new_linear1_size = 0;
287 DCHECK(ConstraintIsEncodingBound(ct_proto));
297 for (
const auto& [ref, ct] : ref_to_linear1) {
298 auto it = ref_to_linear1.find(
NegatedRef(ref));
299 if (it == ref_to_linear1.end())
continue;
313 bool domain_modified =
false;
315 local_model.
var, positive_domain.
UnionWith(negative_domain),
319 *changed = *changed || domain_modified;
320 result->insert({ref, positive_domain});
321 result->insert({
NegatedRef(ref), negative_domain});
344 if (bool_or.
literals().size() < 2)
continue;
345 bool encoding_detected =
true;
346 Domain non_overlapping_domain;
347 std::vector<std::pair<int, Domain>> ref_and_domains;
348 for (
const int ref : bool_or.
literals()) {
349 auto it = ref_to_linear1.find(ref);
350 if (it == ref_to_linear1.end()) {
351 encoding_detected =
false;
356 ref_and_domains.push_back({ref, domain});
358 encoding_detected =
false;
361 non_overlapping_domain = non_overlapping_domain.
UnionWith(domain);
363 if (encoding_detected) {
365 bool domain_modified =
false;
370 if (domain_modified) {
372 "variables: restricted domain to fully encoded domain");
374 *changed = *changed || domain_modified;
375 for (
const auto& [ref, domain] : ref_and_domains) {
376 result->insert({ref, domain});
383 "variables: promoted bool_or to exactly_one for fully encoded "
385 std::vector<int> new_enforcement_literals(bool_or.
literals().begin(),
391 ->Add(new_enforcement_literals.begin(),
392 new_enforcement_literals.end());
404 absl::flat_hash_map<int, Domain>* fully_encoded_domains,
bool* changed) {
429 google::protobuf::RepeatedField<int32_t>& literals =
435 if (literals.size() <= 1)
return true;
459 std::optional<int> maybe_lit1;
461 std::optional<int> maybe_lit2;
463 for (
const int lit_var : literals) {
468 auto it = fully_encoded_domains->find(lit_var);
469 if (it == fully_encoded_domains->end()) {
474 maybe_lit1 = lit_var;
475 domain_lit1 = it->second;
477 maybe_lit2 = lit_var;
478 domain_lit2 = it->second;
483 if (!maybe_lit2.has_value())
return true;
484 DCHECK(maybe_lit1.has_value());
485 const int lit1 = *maybe_lit1;
486 const int lit2 = *maybe_lit2;
496 "variables: detected encoding of a complex domain with multiple "
511 const Domain domain_new_var_true =
517 if (domain_new_var_true.
IsEmpty()) {
519 }
else if (domain_new_var_false.
IsEmpty()) {
541 for (
int i = 0;
i < literals.size(); ++
i) {
542 if (literals.Get(
i) != lit1 && literals.Get(
i) != lit2) {
543 literals.Set(new_size++, literals.Get(
i));
546 literals.Truncate(new_size);
547 literals.Add(new_var);
551 fully_encoded_domains->insert({new_var, domain_new_var_true});
552 fully_encoded_domains->insert({
NegatedRef(new_var), domain_new_var_false});
553 fully_encoded_domains->erase(lit1);
554 fully_encoded_domains->erase(lit2);
555 fully_encoded_domains->erase(
NegatedRef(lit1));
556 fully_encoded_domains->erase(
NegatedRef(lit2));
559 int new_linear1_size = 0;
579 absl::flat_hash_map<int, Domain> fully_encoded_domains;
580 bool changed_on_basic_presolve =
false;
582 &fully_encoded_domains,
583 &changed_on_basic_presolve)) {
593 &fully_encoded_domains, &changed)) {
602 if (local_model.
var == -1)
return true;
618 !other_ct.enforcement_literal().empty() ||
625 std::function<std::pair<int, Domain>(
const Domain& implied)> transfer_f =
628 const int var = local_model.
var;
635 other_ct.lin_max().target().vars().size() == 1 &&
636 other_ct.lin_max().target().vars(0) == var &&
637 std::abs(other_ct.lin_max().target().coeffs(0)) == 1 &&
642 transfer_f = [target = target, expr = expr](
const Domain& implied) {
647 Domain(0, std::numeric_limits<int64_t>::max()));
650 const Domain expr_domain =
654 return std::make_pair(expr.
vars(0), new_domain);
658 if (transfer_f ==
nullptr) {
660 "TODO linear1: appear in only one extra 2-var constraint");
666 for (
const int c : to_rewrite) {
670 LOG(INFO) <<
"Aborted in MaybeTransferLinear1ToAnotherVariable()";
676 auto [new_var, new_domain] = transfer_f(implied);
679 if (new_domain.IsEmpty()) {
683 }
else if (new_domain == current) {
700 local_model.
var = -1;
Domain IntersectionWith(const Domain &domain) const
Domain ContinuousMultiplicationBy(int64_t coeff) const
Domain AdditionWith(const Domain &domain) const
bool IsIncludedIn(const Domain &domain) const
Domain UnionWith(const Domain &domain) const
Domain Complement() const
Domain InverseMultiplicationBy(int64_t coeff) const
::int32_t literals(int index) const
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_literals()
ConstraintCase constraint_case() const
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
const ::operations_research::sat::LinearConstraintProto & linear() const
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_exactly_one()
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_or()
void add_enforcement_literal(::int32_t value)
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
const ::operations_research::sat::BoolArgumentProto & bool_or() const
::operations_research::sat::LinearConstraintProto *PROTOBUF_NONNULL mutable_linear()
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_at_most_one()
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL mutable_constraints(int index)
const ::operations_research::sat::ConstraintProto & constraints(int index) const
int constraints_size() const
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL add_constraints()
int variables_size() const
::int32_t vars(int index) const
void set_vars(int index, ::int32_t value)
void add_vars(::int32_t value)
void add_coeffs(::int64_t value)
::int64_t coeffs(int index) const
::int64_t coeffs(int index) const
::int32_t vars(int index) const
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
CpModelProto * working_model
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
absl::Span< const int > ConstraintToVars(int c) const
bool IsFixed(int ref) const
int NewBoolVarWithClause(absl::Span< const int > clause)
bool MarkConstraintAsFalse(ConstraintProto *ct, std::string_view reason)
ConstraintProto * NewMappingConstraint(absl::string_view file, int line)
void UpdateConstraintVariableUsage(int c)
const absl::flat_hash_set< int > & VarToConstraints(int var) const
void MarkVariableAsRemoved(int ref)
void UpdateNewConstraintsVariableUsage()
const absl::flat_hash_map< int, int64_t > & ObjectiveMap() const
const Domain & DomainOf(int var) const
void UpdateRuleStats(std::string_view name, int num_times=1)
bool ModelIsUnsat() const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
std::vector< VariableEncodingLocalModel > CreateVariableEncodingLocalModels(PresolveContext *context)
bool BasicPresolveAndGetFullyEncodedDomains(PresolveContext *context, VariableEncodingLocalModel &local_model, absl::flat_hash_map< int, Domain > *result, bool *changed)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
bool MaybeTransferLinear1ToAnotherVariable(VariableEncodingLocalModel &local_model, PresolveContext *context)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
bool IsAffineIntAbs(const ConstraintProto &ct)
bool DetectEncodedComplexDomain(PresolveContext *context, int ct_index, VariableEncodingLocalModel &local_model, absl::flat_hash_map< int, Domain > *fully_encoded_domains, bool *changed)
bool DetectAllEncodedComplexDomain(PresolveContext *context, VariableEncodingLocalModel &local_model)
int single_constraint_using_the_var_outside_the_local_model
bool var_in_more_than_one_constraint_outside_the_local_model
int64_t variable_coeff_in_objective
absl::flat_hash_set< int > bools_only_used_inside_the_local_model
std::vector< int > linear1_constraints
std::vector< int > constraints_linking_two_encoding_booleans