27 #include "absl/container/btree_set.h"
28 #include "absl/container/flat_hash_map.h"
29 #include "absl/container/flat_hash_set.h"
49 return absl::StrCat(
"coeff=",
coeff.value(),
" lp=",
lp_value,
59 for (
int i = 0; i < 2; ++i) {
74 double lp_value, IntegerValue lb, IntegerValue ub) {
75 if (coeff == 0)
return true;
76 const IntegerValue bound_diff = ub - lb;
79 bool complement =
false;
80 const double lb_dist = std::abs(lp_value -
ToDouble(lb));
81 const double ub_dist = std::abs(lp_value -
ToDouble(ub));
82 if (ub_dist < lb_dist) {
93 if (bound_diff == 0)
return true;
103 entry.
coeff = -coeff;
112 terms.push_back(entry);
122 const int num_terms = base_ct.
vars.size();
123 for (
int i = 0; i < num_terms; ++i) {
124 const IntegerVariable
var = base_ct.
vars[i];
141 const int size = lp_values.size();
142 if (size == 0)
return true;
146 CHECK_EQ(base_ct.
vars.size(), size);
147 CHECK_EQ(base_ct.
coeffs.size(), size);
150 for (
int i = 0; i < size; ++i) {
163 for (
int i = 0; i <
terms.size(); ++i) {
175 return a.lp_value > b.lp_value;
181 constraint_is_indexed_ =
false;
182 direct_index_.clear();
183 complemented_index_.clear();
186 void CutDataBuilder::RegisterAllBooleansTerms(
const CutData& cut) {
187 constraint_is_indexed_ =
true;
188 const int size = cut.
terms.size();
189 for (
int i = 0; i < size; ++i) {
196 complemented_index_[term.
expr_vars[0]] = i;
203 if (!constraint_is_indexed_) {
204 RegisterAllBooleansTerms(*cut);
209 const int new_index = cut->
terms.size();
210 const auto [it, inserted] =
212 : complemented_index_.insert({var, new_index});
213 const int entry_index = it->second;
215 cut->
terms.push_back(term);
222 const int64_t new_coeff =
224 const int64_t overflow_check =
CapProd(t.value(), new_coeff);
227 cut->
terms.push_back(term);
230 cut->
terms[entry_index].coeff = IntegerValue(new_coeff);
238 IntegerValue new_rhs = cut.
rhs;
240 for (
int i = 0; i < 2; ++i) {
254 output->
ub = new_rhs;
255 for (
const auto [
var, coeff] : tmp_map_) {
256 if (coeff == 0)
continue;
258 output->
coeffs.push_back(coeff);
268 const double kMinCutViolation = 1e-4;
270 IntegerValue CapProdI(IntegerValue
a, IntegerValue
b) {
271 return IntegerValue(
CapProd(
a.value(),
b.value()));
274 IntegerValue CapSubI(IntegerValue
a, IntegerValue
b) {
275 return IntegerValue(
CapSub(
a.value(),
b.value()));
278 IntegerValue CapAddI(IntegerValue
a, IntegerValue
b) {
279 return IntegerValue(
CapAdd(
a.value(),
b.value()));
282 bool ProdOverflow(IntegerValue t, IntegerValue
value) {
292 IntegerValue
GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor,
293 IntegerValue max_magnitude) {
298 if (max_magnitude != 0) {
299 max_t = max_t / max_magnitude;
301 return rhs_remainder == 0
307 IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t,
308 IntegerValue max_scaling) {
309 DCHECK_GE(max_scaling, 1);
314 DCHECK_LT(rhs_remainder, divisor);
322 const IntegerValue size = divisor - rhs_remainder;
323 if (max_scaling == 1 || size == 1) {
327 return [t, divisor](IntegerValue coeff) {
330 }
else if (size <= max_scaling) {
331 return [size, rhs_remainder, t, divisor](IntegerValue coeff) {
332 const IntegerValue t_coeff = t * coeff;
335 const IntegerValue diff = remainder - rhs_remainder;
338 }
else if (max_scaling.value() * rhs_remainder.value() < divisor) {
348 return [t, divisor, max_scaling](IntegerValue coeff) {
349 const IntegerValue t_coeff = t * coeff;
352 const IntegerValue bucket =
FloorRatio(remainder * max_scaling, divisor);
353 return max_scaling *
ratio + bucket;
378 return [size, rhs_remainder, t, divisor, max_scaling](IntegerValue coeff) {
379 const IntegerValue t_coeff = t * coeff;
382 const IntegerValue diff = remainder - rhs_remainder;
383 const IntegerValue bucket =
384 diff > 0 ?
CeilRatio(diff * (max_scaling - 1), size)
386 return max_scaling *
ratio + bucket;
393 if (shared_stats_ ==
nullptr)
return;
394 std::vector<std::pair<std::string, int64_t>> stats;
395 stats.push_back({
"rounding_cut/num_initial_ibs_", total_num_initial_ibs_});
397 {
"rounding_cut/num_initial_merges_", total_num_initial_merges_});
398 stats.push_back({
"rounding_cut/num_pos_lifts", total_num_pos_lifts_});
399 stats.push_back({
"rounding_cut/num_neg_lifts", total_num_neg_lifts_});
401 {
"rounding_cut/num_post_complements", total_num_post_complements_});
402 stats.push_back({
"rounding_cut/num_overflows", total_num_overflow_abort_});
403 stats.push_back({
"rounding_cut/num_adjusts", total_num_coeff_adjust_});
404 stats.push_back({
"rounding_cut/num_merges", total_num_merges_});
405 stats.push_back({
"rounding_cut/num_bumps", total_num_bumps_});
407 {
"rounding_cut/num_final_complements", total_num_final_complements_});
408 stats.push_back({
"rounding_cut/num_dominating_f", total_num_dominating_f_});
412 double IntegerRoundingCutHelper::GetScaledViolation(
413 IntegerValue divisor, IntegerValue max_scaling,
414 IntegerValue remainder_threshold,
const CutData& cut) {
415 IntegerValue rhs =
cut.rhs;
416 IntegerValue max_magnitude =
cut.max_magnitude;
418 if (initial_rhs_remainder < remainder_threshold)
return 0.0;
436 adjusted_coeffs_.clear();
437 const IntegerValue adjust_threshold =
438 (divisor - initial_rhs_remainder - 1) /
439 IntegerValue(
std::max(1000,
cut.num_relevant_entries));
440 if (adjust_threshold > 0) {
444 double max_violation =
ToDouble(initial_rhs_remainder);
445 for (
int i = 0; i <
cut.num_relevant_entries; ++i) {
448 if (remainder == 0)
continue;
449 if (remainder <= initial_rhs_remainder) {
453 if (max_violation <= 1e-3)
return 0.0;
458 const IntegerValue adjust = divisor - remainder;
459 const IntegerValue prod = CapProdI(adjust, entry.
bound_diff);
460 if (prod <= adjust_threshold) {
462 const IntegerValue new_coeff = entry.
coeff + adjust;
463 adjusted_coeffs_.push_back({i, new_coeff});
471 const IntegerValue t =
GetFactorT(rhs_remainder, divisor, max_magnitude);
483 double max_violation = scaling *
ToDouble(rhs_remainder);
489 double violation = -
ToDouble(f(rhs));
491 int adjusted_coeffs_index = 0;
492 for (
int i = 0; i <
cut.num_relevant_entries; ++i) {
493 const CutTerm& entry =
cut.terms[i];
496 IntegerValue coeff = entry.coeff;
497 if (adjusted_coeffs_index < adjusted_coeffs_.size() &&
498 adjusted_coeffs_[adjusted_coeffs_index].first == i) {
499 coeff = adjusted_coeffs_[adjusted_coeffs_index].second;
500 adjusted_coeffs_index++;
503 if (coeff == 0)
continue;
504 const IntegerValue new_coeff = f(coeff);
505 const double new_coeff_double =
ToDouble(new_coeff);
506 const double lp_value = entry.lp_value;
511 l2_norm += new_coeff_double * new_coeff_double;
512 violation += new_coeff_double * lp_value;
513 max_violation -= (scaling *
ToDouble(coeff) - new_coeff_double) * lp_value;
514 if (max_violation <= 1e-3)
return 0.0;
516 if (
l2_norm == 0.0)
return 0.0;
526 return violation / sqrt(
l2_norm);
529 bool IntegerRoundingCutHelper::HasComplementedImpliedBound(
530 const CutTerm& entry, ImpliedBoundsProcessor* ib_processor) {
531 if (ib_processor ==
nullptr)
return false;
532 if (!entry.IsSimple())
return false;
533 if (entry.bound_diff == 1)
return false;
534 const ImpliedBoundsProcessor::BestImpliedBoundInfo info =
535 ib_processor->GetCachedImpliedBoundInfo(
536 entry.expr_coeffs[0] > 0 ?
NegationOf(entry.expr_vars[0])
537 : entry.expr_vars[0]);
550 const int old_size =
static_cast<int>(best_cut_.
terms.size());
552 for (
int i = 0; i < old_size; ++i) {
553 if (best_cut_.
terms[i].bound_diff <= 1)
continue;
554 if (!best_cut_.
terms[i].HasRelevantLpValue())
continue;
558 if (best_cut_.
terms[i].Complement(&best_cut_.
rhs)) {
561 true, &best_cut_, &cut_builder_)) {
562 ++total_num_initial_ibs_;
566 best_cut_.
terms[i].Complement(&best_cut_.
rhs);
572 true, &best_cut_, &cut_builder_)) {
574 ++total_num_initial_ibs_;
582 if (abort)
return false;
603 const IntegerValue remainder_threshold(
605 if (best_cut_.
rhs >= 0 && best_cut_.
rhs < remainder_threshold) {
618 if (magnitude <= remainder_threshold)
continue;
619 divisors_.push_back(magnitude);
621 if (divisors_.empty())
return false;
631 IntegerValue best_divisor(0);
632 double best_scaled_violation = 1e-3;
633 for (
const IntegerValue divisor : divisors_) {
636 const double violation = GetScaledViolation(divisor, options.
max_scaling,
637 remainder_threshold, best_cut_);
638 if (violation > best_scaled_violation) {
639 best_scaled_violation = violation;
640 best_adjusted_coeffs_ = adjusted_coeffs_;
641 best_divisor = divisor;
644 if (best_divisor == 0)
return false;
647 for (
int div = 2; div < 9; ++div) {
648 const IntegerValue divisor = best_divisor / IntegerValue(div);
649 if (divisor <= 1)
continue;
650 const double violation = GetScaledViolation(divisor, options.
max_scaling,
651 remainder_threshold, best_cut_);
652 if (violation > best_scaled_violation) {
653 best_scaled_violation = violation;
654 best_adjusted_coeffs_ = adjusted_coeffs_;
655 best_divisor = divisor;
662 if (entry.
coeff % best_divisor == 0)
continue;
667 const double violation = GetScaledViolation(
668 best_divisor, options.
max_scaling, remainder_threshold, best_cut_);
669 if (violation > best_scaled_violation) {
671 ++total_num_post_complements_;
672 best_scaled_violation = violation;
673 best_adjusted_coeffs_ = adjusted_coeffs_;
681 for (
const auto [
index, new_coeff] : best_adjusted_coeffs_) {
682 ++total_num_coeff_adjust_;
684 const IntegerValue remainder = new_coeff - entry.
coeff;
685 CHECK_GT(remainder, 0);
686 entry.
coeff = new_coeff;
695 const IntegerValue rhs_remainder =
697 IntegerValue factor_t =
711 const IntegerValue coeff = entry.
coeff;
713 if (r > rhs_remainder) remainders_.push_back(r);
716 if (remainders_.size() <= 100) {
718 for (
const IntegerValue r : remainders_) {
719 best_rs_.push_back(f(r));
721 IntegerValue best_d = f(best_divisor);
726 for (
const IntegerValue t :
729 for (IntegerValue s(2); s <= options.
max_scaling; ++s) {
732 int num_strictly_better = 0;
734 const IntegerValue d = g(best_divisor);
735 for (
int i = 0; i < best_rs_.size(); ++i) {
736 const IntegerValue temp = g(remainders_[i]);
737 if (temp * best_d < best_rs_[i] * d)
break;
738 if (temp * best_d > best_rs_[i] * d) num_strictly_better++;
741 if (rs_.size() == best_rs_.size() && num_strictly_better > 0) {
742 ++total_num_dominating_f_;
755 if (ib_processor !=
nullptr) {
757 const int old_size = best_cut_.
terms.size();
758 for (
int i = 0; i < old_size; ++i) {
766 factor_t, i,
false, &best_cut_, &cut_builder_)) {
768 ++total_num_pos_lifts_;
790 if (!HasComplementedImpliedBound(term, ib_processor))
continue;
798 if (ProdOverflow(factor_t, CapProdI(term.
coeff, bound_diff))) {
806 const IntegerValue coeff_b =
807 f(term.
coeff * bound_diff) + f(-term.
coeff) * bound_diff;
808 CHECK_LE(coeff_b, 0);
812 if (lp2 > lp1 + 1e-2) {
843 ++total_num_neg_lifts_;
853 for (
int i = 0; i < 3; ++i) {
854 const int64_t saved = total_num_final_complements_;
872 if (entry.
coeff % best_divisor == 0)
continue;
877 if (ProdOverflow(factor_t, prod))
continue;
878 if (ProdOverflow(factor_t, CapSubI(best_cut_.
rhs, prod)))
continue;
882 const double lp2 =
ToDouble(f(best_cut_.
rhs - prod)) -
885 if (lp2 + 1e-2 < lp1) {
887 ++total_num_final_complements_;
890 if (total_num_final_complements_ == saved)
break;
907 bool improved =
false;
908 const IntegerValue rhs = best_cut_.
rhs;
909 const IntegerValue f_rhs = f(best_cut_.
rhs);
910 best_cut_.
rhs = f_rhs;
912 const IntegerValue f_coeff = f(entry.
coeff);
914 !ProdOverflow(factor_t, CapSubI(rhs, entry.
coeff))) {
915 const IntegerValue alternative = f_rhs - f(rhs - entry.
coeff);
916 DCHECK_GE(alternative, f_coeff);
917 if (alternative > f_coeff) {
920 entry.
coeff = alternative;
924 entry.
coeff = f_coeff;
928 ++total_num_overflow_abort_;
936 if (shared_stats_ ==
nullptr)
return;
937 std::vector<std::pair<std::string, int64_t>> stats;
938 stats.push_back({
"cover_cut/num_overflows", total_num_overflow_abort_});
939 stats.push_back({
"cover_cut/num_lifting", total_num_lifting_});
940 stats.push_back({
"cover_cut/num_implied_bounds", total_num_ibs_});
946 int CoverCutHelper::GetCoverSize(
int relevant_size, IntegerValue* rhs) {
947 if (relevant_size == 0)
return 0;
952 const double threshold = 1.0 /
static_cast<double>(relevant_size);
953 for (
int i = 0; i < relevant_size;) {
954 const double dist = base_ct_.
terms[i].LpDistToMaxValue();
955 if (dist < threshold) {
960 }
else if (dist < 0.9999) {
969 std::sort(base_ct_.
terms.begin() + part1,
970 base_ct_.
terms.begin() + relevant_size,
971 [](
const CutTerm&
a,
const CutTerm&
b) {
972 const double dist_a = a.LpDistToMaxValue();
973 const double dist_b = b.LpDistToMaxValue();
974 if (dist_a == dist_b) {
976 return a.coeff < b.coeff;
978 return dist_a < dist_b;
981 double activity = 0.0;
982 int cover_size = relevant_size;
984 for (
int i = 0; i < relevant_size; ++i) {
985 const CutTerm& term = base_ct_.terms[i];
995 if (activity > 0.9999) {
1000 if (!
AddProductTo(-term.coeff, term.bound_diff, rhs)) {
1016 if (*rhs >= 0)
return 0;
1017 if (cover_size == 0)
return 0;
1023 std::sort(base_ct_.terms.begin(), base_ct_.terms.begin() + cover_size,
1024 [](
const CutTerm&
a,
const CutTerm&
b) {
1025 if (a.coeff == b.coeff) {
1026 return a.LpDistToMaxValue() > b.LpDistToMaxValue();
1028 return a.coeff >
b.coeff;
1030 for (
int i = 0; i < cover_size; ++i) {
1031 const CutTerm& t = base_ct_.terms[i];
1032 if (t.bound_diff * t.coeff + *rhs >= 0)
continue;
1033 *rhs += t.bound_diff * t.coeff;
1034 std::swap(base_ct_.terms[i], base_ct_.terms[--cover_size]);
1036 DCHECK_GT(cover_size, 0);
1041 bool CoverCutHelper::MakeAllTermsPositive(
CutData* cut) {
1047 if (term.
coeff >= 0)
continue;
1049 ++total_num_overflow_abort_;
1055 CHECK_GE(cut->
rhs, 0);
1064 if (ib_processor !=
nullptr) {
1065 cut_builder_.ClearIndices();
1066 const int old_size =
static_cast<int>(base_ct_.terms.size());
1067 for (
int i = 0; i < old_size; ++i) {
1070 const CutTerm& term = base_ct_.terms[i];
1078 false, &base_ct_, &cut_builder_)) {
1085 const int base_size =
static_cast<int>(base_ct_.terms.size());
1086 const int cover_size = GetCoverSize(base_size, &rhs);
1087 if (cover_size == 0)
return false;
1090 base_ct_.rhs = IntegerValue(-1);
1091 IntegerValue max_coeff(0);
1092 for (
int i = 0; i < cover_size; ++i) {
1093 max_coeff =
std::max(max_coeff, base_ct_.terms[i].coeff);
1094 base_ct_.terms[i].coeff = IntegerValue(1);
1095 base_ct_.rhs += base_ct_.terms[i].bound_diff;
1097 CHECK_GT(max_coeff, 0);
1103 if (max_coeff < -rhs) {
1104 const IntegerValue m =
FloorRatio(-rhs - 1, max_coeff);
1105 rhs += max_coeff * m;
1110 IntegerValue max_base_magnitude = max_coeff;
1112 for (
int i = cover_size; i < base_size; ++i) {
1113 max_base_magnitude =
std::max(max_base_magnitude, base_ct_.terms[i].coeff);
1115 const IntegerValue max_scaling(
std::min(
1137 const IntegerValue slack = -rhs;
1138 const IntegerValue remainder = max_coeff - slack;
1140 IntegerValue(1), max_scaling);
1142 const IntegerValue scaling = f(max_coeff);
1144 for (
int i = 0; i < cover_size; ++i) {
1145 base_ct_.terms[i].coeff *= scaling;
1147 base_ct_.rhs *= scaling;
1151 for (
int i = cover_size; i < base_size; ++i) {
1152 const IntegerValue positive_coeff = base_ct_.terms[i].coeff;
1153 const IntegerValue new_coeff = f(positive_coeff);
1154 base_ct_.terms[i].coeff = new_coeff;
1155 if (new_coeff != 0) ++num_lifting_;
1157 total_num_lifting_ += num_lifting_;
1159 if (!cut_builder_.ConvertToLinearConstraint(base_ct_, &cut_)) {
1161 ++total_num_overflow_abort_;
1168 bool CoverCutHelper::TryWithLetchfordSouliLifting(
1177 if (ib_processor !=
nullptr) {
1178 cut_builder_.ClearIndices();
1179 const int old_size =
static_cast<int>(base_ct_.terms.size());
1180 for (
int i = 0; i < old_size; ++i) {
1181 if (base_ct_.terms[i].bound_diff <= 1)
continue;
1184 false, &base_ct_, &cut_builder_)) {
1191 const int num_bools =
1192 std::partition(base_ct_.terms.begin(), base_ct_.terms.end(),
1193 [](
const CutTerm& t) { return t.bound_diff == 1; }) -
1194 base_ct_.terms.begin();
1195 if (num_bools == 0)
return false;
1198 const int cover_size = GetCoverSize(num_bools, &rhs);
1199 if (cover_size == 0)
return false;
1202 IntegerValue sum(0);
1203 std::vector<IntegerValue> cover_weights;
1204 for (
int i = 0; i < cover_size; ++i) {
1205 CHECK_EQ(base_ct_.terms[i].bound_diff, 1);
1206 CHECK_GT(base_ct_.terms[i].coeff, 0);
1207 cover_weights.push_back(base_ct_.terms[i].coeff);
1208 sum = CapAddI(sum, base_ct_.terms[i].coeff);
1211 ++total_num_overflow_abort_;
1214 CHECK_GT(sum, base_ct_.rhs);
1220 IntegerValue previous_sum(0);
1221 std::sort(cover_weights.begin(), cover_weights.end());
1222 for (
int i = 0; i < cover_size; ++i) {
1223 q = IntegerValue(cover_weights.size() - i);
1224 if (previous_sum + cover_weights[i] * q > base_ct_.rhs) {
1225 p = base_ct_.rhs - previous_sum;
1228 previous_sum += cover_weights[i];
1235 std::vector<IntegerValue> thresholds;
1236 for (
int i = 0; i < q; ++i) {
1239 ++total_num_overflow_abort_;
1242 thresholds.push_back(
CeilRatio(p * (i + 1) + 1, q));
1246 std::reverse(cover_weights.begin(), cover_weights.end());
1247 for (
int i = q.value(); i < cover_size; ++i) {
1248 thresholds.push_back(thresholds.back() + cover_weights[i]);
1250 CHECK_EQ(thresholds.back(), base_ct_.rhs + 1);
1260 temp_cut_.rhs = IntegerValue(cover_size - 1);
1261 temp_cut_.terms.clear();
1264 const int base_size =
static_cast<int>(base_ct_.terms.size());
1265 for (
int i = 0; i < base_size; ++i) {
1266 const CutTerm& term = base_ct_.terms[i];
1267 const IntegerValue coeff = term.
coeff;
1268 IntegerValue cut_coeff(1);
1269 if (coeff < thresholds[0]) {
1270 if (i >= cover_size)
continue;
1276 for (
int i = 1; i < cover_size; ++i) {
1277 if (coeff < thresholds[i])
break;
1278 cut_coeff = IntegerValue(i + 1);
1280 if (cut_coeff != 0 && i >= cover_size) ++num_lifting_;
1281 if (cut_coeff > 1 && i < cover_size) ++num_lifting_;
1284 temp_cut_.terms.push_back(term);
1285 temp_cut_.terms.back().coeff = cut_coeff;
1287 if (!cut_builder_.ConvertToLinearConstraint(temp_cut_, &cut_)) {
1289 ++total_num_overflow_abort_;
1298 int linearization_level,
1309 [z, x, y, linearization_level,
model, trail, integer_trail](
1312 if (trail->CurrentDecisionLevel() > 0 && linearization_level == 1) {
1321 if (x_lb == x_ub || y_lb == y_ub)
return true;
1326 const int64_t x_max_amp =
std::max(std::abs(x_lb), std::abs(x_ub));
1327 const int64_t y_max_amp =
std::max(std::abs(y_lb), std::abs(y_ub));
1328 constexpr int64_t kMaxSafeInteger = (int64_t{1} << 53) - 1;
1329 if (
CapProd(y_max_amp, x_max_amp) > kMaxSafeInteger)
return true;
1339 const double x_lp_value = x.
LpValue(lp_values);
1340 const double y_lp_value = y.
LpValue(lp_values);
1341 const double z_lp_value = z.
LpValue(lp_values);
1349 auto try_add_above_cut = [&](int64_t x_coeff, int64_t y_coeff,
1351 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff >=
1352 rhs + kMinCutViolation) {
1355 cut.
AddTerm(z, IntegerValue(-1));
1356 if (x_coeff != 0) cut.
AddTerm(x, IntegerValue(x_coeff));
1357 if (y_coeff != 0) cut.
AddTerm(y, IntegerValue(y_coeff));
1358 manager->AddCut(cut.
Build(),
"PositiveProduct", lp_values);
1363 auto try_add_below_cut = [&](int64_t x_coeff, int64_t y_coeff,
1365 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff <=
1366 rhs - kMinCutViolation) {
1369 cut.
AddTerm(z, IntegerValue(-1));
1370 if (x_coeff != 0) cut.
AddTerm(x, IntegerValue(x_coeff));
1371 if (y_coeff != 0) cut.
AddTerm(y, IntegerValue(y_coeff));
1372 manager->AddCut(cut.
Build(),
"PositiveProduct", lp_values);
1383 try_add_above_cut(y_lb, x_lb, x_lb * y_lb);
1384 try_add_above_cut(y_ub, x_ub, x_ub * y_ub);
1385 try_add_below_cut(y_ub, x_lb, x_lb * y_ub);
1386 try_add_below_cut(y_lb, x_ub, x_ub * y_lb);
1397 const IntegerValue above_slope = x_ub + x_lb;
1400 above_hyperplan.
AddTerm(square, 1);
1401 above_hyperplan.
AddTerm(x, IntegerValue(-above_slope));
1402 return above_hyperplan.
Build();
1407 IntegerValue x_value,
1409 const IntegerValue below_slope = 2 * x_value + 1;
1412 below_hyperplan.
AddTerm(square, 1);
1413 below_hyperplan.
AddTerm(x, -below_slope);
1414 return below_hyperplan.
Build();
1426 [y, x, linearization_level, trail, integer_trail,
model](
1432 const IntegerValue x_ub = integer_trail->LevelZeroUpperBound(x);
1433 const IntegerValue x_lb = integer_trail->LevelZeroLowerBound(x);
1436 if (x_lb == x_ub)
return true;
1439 if (x_ub > (int64_t{1} << 31))
return true;
1442 "SquareUpper", lp_values);
1444 const IntegerValue x_floor =
1445 static_cast<int64_t
>(std::floor(x.
LpValue(lp_values)));
1447 "SquareLower", lp_values);
1454 ImpliedBoundsProcessor::BestImpliedBoundInfo
1455 ImpliedBoundsProcessor::GetCachedImpliedBoundInfo(IntegerVariable
var)
const {
1456 auto it = cache_.find(
var);
1457 if (it != cache_.end()) {
1467 ImpliedBoundsProcessor::ComputeBestImpliedBound(
1468 IntegerVariable
var,
1470 auto it = cache_.find(
var);
1471 if (it != cache_.end())
return it->second;
1472 BestImpliedBoundInfo result;
1473 double result_slack_lp_value = std::numeric_limits<double>::infinity();
1474 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1476 implied_bounds_->GetImpliedBounds(
var)) {
1488 const IntegerValue diff = entry.lower_bound - lb;
1490 const double bool_lp_value = entry.is_positive
1491 ? lp_values[entry.literal_view]
1492 : 1.0 - lp_values[entry.literal_view];
1493 const double slack_lp_value =
1498 if (slack_lp_value < -1e-4) {
1499 LinearConstraint ib_cut;
1501 std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
1502 if (entry.is_positive) {
1504 terms.push_back({entry.literal_view, diff});
1505 terms.push_back({
var, IntegerValue(-1)});
1509 terms.push_back({entry.literal_view, -diff});
1510 terms.push_back({
var, IntegerValue(-1)});
1511 ib_cut.ub = -entry.lower_bound;
1514 ib_cut_pool_.AddCut(std::move(ib_cut),
"IB", lp_values);
1520 if (slack_lp_value + 1e-4 < result_slack_lp_value ||
1521 (slack_lp_value < result_slack_lp_value + 1e-4 &&
1522 entry.lower_bound > result.implied_bound)) {
1523 result_slack_lp_value = slack_lp_value;
1524 result.var_lp_value = lp_values[
var];
1525 result.bool_lp_value = bool_lp_value;
1526 result.implied_bound = entry.lower_bound;
1527 result.is_positive = entry.is_positive;
1528 result.bool_var = entry.literal_view;
1531 cache_[
var] = result;
1535 void ImpliedBoundsProcessor::RecomputeCacheAndSeparateSomeImpliedBoundCuts(
1538 for (
const IntegerVariable
var :
1539 implied_bounds_->VariablesWithImpliedBounds()) {
1541 ComputeBestImpliedBound(
var, lp_values);
1546 bool ImpliedBoundsProcessor::TryToExpandWithLowerImpliedbound(
1547 IntegerValue factor_t,
int i,
bool complement,
CutData* cut,
1553 if (!term.
IsSimple())
return false;
1574 const IntegerVariable ib_var = term.
expr_coeffs[0] > 0
1578 GetCachedImpliedBoundInfo(ib_var);
1581 if (bound_diff <= 0)
return false;
1583 if (ProdOverflow(factor_t, CapProdI(term.
coeff, bound_diff)))
return false;
1629 0.5 *
static_cast<double>(slack_term.
bound_diff.value())) {
1639 std::string SingleNodeFlow::DebugString()
const {
1640 return absl::StrCat(
"#in:", in_flow.size(),
" #out:", out_flow.size(),
1641 " demand:",
demand.value(),
" #bool:", num_bool,
1642 " #lb:", num_to_lb,
" #ub:", num_to_ub);
1645 bool FlowCoverCutHelper::TryXminusLB(IntegerVariable
var,
double lp_value,
1646 IntegerValue lb, IntegerValue ub,
1676 result->
demand -= coeff * lb;
1678 result->
in_flow.push_back(info);
1685 bool FlowCoverCutHelper::TryUBminusX(IntegerVariable
var,
double lp_value,
1686 IntegerValue lb, IntegerValue ub,
1688 ImpliedBoundsProcessor* ib_helper,
1689 SingleNodeFlow* result)
const {
1690 const ImpliedBoundsProcessor::BestImpliedBoundInfo ib =
1691 ib_helper->GetCachedImpliedBoundInfo(
var);
1693 if (ib.implied_bound != ub)
return false;
1699 if (ib.is_positive) {
1701 info.bool_expr.var = ib.bool_var;
1702 info.bool_expr.coeff = -1;
1703 info.bool_expr.constant = 1;
1705 info.bool_lp_value = ib.bool_lp_value;
1706 info.bool_expr.var = ib.bool_var;
1707 info.bool_expr.coeff = 1;
1709 info.capacity =
IntTypeAbs(coeff) * (ub - lb);
1711 info.flow_expr.var =
var;
1713 info.flow_expr.constant = ub *
IntTypeAbs(coeff);
1717 result->demand -= coeff * ub;
1719 result->out_flow.push_back(info);
1721 result->in_flow.push_back(info);
1726 bool FlowCoverCutHelper::ComputeFlowCoverRelaxationAndGenerateCut(
1730 if (!ComputeFlowCoverRelaxation(base_ct, lp_values, &snf_, integer_trail,
1734 return GenerateCut(snf_);
1737 bool FlowCoverCutHelper::ComputeFlowCoverRelaxation(
1744 const int size = base_ct.
vars.size();
1745 for (
int i = 0; i < size; ++i) {
1747 const IntegerVariable
var = base_ct.
vars[i];
1748 const IntegerValue coeff = base_ct.
coeffs[i];
1751 if (
IntTypeAbs(coeff) > 1'000'000)
return false;
1760 snf->
demand -= coeff * lb;
1768 info.bool_lp_value = (lp_values[
var] -
ToDouble(lb));
1770 info.bool_expr.var =
var;
1771 info.bool_expr.coeff = 1;
1772 info.bool_expr.constant = -lb;
1775 info.flow_expr.var =
var;
1776 info.flow_expr.coeff = info.capacity;
1777 info.flow_expr.constant = -lb * info.capacity;
1780 snf->
demand -= coeff * lb;
1792 const double lp = lp_values[
var];
1795 if (TryXminusLB(
var, lp, lb, ub, coeff, ib_helper, snf)) {
1799 if (TryUBminusX(
var, lp, lb, ub, coeff, ib_helper, snf)) {
1804 if (TryUBminusX(
var, lp, lb, ub, coeff, ib_helper, snf)) {
1808 if (TryXminusLB(
var, lp, lb, ub, coeff, ib_helper, snf)) {
1825 if (data.
empty())
return false;
1826 const double tolerance = 1e-2;
1832 std::vector<bool> in_cover(data.
in_flow.size(),
false);
1833 std::vector<bool> out_cover(data.
out_flow.size(),
false);
1839 IntegerValue sum_in = 0;
1840 IntegerValue sum_out = 0;
1841 for (
int i = 0; i < data.
in_flow.size(); ++i) {
1848 for (
int i = 0; i < data.
out_flow.size(); ++i) {
1851 out_cover[i] =
true;
1857 slack = sum_in - sum_out - data.
demand;
1859 if (slack <= 0)
return false;
1868 bool correspond_to_in_flow;
1872 std::vector<Item> actions;
1873 for (
int i = 0; i < data.
in_flow.size(); ++i) {
1874 if (!in_cover[i])
continue;
1880 for (
int i = 0; i < data.
out_flow.size(); ++i) {
1881 if (out_cover[i])
continue;
1889 std::sort(actions.begin(), actions.end(),
1890 [](
const Item&
a,
const Item&
b) { return a.score > b.score; });
1893 for (
const Item& item : actions) {
1894 if (item.correspond_to_in_flow) {
1895 const IntegerValue
delta = data.
in_flow[item.index].capacity;
1896 if (
delta >= slack)
continue;
1898 in_cover[item.index] =
false;
1900 const IntegerValue
delta = data.
out_flow[item.index].capacity;
1901 if (
delta >= slack)
continue;
1903 out_cover[item.index] =
true;
1921 num_in_ignored_ = 0;
1928 cut_builder_.Clear();
1929 for (
int i = 0; i < data.
in_flow.size(); ++i) {
1936 cut_builder_.AddTerm(info.
flow_expr, -1);
1939 const IntegerValue coeff = info.
capacity - slack;
1940 cut_builder_.AddConstant(-coeff);
1941 cut_builder_.AddTerm(info.
bool_expr, coeff);
1944 for (
int i = 0; i < data.
out_flow.size(); ++i) {
1948 cut_builder_.AddConstant(info.
capacity);
1949 }
else if (info.
capacity > slack) {
1951 cut_builder_.AddTerm(info.
bool_expr, slack);
1954 cut_builder_.AddTerm(info.
flow_expr, 1);
1963 void SumOfAllDiffLowerBounder::Clear() {
1964 min_values_.clear();
1972 if (integer_trail.
IsFixed(expr)) {
1973 min_values_.insert(integer_trail.
FixedValue(expr));
1975 if (expr.
coeff > 0) {
1977 for (
const IntegerValue
value :
1980 if (++count >= num_exprs)
break;
1984 for (
const IntegerValue
value :
1987 if (++count >= num_exprs)
break;
1993 IntegerValue SumOfAllDiffLowerBounder::SumOfMinDomainValues() {
1995 IntegerValue sum = 0;
1996 for (
const IntegerValue
value : min_values_) {
1998 if (++count >= expr_mins_.size())
return sum;
2003 IntegerValue SumOfAllDiffLowerBounder::SumOfDifferentMins() {
2004 std::sort(expr_mins_.begin(), expr_mins_.end());
2006 IntegerValue result = 0;
2007 for (
const IntegerValue
value : expr_mins_) {
2010 result += tmp_value;
2015 IntegerValue SumOfAllDiffLowerBounder::GetBestLowerBound(std::string& suffix) {
2016 const IntegerValue domain_bound = SumOfMinDomainValues();
2017 const IntegerValue alldiff_bound = SumOfDifferentMins();
2018 if (domain_bound > alldiff_bound) {
2020 return domain_bound;
2022 suffix = alldiff_bound > domain_bound ?
"a" :
"e";
2023 return alldiff_bound;
2028 void TryToGenerateAllDiffCut(
2029 const std::vector<std::pair<double, AffineExpression>>& sorted_exprs_lp,
2033 const int num_exprs = sorted_exprs_lp.size();
2035 std::vector<AffineExpression> current_set_exprs;
2041 for (
const auto& [expr_lp, expr] : sorted_exprs_lp) {
2043 diff_mins.
Add(expr, num_exprs, integer_trail);
2044 negated_diff_maxes.
Add(expr.Negated(), num_exprs, integer_trail);
2045 current_set_exprs.push_back(expr);
2046 CHECK_EQ(current_set_exprs.size(), diff_mins.
size());
2047 CHECK_EQ(current_set_exprs.size(), negated_diff_maxes.
size());
2048 std::string min_suffix;
2049 const IntegerValue required_min_sum =
2051 std::string max_suffix;
2052 const IntegerValue required_max_sum =
2054 if (sum <
ToDouble(required_min_sum) - kMinCutViolation ||
2055 sum >
ToDouble(required_max_sum) + kMinCutViolation) {
2058 cut.AddTerm(expr, IntegerValue(1));
2060 top_n_cuts.
AddCut(cut.Build(),
2061 absl::StrCat(
"AllDiff_", min_suffix, max_suffix),
2066 current_set_exprs.clear();
2068 negated_diff_maxes.
Clear();
2076 const std::vector<AffineExpression>& exprs,
Model*
model) {
2081 if (!integer_trail->
IsFixed(expr)) {
2082 result.
vars.push_back(expr.var);
2089 [exprs, integer_trail, trail,
model](
2096 std::vector<std::pair<double, AffineExpression>> sorted_exprs;
2102 sorted_exprs.push_back(std::make_pair(expr.LpValue(lp_values), expr));
2106 std::sort(sorted_exprs.begin(), sorted_exprs.end(),
2107 [](std::pair<double, AffineExpression>&
a,
2108 const std::pair<double, AffineExpression>&
b) {
2109 return a.first < b.first;
2111 TryToGenerateAllDiffCut(sorted_exprs, *integer_trail, lp_values,
2114 std::reverse(sorted_exprs.begin(), sorted_exprs.end());
2115 TryToGenerateAllDiffCut(sorted_exprs, *integer_trail, lp_values,
2120 VLOG(2) <<
"Created all_diff cut generator of size: " << exprs.size();
2126 IntegerValue MaxCornerDifference(
const IntegerVariable
var,
2127 const IntegerValue w1_i,
2128 const IntegerValue w2_i,
2129 const IntegerTrail& integer_trail) {
2130 const IntegerValue lb = integer_trail.LevelZeroLowerBound(
var);
2131 const IntegerValue ub = integer_trail.LevelZeroUpperBound(
var);
2132 return std::max((w2_i - w1_i) * lb, (w2_i - w1_i) * ub);
2141 IntegerValue MPlusCoefficient(
2142 const std::vector<IntegerVariable>& x_vars,
2143 const std::vector<LinearExpression>& exprs,
2145 const int max_index,
const IntegerTrail& integer_trail) {
2146 IntegerValue coeff = exprs[max_index].offset;
2149 for (
const IntegerVariable
var : x_vars) {
2150 const int target_index = variable_partition[
var];
2151 if (max_index != target_index) {
2152 coeff += MaxCornerDifference(
2163 double ComputeContribution(
2164 const IntegerVariable xi_var,
const std::vector<IntegerVariable>& z_vars,
2165 const std::vector<LinearExpression>& exprs,
2167 const IntegerTrail& integer_trail,
const int target_index) {
2168 CHECK_GE(target_index, 0);
2169 CHECK_LT(target_index, exprs.size());
2170 const LinearExpression& target_expr = exprs[target_index];
2171 const double xi_value = lp_values[xi_var];
2173 double contrib =
ToDouble(wt_i) * xi_value;
2174 for (
int expr_index = 0; expr_index < exprs.size(); ++expr_index) {
2175 if (expr_index == target_index)
continue;
2176 const LinearExpression& max_expr = exprs[expr_index];
2177 const double z_max_value = lp_values[z_vars[expr_index]];
2178 const IntegerValue corner_value = MaxCornerDifference(
2181 contrib +=
ToDouble(corner_value) * z_max_value;
2188 const IntegerVariable target,
const std::vector<LinearExpression>& exprs,
2189 const std::vector<IntegerVariable>& z_vars,
Model*
model) {
2191 std::vector<IntegerVariable> x_vars;
2192 result.
vars = {target};
2193 const int num_exprs = exprs.size();
2194 for (
int i = 0; i < num_exprs; ++i) {
2195 result.
vars.push_back(z_vars[i]);
2196 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
2200 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
2201 return VariableIsPositive(var);
2203 result.
vars.insert(result.
vars.end(), x_vars.begin(), x_vars.end());
2207 [x_vars, z_vars, target, num_exprs, exprs, integer_trail,
model](
2211 lp_values.size(), -1);
2213 lp_values.size(), std::numeric_limits<double>::infinity());
2214 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
2215 for (
const IntegerVariable
var : x_vars) {
2216 const double contribution = ComputeContribution(
2217 var, z_vars, exprs, lp_values, *integer_trail, expr_index);
2218 const double prev_contribution = variable_partition_contrib[
var];
2219 if (contribution < prev_contribution) {
2220 variable_partition[
var] = expr_index;
2221 variable_partition_contrib[
var] = contribution;
2228 double violation = lp_values[target];
2229 cut.
AddTerm(target, IntegerValue(-1));
2231 for (
const IntegerVariable xi_var : x_vars) {
2232 const int input_index = variable_partition[xi_var];
2235 if (coeff != IntegerValue(0)) {
2238 violation -=
ToDouble(coeff) * lp_values[xi_var];
2240 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
2241 const IntegerVariable z_var = z_vars[expr_index];
2242 const IntegerValue z_coeff = MPlusCoefficient(
2243 x_vars, exprs, variable_partition, expr_index, *integer_trail);
2244 if (z_coeff != IntegerValue(0)) {
2247 violation -=
ToDouble(z_coeff) * lp_values[z_var];
2249 if (violation > 1e-2) {
2250 manager->AddCut(cut.
Build(),
"LinMax", lp_values);
2259 IntegerValue EvaluateMaxAffine(
2260 const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
2263 for (
const auto& p : affines) {
2264 y =
std::max(y, x * p.first + p.second);
2273 const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
2277 const IntegerValue x_max = integer_trail->LevelZeroUpperBound(
var);
2279 const IntegerValue y_at_min = EvaluateMaxAffine(affines, x_min);
2280 const IntegerValue y_at_max = EvaluateMaxAffine(affines, x_max);
2282 const IntegerValue delta_x = x_max - x_min;
2283 const IntegerValue delta_y = y_at_max - y_at_min;
2301 VLOG(2) <<
"Linear constraint can cause overflow: " << builder->
Build();
2311 std::vector<std::pair<IntegerValue, IntegerValue>> affines,
2320 [target,
var, affines, cut_name, integer_trail,
model](
2323 if (integer_trail->
IsFixed(
var))
return true;
2326 manager->AddCut(builder.
Build(), cut_name, lp_values);
2334 const std::vector<IntegerVariable>& base_variables,
Model*
model) {
2337 std::vector<IntegerVariable> variables;
2338 std::vector<Literal> literals;
2339 absl::flat_hash_map<LiteralIndex, IntegerVariable> positive_map;
2340 absl::flat_hash_map<LiteralIndex, IntegerVariable> negative_map;
2343 for (
const IntegerVariable
var : base_variables) {
2344 if (integer_trail->LowerBound(
var) != IntegerValue(0))
continue;
2345 if (integer_trail->UpperBound(
var) != IntegerValue(1))
continue;
2346 const LiteralIndex literal_index = encoder->GetAssociatedLiteral(
2349 variables.push_back(
var);
2350 literals.push_back(
Literal(literal_index));
2351 positive_map[literal_index] =
var;
2356 result.
vars = variables;
2359 [variables, literals, implication_graph, positive_map, negative_map,
2362 std::vector<double> packed_values;
2363 for (
int i = 0; i < literals.size(); ++i) {
2364 packed_values.push_back(lp_values[variables[i]]);
2366 const std::vector<std::vector<Literal>> at_most_ones =
2367 implication_graph->GenerateAtMostOnesWithLargeWeight(literals,
2370 for (
const std::vector<Literal>& at_most_one : at_most_ones) {
2377 for (
const Literal l : at_most_one) {
2378 if (positive_map.contains(l.Index())) {
2379 builder.
AddTerm(positive_map.at(l.Index()), IntegerValue(1));
2382 builder.
AddTerm(negative_map.at(l.Index()), IntegerValue(-1));
2387 manager->AddCut(builder.
Build(),
"Clique", lp_values);
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
DomainIteratorBeginEnd Values() const &
bool ConvertToLinearConstraint(const CutData &cut, LinearConstraint *output)
void AddOrMergeTerm(const CutTerm &term, IntegerValue t, CutData *cut)
int NumMergesSinceLastClear() const
BestImpliedBoundInfo GetCachedImpliedBoundInfo(IntegerVariable var) const
bool TryToExpandWithLowerImpliedbound(IntegerValue factor_t, int i, bool complement, CutData *cut, CutDataBuilder *builder)
const LinearConstraint & cut() const
bool ComputeCut(RoundingOptions options, const CutData &base_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
~IntegerRoundingCutHelper()
bool IsFixed(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue FixedValue(IntegerVariable i) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
const Domain & InitialVariableDomain(IntegerVariable var) const
void AddConstant(IntegerValue value)
void AddLinearExpression(const LinearExpression &expr)
void ResetBounds(IntegerValue lb, IntegerValue ub)
void AddTerm(IntegerVariable var, IntegerValue coeff)
LiteralIndex NegatedIndex() const
Class that owns everything related to a particular optimization model.
void AddStats(absl::Span< const std::pair< std::string, int64_t >> stats)
IntegerValue GetBestLowerBound(std::string &suffix)
void Add(const AffineExpression &expr, int num_expr, const IntegerTrail &integer_trail)
void AddCut(LinearConstraint ct, const std::string &name, const absl::StrongVector< IntegerVariable, double > &lp_solution)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
int CurrentDecisionLevel() const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
static double ToDouble(double f)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
LinearConstraint ComputeHyperplanBelowSquare(AffineExpression x, AffineExpression square, IntegerValue x_value, Model *model)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< AffineExpression > &exprs, Model *model)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue >> affines, const std::string cut_name, Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
IntegerValue PositiveRemainder(IntegerValue dividend, IntegerValue positive_divisor)
CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z, AffineExpression x, AffineExpression y, int linearization_level, Model *model)
bool BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue >> &affines, Model *model, LinearConstraintBuilder *builder)
IntegerValue GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue max_magnitude)
std::function< IntegerValue(IntegerValue)> GetSuperAdditiveRoundingFunction(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t, IntegerValue max_scaling)
CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x, int linearization_level, Model *model)
LinearConstraint ComputeHyperplanAboveSquare(AffineExpression x, AffineExpression square, IntegerValue x_lb, IntegerValue x_ub, Model *model)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var, const LinearExpression &expr)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue >> *terms, ClassWithVarsAndCoeffs *output)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
void DivideByGCD(LinearConstraint *constraint)
double ToDouble(IntegerValue value)
Collection of objects used to extend the Constraint Solver library.
bool AtMinOrMaxInt64(int64_t x)
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
static int input(yyscan_t yyscanner)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
IntegerValue ValueAt(IntegerValue var_value) const
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const
std::vector< CutTerm > terms
IntegerValue max_magnitude
bool FillFromLinearConstraint(const LinearConstraint &base_ct, const absl::StrongVector< IntegerVariable, double > &lp_values, IntegerTrail *integer_trail)
bool FillFromParallelVectors(const LinearConstraint &base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
bool AppendOneTerm(IntegerVariable var, IntegerValue coeff, double lp_value, IntegerValue lb, IntegerValue ub)
std::vector< IntegerVariable > vars
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
double LpDistToMaxValue() const
bool HasRelevantLpValue() const
std::string DebugString() const
std::array< IntegerVariable, 2 > expr_vars
bool Complement(IntegerValue *rhs)
std::array< IntegerValue, 2 > expr_coeffs
AffineExpression flow_expr
AffineExpression bool_expr
IntegerValue implied_bound
double SlackLpValue(IntegerValue lb) const
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
std::vector< IntegerVariable > vars
bool use_ib_before_heuristic
std::vector< FlowInfo > out_flow
std::vector< FlowInfo > in_flow
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)