27 #include "absl/container/flat_hash_map.h"
28 #include "absl/strings/str_cat.h"
35 #include "ortools/sat/sat_parameters.pb.h"
47 if (shared_stats_ ==
nullptr)
return;
48 std::vector<std::pair<std::string, int64_t>> stats;
49 stats.push_back({
"implied_bound/num_deductions", num_deductions_});
50 stats.push_back({
"implied_bound/num_stored", bounds_.size()});
52 {
"implied_bound/num_stored_with_view", num_enqueued_in_var_to_bounds_});
57 if (!parameters_.use_implied_bounds())
return true;
58 const IntegerVariable
var = integer_literal.
var;
63 if (integer_literal.
bound <= root_lb)
return true;
72 const auto key = std::make_pair(
literal.Index(),
var);
73 auto insert_result = bounds_.insert({key, integer_literal.
bound});
74 if (!insert_result.second) {
75 if (insert_result.first->second < integer_literal.
bound) {
76 insert_result.first->second = integer_literal.
bound;
89 if (it != bounds_.end() && it->second == -integer_literal.
bound) {
101 const auto it = bounds_.find(std::make_pair(
literal.NegatedIndex(),
var));
102 if (it != bounds_.end()) {
103 if (it->second <= root_lb) {
108 const IntegerValue deduction =
110 DCHECK_GT(deduction, root_lb);
118 VLOG(2) <<
"Deduction old: "
125 if (it->second == deduction) {
128 if (integer_literal.
bound == deduction) {
129 bounds_.erase(std::make_pair(
literal.Index(),
var));
149 if (parameters_.linearization_level() == 0)
return true;
150 if (parameters_.cut_level() == 0)
return true;
156 if (var_to_bounds_.size() <=
var) {
157 var_to_bounds_.resize(
var.value() + 1);
160 ++num_enqueued_in_var_to_bounds_;
161 has_implied_bounds_.
Set(
var);
163 integer_literal.
bound,
true});
166 if (var_to_bounds_.size() <=
var) {
167 var_to_bounds_.resize(
var.value() + 1);
170 ++num_enqueued_in_var_to_bounds_;
171 has_implied_bounds_.
Set(
var);
172 var_to_bounds_[
var].push_back(
174 integer_literal.
bound,
false});
180 IntegerVariable
var) {
181 if (
var >= var_to_bounds_.size())
return empty_implied_bounds_;
188 std::vector<ImpliedBoundEntry>& ref = var_to_bounds_[
var];
191 if (entry.lower_bound <= root_lb)
continue;
192 ref[new_size++] = entry;
194 ref.resize(new_size);
201 IntegerValue
value) {
210 if (!parameters_.use_implied_bounds())
return true;
213 tmp_integer_literals_.clear();
216 if (!
Add(first_decision, lit))
return false;
222 IntegerVariable
var,
const std::vector<ValueLiteralPair>& encoding,
223 int exactly_one_index) {
224 var_to_index_to_element_encodings_[
var][exactly_one_index] = encoding;
227 const absl::flat_hash_map<int, std::vector<ValueLiteralPair>>&
229 const auto& it = var_to_index_to_element_encodings_.find(
var);
230 if (it == var_to_index_to_element_encodings_.end()) {
231 return empty_element_encoding_;
239 return element_encoded_variables_;
242 std::string
EncodingStr(
const std::vector<ValueLiteralPair>& enc) {
245 absl::StrAppend(&result, term.literal.DebugString(),
":",
246 term.value.value(),
" ");
268 const std::vector<ValueLiteralPair>& affine_var_encoding,
269 bool put_affine_left_in_result,
Model*
model) {
271 IntegerVariable binary = size2_affine.
var;
272 std::vector<LiteralValueValue> terms;
274 const std::vector<ValueLiteralPair>& size2_enc =
279 if (size2_enc.size() != 2)
return terms;
281 Literal lit0 = size2_enc[0].literal;
282 IntegerValue value0 = size2_affine.
ValueAt(size2_enc[0].
value);
283 Literal lit1 = size2_enc[1].literal;
284 IntegerValue value1 = size2_affine.
ValueAt(size2_enc[1].
value);
286 for (
const auto& [unused, candidate_literal] : affine_var_encoding) {
287 if (candidate_literal == lit1) {
291 if (candidate_literal != lit0)
continue;
294 for (
const auto& [
value,
literal] : affine_var_encoding) {
295 const IntegerValue size_2_value =
literal == lit0 ? value0 : value1;
296 const IntegerValue affine_value = affine.
ValueAt(
value);
297 if (put_affine_left_in_result) {
298 terms.push_back({
literal, affine_value, size_2_value});
300 terms.push_back({
literal, size_2_value, affine_value});
314 std::vector<LiteralValueValue> terms;
319 const std::vector<ValueLiteralPair>& left_enc =
321 const std::vector<ValueLiteralPair>& right_enc =
323 if (left_enc.size() != 2 || right_enc.size() != 2) {
324 VLOG(2) <<
"encodings are not fully propagated";
328 const Literal left_lit0 = left_enc[0].literal;
329 const IntegerValue left_value0 = left.
ValueAt(left_enc[0].
value);
330 const Literal left_lit1 = left_enc[1].literal;
331 const IntegerValue left_value1 = left.
ValueAt(left_enc[1].
value);
333 const Literal right_lit0 = right_enc[0].literal;
334 const IntegerValue right_value0 = right.
ValueAt(right_enc[0].
value);
335 const Literal right_lit1 = right_enc[1].literal;
336 const IntegerValue right_value1 = right.
ValueAt(right_enc[1].
value);
338 if (left_lit0 == right_lit0 || left_lit0 == right_lit1.
Negated()) {
339 terms.push_back({left_lit0, left_value0, right_value0});
340 terms.push_back({left_lit0.
Negated(), left_value1, right_value1});
341 }
else if (left_lit0 == right_lit1 || left_lit0 == right_lit0.
Negated()) {
342 terms.push_back({left_lit0, left_value0, right_value1});
343 terms.push_back({left_lit0.
Negated(), left_value1, right_value0});
344 }
else if (left_lit1 == right_lit1 || left_lit1 == right_lit0.
Negated()) {
345 terms.push_back({left_lit1.
Negated(), left_value0, right_value0});
346 terms.push_back({left_lit1, left_value1, right_value1});
347 }
else if (left_lit1 == right_lit0 || left_lit1 == right_lit1.
Negated()) {
348 terms.push_back({left_lit1.
Negated(), left_value0, right_value1});
349 terms.push_back({left_lit1, left_value1, right_value0});
351 VLOG(3) <<
"Complex size 2 encoding case, need to scan exactly_ones";
360 if (integer_trail->
IsFixed(left) || integer_trail->
IsFixed(right))
return {};
364 const absl::flat_hash_map<int, std::vector<ValueLiteralPair>>&
368 const absl::flat_hash_map<int, std::vector<ValueLiteralPair>>&
371 std::vector<int> compatible_keys;
372 for (
const auto& [
index, encoding] : left_encodings) {
373 if (right_encodings.contains(
index)) {
374 compatible_keys.push_back(
index);
378 if (compatible_keys.empty()) {
380 for (
const auto& [
index, right_encoding] : right_encodings) {
381 const std::vector<LiteralValueValue> result =
384 if (!result.empty()) {
390 for (
const auto& [
index, left_encoding] : left_encodings) {
391 const std::vector<LiteralValueValue> result =
394 if (!result.empty()) {
401 const std::vector<LiteralValueValue> result =
403 if (!result.empty()) {
410 if (compatible_keys.size() > 1) {
411 VLOG(3) <<
"More than one exactly_one involved in the encoding of the two "
416 const int min_index =
417 *std::min_element(compatible_keys.begin(), compatible_keys.end());
420 const std::vector<ValueLiteralPair>& left_encoding =
421 left_encodings.at(min_index);
422 const std::vector<ValueLiteralPair>& right_encoding =
423 right_encodings.at(min_index);
424 DCHECK_EQ(left_encoding.size(), right_encoding.size());
427 std::vector<LiteralValueValue> terms;
428 for (
int i = 0; i < left_encoding.size(); ++i) {
432 right.
ValueAt(right_encoding[i].value)});
443 DCHECK(builder !=
nullptr);
447 if (integer_trail->
IsFixed(left)) {
448 if (integer_trail->
IsFixed(right)) {
457 if (integer_trail->
IsFixed(right)) {
467 const IntegerValue left_coeff =
469 const IntegerValue right_coeff =
472 left_coeff * right_coeff + left.
constant * right_coeff +
478 const std::vector<LiteralValueValue> product =
480 if (product.empty())
return false;
485 std::min(min_coefficient, term.left_value * term.right_value);
490 term.left_value * term.right_value - min_coefficient;
501 : enabled_(
model->GetOrCreate<SatParameters>()->linearization_level() > 1),
510 if (shared_stats_ ==
nullptr)
return;
511 std::vector<std::pair<std::string, int64_t>> stats;
513 {
"product_detector/num_processed_binary", num_processed_binary_});
515 {
"product_detector/num_processed_exactly_one", num_processed_exo_});
517 {
"product_detector/num_processed_ternary", num_processed_ternary_});
518 stats.push_back({
"product_detector/num_trail_updates", num_trail_updates_});
519 stats.push_back({
"product_detector/num_products", num_products_});
520 stats.push_back({
"product_detector/num_conditional_equalities",
521 num_conditional_equalities_});
523 {
"product_detector/num_conditional_zeros", num_conditional_zeros_});
524 stats.push_back({
"product_detector/num_int_products", num_int_products_});
529 absl::Span<const Literal> ternary_clause) {
530 if (!enabled_)
return;
531 if (ternary_clause.size() != 3)
return;
532 ++num_processed_ternary_;
533 candidates_[GetKey(ternary_clause[0].
Index(), ternary_clause[1].
Index())]
534 .push_back(ternary_clause[2].
Index());
535 candidates_[GetKey(ternary_clause[0].
Index(), ternary_clause[2].
Index())]
536 .push_back(ternary_clause[1].
Index());
537 candidates_[GetKey(ternary_clause[1].
Index(), ternary_clause[2].
Index())]
538 .push_back(ternary_clause[0].
Index());
542 for (
const Literal l : ternary_clause) {
543 if (l.Index() >= seen_.
size()) seen_.
resize(l.Index() + 1);
544 seen_[l.Index()] =
true;
549 absl::Span<const Literal> ternary_exo) {
550 if (!enabled_)
return;
551 if (ternary_exo.size() != 3)
return;
552 ++num_processed_exo_;
553 ProcessNewProduct(ternary_exo[0].
Index(), ternary_exo[1].NegatedIndex(),
554 ternary_exo[2].NegatedIndex());
555 ProcessNewProduct(ternary_exo[1].
Index(), ternary_exo[0].NegatedIndex(),
556 ternary_exo[2].NegatedIndex());
557 ProcessNewProduct(ternary_exo[2].
Index(), ternary_exo[0].NegatedIndex(),
558 ternary_exo[1].NegatedIndex());
564 absl::Span<const Literal> binary_clause) {
565 if (!enabled_)
return;
566 if (binary_clause.size() != 2)
return;
567 ++num_processed_binary_;
568 const std::array<LiteralIndex, 2> key =
569 GetKey(binary_clause[0].NegatedIndex(), binary_clause[1].NegatedIndex());
570 std::array<LiteralIndex, 3> ternary;
571 for (
const LiteralIndex l : candidates_[key]) {
575 std::sort(ternary.begin(), ternary.end());
576 const int l_index = ternary[0] == l ? 0 : ternary[1] == l ? 1 : 2;
577 std::bitset<3>& bs = detector_[ternary];
578 if (bs[l_index])
continue;
580 if (bs[0] && bs[1] && l_index != 2) {
581 ProcessNewProduct(ternary[2],
Literal(ternary[0]).NegatedIndex(),
582 Literal(ternary[1]).NegatedIndex());
584 if (bs[0] && bs[2] && l_index != 1) {
585 ProcessNewProduct(ternary[1],
Literal(ternary[0]).NegatedIndex(),
586 Literal(ternary[2]).NegatedIndex());
588 if (bs[1] && bs[2] && l_index != 0) {
589 ProcessNewProduct(ternary[0],
Literal(ternary[1]).NegatedIndex(),
590 Literal(ternary[2]).NegatedIndex());
596 if (!enabled_)
return;
597 for (LiteralIndex
a(0);
a < seen_.
size(); ++
a) {
598 if (!seen_[
a])
continue;
608 if (!enabled_)
return;
610 ++num_trail_updates_;
618 const int current_index = trail_->
Index();
619 for (
int i = decision.
trail_index + 1; i < current_index; ++i) {
626 const auto it = products_.find(GetKey(
a.Index(),
b.Index()));
631 std::array<LiteralIndex, 2> ProductDetector::GetKey(LiteralIndex
a,
632 LiteralIndex
b)
const {
633 std::array<LiteralIndex, 2> key{
a,
b};
634 if (key[0] > key[1])
std::swap(key[0], key[1]);
638 void ProductDetector::ProcessNewProduct(LiteralIndex p, LiteralIndex
a,
642 products_[GetKey(
a,
b)] = p;
646 GetKey(Literal(
a).IsPositive() ?
a : Literal(
a).NegatedIndex(),
647 Literal(
b).IsPositive() ?
b : Literal(
b).NegatedIndex()));
651 IntegerVariable
b)
const {
652 if (
a ==
b)
return true;
660 const LiteralIndex la =
665 const LiteralIndex lb =
671 return has_product_.contains(
672 GetKey(
Literal(la).IsPositive() ? la :
Literal(la).NegatedIndex(),
677 IntegerVariable
b)
const {
683 void ProductDetector::ProcessNewProduct(IntegerVariable p,
Literal l,
692 int_products_[{l.
Index(), x}] = p;
697 ++num_conditional_equalities_;
701 for (
int i = 0; i < 2; ++i) {
710 std::vector<IntegerVariable>& others =
711 conditional_equalities_[{l.
Index(), x}];
712 for (
const IntegerVariable o : others) {
721 if (conditional_zeros_.contains({l.NegatedIndex(), x})) {
722 ProcessNewProduct(x, l, y);
730 ++num_conditional_zeros_;
732 auto [_, inserted] = conditional_zeros_.insert({l.
Index(), p});
734 const auto it = conditional_equalities_.find({l.
NegatedIndex(), p});
735 if (it != conditional_equalities_.end()) {
736 for (
const IntegerVariable x : it->second) {
737 ProcessNewProduct(p, l.
Negated(), x);
void resize(size_type new_size)
int64_t Size() const
Returns the number of elements in the domain.
void Set(IntegerType index)
void Resize(IntegerType size)
const std::vector< Literal > & DirectImplications(Literal literal)
const std::vector< ImpliedBoundEntry > & GetImpliedBounds(IntegerVariable var)
void AddLiteralImpliesVarEqValue(Literal literal, IntegerVariable var, IntegerValue value)
bool Add(Literal literal, IntegerLiteral integer_literal)
const absl::flat_hash_map< int, std::vector< ValueLiteralPair > > & GetElementEncodings(IntegerVariable var)
void AddElementEncoding(IntegerVariable var, const std::vector< ValueLiteralPair > &encoding, int exactly_one_index)
const std::vector< IntegerVariable > & GetElementEncodedVariables() const
bool ProcessIntegerTrail(Literal first_decision)
LiteralIndex GetAssociatedLiteral(IntegerLiteral i_lit) const
const IntegerVariable GetLiteralView(Literal lit) const
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
bool VariableIsFullyEncoded(IntegerVariable var) const
bool IsFixed(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool RootLevelEnqueue(IntegerLiteral i_lit)
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue FixedValue(IntegerVariable i) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
IntegerValue LowerBound(IntegerVariable i) const
bool IsOptional(IntegerVariable i) const
const Domain & InitialVariableDomain(IntegerVariable var) const
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
void AddConstant(IntegerValue value)
void AddTerm(IntegerVariable var, IntegerValue coeff)
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
Class that owns everything related to a particular optimization model.
void ProcessTernaryExactlyOne(absl::Span< const Literal > ternary_exo)
void ProcessTrailAtLevelOne()
ProductDetector(Model *model)
void ProcessConditionalZero(Literal l, IntegerVariable p)
LiteralIndex GetProduct(Literal a, Literal b) const
void ProcessBinaryClause(absl::Span< const Literal > binary_clause)
bool ProductIsLinearizable(IntegerVariable a, IntegerVariable b) const
void ProcessImplicationGraph(BinaryImplicationGraph *graph)
void ProcessTernaryClause(absl::Span< const Literal > ternary_clause)
void ProcessConditionalEquality(Literal l, IntegerVariable x, IntegerVariable y)
int CurrentDecisionLevel() const
const std::vector< Decision > & Decisions() const
void AddStats(absl::Span< const std::pair< std::string, int64_t >> stats)
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
bool LiteralIsAssigned(Literal literal) const
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const LiteralIndex kNoLiteralIndex(-1)
std::string EncodingStr(const std::vector< ValueLiteralPair > &enc)
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
bool DetectLinearEncodingOfProducts(const AffineExpression &left, const AffineExpression &right, Model *model, LinearConstraintBuilder *builder)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::vector< LiteralValueValue > TryToDecomposeProduct(const AffineExpression &left, const AffineExpression &right, Model *model)
std::vector< LiteralValueValue > TryToReconcileEncodings(const AffineExpression &size2_affine, const AffineExpression &affine, const std::vector< ValueLiteralPair > &affine_var_encoding, bool put_affine_left_in_result, Model *model)
std::vector< LiteralValueValue > TryToReconcileSize2Encodings(const AffineExpression &left, const AffineExpression &right, Model *model)
bool VariableIsPositive(IntegerVariable i)
Collection of objects used to extend the Constraint Solver library.
IntegerValue ValueAt(IntegerValue var_value) const
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)