27#include "absl/container/btree_set.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/memory/memory.h"
31#include "absl/meta/type_traits.h"
55const double kMinCutViolation = 1e-4;
58double GetLiteralLpValue(
61 const IntegerEncoder* encoder) {
62 const IntegerVariable direct_view = encoder->GetLiteralView(lit);
64 return lp_values[direct_view];
66 const IntegerVariable opposite_view = encoder->GetLiteralView(lit.Negated());
68 return 1.0 - lp_values[opposite_view];
74LinearConstraint GenerateKnapsackCutForCover(
75 const std::vector<IntegerVariable>& vars,
76 const std::vector<IntegerValue>& coeffs,
const IntegerValue
upper_bound,
77 const IntegerTrail& integer_trail) {
78 CHECK_EQ(vars.size(), coeffs.size());
81 IntegerValue cut_upper_bound = IntegerValue(0);
82 IntegerValue max_coeff = coeffs[0];
85 for (
int i = 0; i < vars.size(); ++i) {
86 const IntegerValue var_upper_bound =
87 integer_trail.LevelZeroUpperBound(vars[i]);
88 cut_upper_bound += var_upper_bound;
89 cut.vars.push_back(vars[i]);
90 cut.coeffs.push_back(IntegerValue(1));
91 max_coeff =
std::max(max_coeff, coeffs[i]);
92 slack += coeffs[i] * var_upper_bound;
94 CHECK_GT(slack, 0.0) <<
"Invalid cover for knapsack cut.";
95 cut_upper_bound -=
CeilRatio(slack, max_coeff);
97 cut.ub = cut_upper_bound;
98 VLOG(2) <<
"Generated Knapsack Constraint:" << cut.DebugString();
102bool SolutionSatisfiesConstraint(
103 const LinearConstraint& constraint,
106 const double tolerance = 1e-6;
107 return (activity <=
ToDouble(constraint.ub) + tolerance &&
108 activity >=
ToDouble(constraint.lb) - tolerance)
113bool SmallRangeAndAllCoefficientsMagnitudeAreTheSame(
114 const LinearConstraint& constraint, IntegerTrail* integer_trail) {
115 if (constraint.vars.empty())
return true;
117 const int64_t magnitude = std::abs(constraint.coeffs[0].value());
118 for (
int i = 1; i < constraint.coeffs.size(); ++i) {
119 const IntegerVariable
var = constraint.vars[i];
120 if (integer_trail->LevelZeroUpperBound(
var) -
121 integer_trail->LevelZeroLowerBound(
var) >
125 if (std::abs(constraint.coeffs[i].value()) != magnitude) {
132bool AllVarsTakeIntegerValue(
133 const std::vector<IntegerVariable> vars,
135 for (IntegerVariable
var : vars) {
136 if (std::abs(lp_values[
var] - std::round(lp_values[
var])) > 1e-6) {
152int GetSmallestCoverSize(
const LinearConstraint& constraint,
153 const IntegerTrail& integer_trail) {
154 IntegerValue ub = constraint.ub;
155 std::vector<IntegerValue> sorted_terms;
156 for (
int i = 0; i < constraint.vars.size(); ++i) {
157 const IntegerValue
coeff = constraint.coeffs[i];
158 const IntegerVariable
var = constraint.vars[i];
159 const IntegerValue var_ub = integer_trail.LevelZeroUpperBound(
var);
160 const IntegerValue var_lb = integer_trail.LevelZeroLowerBound(
var);
161 ub -= var_lb *
coeff;
162 sorted_terms.push_back(
coeff * (var_ub - var_lb));
164 std::sort(sorted_terms.begin(), sorted_terms.end(),
165 std::greater<IntegerValue>());
166 int smallest_cover_size = 0;
167 IntegerValue sorted_term_sum = IntegerValue(0);
168 while (sorted_term_sum <= ub &&
169 smallest_cover_size < constraint.vars.size()) {
170 sorted_term_sum += sorted_terms[smallest_cover_size++];
172 return smallest_cover_size;
175bool ConstraintIsEligibleForLifting(
const LinearConstraint& constraint,
176 const IntegerTrail& integer_trail) {
177 for (
const IntegerVariable
var : constraint.vars) {
178 if (integer_trail.LevelZeroLowerBound(
var) != IntegerValue(0) ||
179 integer_trail.LevelZeroUpperBound(
var) != IntegerValue(1)) {
190 const std::vector<IntegerValue>& cut_vars_original_coefficients,
193 absl::btree_set<IntegerVariable> vars_in_cut;
194 for (IntegerVariable
var : cut->
vars) {
195 vars_in_cut.insert(
var);
198 std::vector<std::pair<IntegerValue, IntegerVariable>> non_zero_vars;
199 std::vector<std::pair<IntegerValue, IntegerVariable>> zero_vars;
200 for (
int i = 0; i < constraint.
vars.size(); ++i) {
201 const IntegerVariable
var = constraint.
vars[i];
206 if (vars_in_cut.find(
var) != vars_in_cut.end())
continue;
208 if (lp_values[
var] <= 1e-6) {
211 non_zero_vars.push_back({
coeff,
var});
217 std::sort(non_zero_vars.rbegin(), non_zero_vars.rend());
218 std::sort(zero_vars.rbegin(), zero_vars.rend());
220 std::vector<std::pair<IntegerValue, IntegerVariable>> lifting_sequence(
221 std::move(non_zero_vars));
223 lifting_sequence.insert(lifting_sequence.end(), zero_vars.begin(),
227 std::vector<double> lifting_profits;
228 std::vector<double> lifting_weights;
229 for (
int i = 0; i < cut->
vars.size(); ++i) {
231 lifting_weights.push_back(
ToDouble(cut_vars_original_coefficients[i]));
235 bool is_lifted =
false;
236 bool is_solution_optimal =
false;
238 for (
auto entry : lifting_sequence) {
239 is_solution_optimal =
false;
240 const IntegerValue var_original_coeff = entry.first;
241 const IntegerVariable
var = entry.second;
242 const IntegerValue lifting_capacity = constraint.
ub - entry.first;
243 if (lifting_capacity <= IntegerValue(0))
continue;
244 knapsack_solver.
Init(lifting_profits, lifting_weights,
251 const double knapsack_upper_bound =
253 const IntegerValue cut_coeff =
254 cut->
ub -
static_cast<int64_t
>(knapsack_upper_bound);
255 if (cut_coeff > IntegerValue(0)) {
258 cut->
coeffs.push_back(cut_coeff);
259 lifting_profits.push_back(
ToDouble(cut_coeff));
260 lifting_weights.push_back(
ToDouble(var_original_coeff));
270 IntegerValue ub = constraint.
ub;
272 for (
int i = 0; i < constraint.
vars.size(); ++i) {
273 const IntegerVariable
var = constraint.
vars[i];
276 if (
ToDouble(var_ub) - lp_values[
var] <= 1.0 - kMinCutViolation) {
277 constraint_with_left_vars.
vars.push_back(
var);
282 ub -=
coeff * var_lb;
285 constraint_with_left_vars.
ub = ub;
286 constraint_with_left_vars.
lb = constraint.
lb;
287 return constraint_with_left_vars;
292 IntegerValue term_sum = IntegerValue(0);
293 for (
int i = 0; i < constraint.
vars.size(); ++i) {
294 const IntegerVariable
var = constraint.
vars[i];
297 term_sum +=
coeff * var_ub;
299 if (term_sum <= constraint.
ub) {
300 VLOG(2) <<
"Filtered by cover filter";
310 std::vector<double> variable_upper_bound_distances;
311 for (
const IntegerVariable
var : preprocessed_constraint.
vars) {
313 variable_upper_bound_distances.push_back(
ToDouble(var_ub) - lp_values[
var]);
316 const int smallest_cover_size =
317 GetSmallestCoverSize(preprocessed_constraint, integer_trail);
320 variable_upper_bound_distances.begin(),
321 variable_upper_bound_distances.begin() + smallest_cover_size - 1,
322 variable_upper_bound_distances.end());
323 double cut_lower_bound = 0.0;
324 for (
int i = 0; i < smallest_cover_size; ++i) {
325 cut_lower_bound += variable_upper_bound_distances[i];
327 if (cut_lower_bound >= 1.0 - kMinCutViolation) {
328 VLOG(2) <<
"Filtered by kappa heuristic";
337 std::sort(items.begin(), items.end(), std::greater<KnapsackItem>());
341 if (item.weight <= left_capacity) {
342 profit += item.profit;
343 left_capacity -= item.weight;
345 profit += (left_capacity / item.weight) * item.profit;
356 std::vector<KnapsackItem> items;
358 double sum_variable_profit = 0;
359 for (
int i = 0; i < constraint.
vars.size(); ++i) {
360 const IntegerVariable
var = constraint.
vars[i];
367 items.push_back(item);
369 sum_variable_profit += item.
profit;
374 if (sum_variable_profit - 1.0 + kMinCutViolation < 0.0)
return false;
377 const double knapsack_upper_bound =
379 if (knapsack_upper_bound < sum_variable_profit - 1.0 + kMinCutViolation) {
380 VLOG(2) <<
"Filtered by knapsack upper bound";
405 std::vector<LinearConstraint>* knapsack_constraints,
411 if (SmallRangeAndAllCoefficientsMagnitudeAreTheSame(constraint,
419 for (
int i = 0; i < constraint.
vars.size(); ++i) {
420 const IntegerVariable
var = constraint.
vars[i];
422 if (
coeff > IntegerValue(0)) {
428 canonical_knapsack_form.
ub = constraint.
ub;
430 knapsack_constraints->push_back(canonical_knapsack_form);
437 for (
int i = 0; i < constraint.
vars.size(); ++i) {
438 const IntegerVariable
var = constraint.
vars[i];
440 if (
coeff > IntegerValue(0)) {
446 canonical_knapsack_form.
ub = -constraint.
lb;
448 knapsack_constraints->push_back(canonical_knapsack_form);
455 const std::vector<LinearConstraint>& base_constraints,
456 const std::vector<IntegerVariable>& vars,
Model*
model) {
461 std::vector<LinearConstraint> knapsack_constraints;
469 if (constraint.vars.size() <= 2)
continue;
473 VLOG(1) <<
"#knapsack constraints: " << knapsack_constraints.size();
483 result.
generate_cuts = [implied_bounds_processor, knapsack_constraints, vars,
484 model, integer_trail](
491 if (AllVarsTakeIntegerValue(vars, lp_values))
return true;
494 "Knapsack on demand cover cut generator");
495 int64_t skipped_constraints = 0;
503 VLOG(2) <<
"Processing constraint: " << constraint.DebugString();
505 mutable_constraint = constraint;
507 lp_values, &mutable_constraint);
513 if (preprocessed_constraint.
vars.empty())
continue;
517 skipped_constraints++;
522 std::vector<double> profits;
523 profits.reserve(preprocessed_constraint.
vars.size());
526 std::vector<double> weights;
527 weights.reserve(preprocessed_constraint.
vars.size());
535 double sum_variable_profit = 0;
539 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
540 const IntegerVariable
var = preprocessed_constraint.
vars[i];
544 const double variable_profit = var_ub - lp_values[
var];
545 profits.push_back(variable_profit);
547 sum_variable_profit += variable_profit;
550 weights.push_back(
weight);
555 std::vector<IntegerVariable> cut_vars;
556 std::vector<IntegerValue> cut_vars_original_coefficients;
558 VLOG(2) <<
"Knapsack size: " << profits.size();
562 const double time_limit_for_knapsack_solver =
568 bool is_solution_optimal =
false;
570 sum_variable_profit - 1.0 + kMinCutViolation);
574 auto time_limit_for_solver =
575 absl::make_unique<TimeLimit>(time_limit_for_knapsack_solver);
576 const double sum_of_distance_to_ub_for_vars_in_cover =
577 sum_variable_profit -
578 knapsack_solver.
Solve(time_limit_for_solver.get(),
579 &is_solution_optimal);
580 if (is_solution_optimal) {
581 VLOG(2) <<
"Knapsack Optimal solution found yay !";
583 if (time_limit_for_solver->LimitReached()) {
584 VLOG(1) <<
"Knapsack Solver run out of time limit.";
586 if (sum_of_distance_to_ub_for_vars_in_cover < 1.0 - kMinCutViolation) {
589 IntegerValue constraint_ub_for_cut = preprocessed_constraint.
ub;
590 absl::btree_set<IntegerVariable> vars_in_cut;
591 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
592 const IntegerVariable
var = preprocessed_constraint.
vars[i];
595 cut_vars.push_back(
var);
596 cut_vars_original_coefficients.push_back(
coefficient);
597 vars_in_cut.insert(
var);
604 cut_vars, cut_vars_original_coefficients, constraint_ub_for_cut,
608 bool is_lifted =
false;
609 if (ConstraintIsEligibleForLifting(cut, *integer_trail)) {
611 cut_vars_original_coefficients, *integer_trail,
617 CHECK(!SolutionSatisfiesConstraint(cut, lp_values));
618 manager->AddCut(cut, is_lifted ?
"LiftedKnapsack" :
"Knapsack",
622 if (skipped_constraints > 0) {
623 VLOG(2) <<
"Skipped constraints: " << skipped_constraints;
635IntegerValue
GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor,
636 IntegerValue max_t) {
638 return rhs_remainder == 0
644 IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t,
645 IntegerValue max_scaling) {
659 const IntegerValue size = divisor - rhs_remainder;
660 if (max_scaling == 1 || size == 1) {
664 return [t, divisor](IntegerValue
coeff) {
667 }
else if (size <= max_scaling) {
668 return [size, rhs_remainder, t, divisor](IntegerValue
coeff) {
669 const IntegerValue t_coeff = t *
coeff;
672 const IntegerValue diff = remainder - rhs_remainder;
675 }
else if (max_scaling.value() * rhs_remainder.value() < divisor) {
685 return [t, divisor, max_scaling](IntegerValue
coeff) {
686 const IntegerValue t_coeff = t *
coeff;
689 const IntegerValue bucket =
FloorRatio(remainder * max_scaling, divisor);
690 return max_scaling *
ratio + bucket;
715 return [size, rhs_remainder, t, divisor, max_scaling](IntegerValue
coeff) {
716 const IntegerValue t_coeff = t *
coeff;
719 const IntegerValue diff = remainder - rhs_remainder;
720 const IntegerValue bucket =
721 diff > 0 ?
CeilRatio(diff * (max_scaling - 1), size)
723 return max_scaling *
ratio + bucket;
736 const int size = lp_values.size();
737 if (size == 0)
return;
750 relevant_indices_.clear();
751 relevant_lp_values_.clear();
752 relevant_coeffs_.clear();
753 relevant_bound_diffs_.clear();
755 adjusted_coeffs_.clear();
758 IntegerValue max_magnitude(0);
759 for (
int i = 0; i < size; ++i) {
762 max_magnitude =
std::max(max_magnitude, magnitude);
768 bool overflow =
false;
769 change_sign_at_postprocessing_.assign(size,
false);
770 for (
int i = 0; i < size; ++i) {
771 if (cut->
coeffs[i] == 0)
continue;
776 double lp_value = lp_values[i];
779 const IntegerValue bound_diff =
780 IntegerValue(
CapSub(ub.value(), lb.value()));
792 const double lb_dist = std::abs(lp_value -
ToDouble(lb));
793 const double ub_dist = std::abs(lp_value -
ToDouble(ub));
796 if ((bias * lb_dist > ub_dist && cut->
coeffs[i] < 0) ||
797 (lb_dist > bias * ub_dist && cut->
coeffs[i] > 0)) {
798 change_sign_at_postprocessing_[i] =
true;
800 lp_value = -lp_value;
815 if (bound_diff == 0) {
816 cut->
coeffs[i] = IntegerValue(0);
820 if (std::abs(lp_value) > 1e-2) {
821 relevant_coeffs_.push_back(cut->
coeffs[i]);
822 relevant_indices_.push_back(i);
823 relevant_lp_values_.push_back(lp_value);
824 relevant_bound_diffs_.push_back(bound_diff);
825 divisors_.push_back(magnitude);
830 if (relevant_coeffs_.empty()) {
831 VLOG(2) <<
"Issue, nothing to cut.";
851 double best_scaled_violation = 0.01;
852 const IntegerValue remainder_threshold(max_magnitude / 1000);
863 if (overflow || max_magnitude >= threshold) {
864 VLOG(2) <<
"Issue, overflow.";
868 const IntegerValue max_t = threshold / max_magnitude;
879 const IntegerValue divisor_threshold = max_magnitude / 10;
880 for (
int i = 0; i < divisors_.size(); ++i) {
881 if (divisors_[i] <= divisor_threshold)
continue;
882 divisors_[new_size++] = divisors_[i];
884 divisors_.resize(new_size);
891 IntegerValue best_divisor(0);
892 for (
const IntegerValue divisor : divisors_) {
894 const IntegerValue initial_rhs_remainder =
896 if (initial_rhs_remainder <= remainder_threshold)
continue;
898 IntegerValue temp_ub = cut->
ub;
899 adjusted_coeffs_.clear();
916 const IntegerValue adjust_threshold =
917 (divisor - initial_rhs_remainder - 1) / IntegerValue(size);
918 if (adjust_threshold > 0) {
922 bool early_abort =
false;
923 double loss_lb = 0.0;
924 const double threshold =
ToDouble(initial_rhs_remainder);
926 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
928 const IntegerValue
coeff = relevant_coeffs_[i];
929 const IntegerValue remainder =
932 if (divisor - remainder <= initial_rhs_remainder) {
935 loss_lb +=
ToDouble(divisor - remainder) * relevant_lp_values_[i];
936 if (loss_lb >= threshold) {
943 const IntegerValue diff = relevant_bound_diffs_[i];
944 if (remainder > 0 && remainder <= adjust_threshold &&
945 CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
946 temp_ub += remainder * diff;
947 adjusted_coeffs_.push_back({i,
coeff + remainder});
951 if (early_abort)
continue;
955 const IntegerValue rhs_remainder =
956 temp_ub -
FloorRatio(temp_ub, divisor) * divisor;
957 if (rhs_remainder == 0)
continue;
960 rhs_remainder, divisor,
GetFactorT(rhs_remainder, divisor, max_t),
971 const double threshold = scaling *
ToDouble(rhs_remainder);
978 double violation = -
ToDouble(f(temp_ub));
980 bool early_abort =
false;
981 int adjusted_coeffs_index = 0;
982 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
983 IntegerValue
coeff = relevant_coeffs_[i];
986 if (adjusted_coeffs_index < adjusted_coeffs_.size() &&
987 adjusted_coeffs_[adjusted_coeffs_index].first == i) {
988 coeff = adjusted_coeffs_[adjusted_coeffs_index].second;
989 adjusted_coeffs_index++;
992 if (
coeff == 0)
continue;
993 const IntegerValue new_coeff = f(
coeff);
994 const double new_coeff_double =
ToDouble(new_coeff);
995 const double lp_value = relevant_lp_values_[i];
997 l2_norm += new_coeff_double * new_coeff_double;
998 violation += new_coeff_double * lp_value;
999 loss += (scaling *
ToDouble(
coeff) - new_coeff_double) * lp_value;
1000 if (loss >= threshold) {
1005 if (early_abort)
continue;
1010 if (violation > best_scaled_violation) {
1011 best_scaled_violation = violation;
1012 best_divisor = divisor;
1016 if (best_divisor == 0) {
1026 const IntegerValue initial_rhs_remainder =
1028 const IntegerValue adjust_threshold =
1029 (best_divisor - initial_rhs_remainder - 1) / IntegerValue(size);
1030 if (adjust_threshold > 0) {
1031 for (
int i = 0; i < relevant_indices_.size(); ++i) {
1032 const int index = relevant_indices_[i];
1033 const IntegerValue diff = relevant_bound_diffs_[i];
1034 if (diff > adjust_threshold)
continue;
1038 const IntegerValue remainder =
1040 if (
CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
1041 cut->
ub += remainder * diff;
1054 const IntegerValue rhs_remainder =
1056 IntegerValue factor_t =
GetFactorT(rhs_remainder, best_divisor, max_t);
1063 remainders_.clear();
1064 for (
int i = 0; i < size; ++i) {
1066 const IntegerValue r =
1068 if (r > rhs_remainder) remainders_.push_back(r);
1071 if (remainders_.size() <= 100) {
1073 for (
const IntegerValue r : remainders_) {
1074 best_rs_.push_back(f(r));
1076 IntegerValue best_d = f(best_divisor);
1081 for (
const IntegerValue t :
1082 {IntegerValue(1),
GetFactorT(rhs_remainder, best_divisor, max_t)}) {
1083 for (IntegerValue s(2); s <= options.
max_scaling; ++s) {
1086 int num_strictly_better = 0;
1088 const IntegerValue d = g(best_divisor);
1089 for (
int i = 0; i < best_rs_.size(); ++i) {
1090 const IntegerValue temp = g(remainders_[i]);
1091 if (temp * best_d < best_rs_[i] * d)
break;
1092 if (temp * best_d > best_rs_[i] * d) num_strictly_better++;
1093 rs_.push_back(temp);
1095 if (rs_.size() == best_rs_.size() && num_strictly_better > 0) {
1107 cut->
ub = f(cut->
ub);
1112 num_lifted_booleans_ = 0;
1113 if (ib_processor !=
nullptr) {
1114 for (
int i = 0; i < size; ++i) {
1116 if (
coeff == 0)
continue;
1118 IntegerVariable
var = cut->
vars[i];
1119 if (change_sign_at_postprocessing_[i]) {
1138 const IntegerValue coeff_b =
1141 if (coeff_b == 0)
continue;
1143 ++num_lifted_booleans_;
1145 tmp_terms_.push_back({info.
bool_var, coeff_b});
1147 tmp_terms_.push_back({info.
bool_var, -coeff_b});
1148 cut->
ub =
CapAdd(-coeff_b.value(), cut->
ub.value());
1157 for (
int i = 0; i < size; ++i) {
1159 if (
coeff == 0)
continue;
1161 if (
coeff == 0)
continue;
1162 if (change_sign_at_postprocessing_[i]) {
1168 tmp_terms_.push_back({cut->
vars[i], -
coeff});
1175 tmp_terms_.push_back({cut->
vars[i],
coeff});
1189 const int base_size = lp_values.size();
1195 IntegerValue rhs = base_ct.
ub;
1196 IntegerValue sum_of_diff(0);
1197 IntegerValue max_base_magnitude(0);
1198 for (
int i = 0; i < base_size; ++i) {
1201 max_base_magnitude =
std::max(max_base_magnitude, positive_coeff);
1203 if (!
AddProductTo(positive_coeff, bound_diff, &sum_of_diff)) {
1206 const IntegerValue diff = positive_coeff * bound_diff;
1220 double activity = 0.0;
1222 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1223 if (a.dist_to_max_value == b.dist_to_max_value) {
1225 return a.positive_coeff < b.positive_coeff;
1227 return a.dist_to_max_value <
b.dist_to_max_value;
1229 for (
int i = 0; i < terms_.size(); ++i) {
1230 const Term& term = terms_[i];
1231 activity += term.dist_to_max_value;
1240 if (activity > 1.0) {
1255 if (rhs >= 0)
return false;
1256 if (new_size == 0)
return false;
1264 terms_.resize(new_size);
1265 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1266 if (a.positive_coeff == b.positive_coeff) {
1267 return a.dist_to_max_value > b.dist_to_max_value;
1269 return a.positive_coeff >
b.positive_coeff;
1271 in_cut_.assign(base_ct.vars.size(),
false);
1274 cut_.ub = IntegerValue(-1);
1275 IntegerValue max_coeff(0);
1276 for (
const Term term : terms_) {
1277 if (term.diff + rhs < 0) {
1281 in_cut_[term.index] =
true;
1282 max_coeff =
std::max(max_coeff, term.positive_coeff);
1283 cut_.vars.push_back(base_ct.vars[term.index]);
1284 if (base_ct.coeffs[term.index] > 0) {
1285 cut_.coeffs.push_back(IntegerValue(1));
1288 cut_.coeffs.push_back(IntegerValue(-1));
1297 if (max_coeff == 0)
return true;
1298 if (max_coeff < -rhs) {
1299 const IntegerValue m =
FloorRatio(-rhs - 1, max_coeff);
1300 rhs += max_coeff * m;
1319 const IntegerValue slack = -rhs;
1320 const IntegerValue remainder = max_coeff - slack;
1322 const IntegerValue max_scaling(
std::min(
1325 IntegerValue(1), max_scaling);
1327 const IntegerValue scaling = f(max_coeff);
1329 for (
int i = 0; i < cut_.coeffs.size(); ++i) cut_.coeffs[i] *= scaling;
1334 for (
int i = 0; i < base_size; ++i) {
1335 if (in_cut_[i])
continue;
1336 const IntegerValue positive_coeff =
IntTypeAbs(base_ct.coeffs[i]);
1337 const IntegerValue new_coeff = f(positive_coeff);
1338 if (new_coeff == 0)
continue;
1341 if (base_ct.coeffs[i] > 0) {
1343 cut_.coeffs.push_back(new_coeff);
1344 cut_.vars.push_back(base_ct.vars[i]);
1348 cut_.coeffs.push_back(-new_coeff);
1349 cut_.vars.push_back(base_ct.vars[i]);
1361 int linearization_level,
1372 [z, x, y, linearization_level,
model, trail, integer_trail](
1375 if (trail->CurrentDecisionLevel() > 0 && linearization_level == 1) {
1384 const int64_t kMaxSafeInteger = (int64_t{1} << 53) - 1;
1392 VLOG(3) <<
"Potential overflow in PositiveMultiplicationCutGenerator";
1396 const double x_lp_value = x.
LpValue(lp_values);
1397 const double y_lp_value = y.
LpValue(lp_values);
1398 const double z_lp_value = z.
LpValue(lp_values);
1406 auto try_add_above_cut =
1407 [manager, z_lp_value, x_lp_value, y_lp_value, x, y, z,
model,
1408 &lp_values](int64_t x_coeff, int64_t y_coeff, int64_t rhs) {
1409 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff >=
1410 rhs + kMinCutViolation) {
1413 cut.
AddTerm(z, IntegerValue(-1));
1414 if (x_coeff != 0) cut.
AddTerm(x, IntegerValue(x_coeff));
1415 if (y_coeff != 0) cut.
AddTerm(y, IntegerValue(y_coeff));
1416 manager->AddCut(cut.
Build(),
"PositiveProduct", lp_values);
1421 auto try_add_below_cut =
1422 [manager, z_lp_value, x_lp_value, y_lp_value, x, y, z,
model,
1423 &lp_values](int64_t x_coeff, int64_t y_coeff, int64_t rhs) {
1424 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff <=
1425 rhs - kMinCutViolation) {
1428 cut.
AddTerm(z, IntegerValue(-1));
1429 if (x_coeff != 0) cut.
AddTerm(x, IntegerValue(x_coeff));
1430 if (y_coeff != 0) cut.
AddTerm(y, IntegerValue(y_coeff));
1431 manager->AddCut(cut.
Build(),
"PositiveProduct", lp_values);
1442 try_add_above_cut(y_lb, x_lb, x_lb * y_lb);
1443 try_add_above_cut(y_ub, x_ub, x_ub * y_ub);
1444 try_add_below_cut(y_ub, x_lb, x_lb * y_ub);
1445 try_add_below_cut(y_lb, x_ub, x_ub * y_lb);
1461 [y, x, linearization_level, trail, integer_trail,
model](
1467 const int64_t x_ub = integer_trail->LevelZeroUpperBound(x).value();
1468 const int64_t x_lb = integer_trail->LevelZeroLowerBound(x).value();
1470 if (x_lb == x_ub)
return true;
1473 if (x_ub > (int64_t{1} << 31))
return true;
1476 const double y_lp_value = y.
LpValue(lp_values);
1477 const double x_lp_value = x.
LpValue(lp_values);
1482 const int64_t y_lb = x_lb * x_lb;
1483 const int64_t above_slope = x_ub + x_lb;
1484 const double max_lp_y = y_lb + above_slope * (x_lp_value - x_lb);
1485 if (y_lp_value >= max_lp_y + kMinCutViolation) {
1488 IntegerValue(-x_lb * x_ub));
1489 above_cut.
AddTerm(y, IntegerValue(1));
1490 above_cut.
AddTerm(x, IntegerValue(-above_slope));
1491 manager->AddCut(above_cut.
Build(),
"SquareUpper", lp_values);
1500 const int64_t x_floor =
static_cast<int64_t
>(std::floor(x_lp_value));
1501 const int64_t below_slope = 2 * x_floor + 1;
1502 const double min_lp_y =
1503 below_slope * x_lp_value - x_floor - x_floor * x_floor;
1504 if (min_lp_y >= y_lp_value + kMinCutViolation) {
1508 model, IntegerValue(-x_floor - x_floor * x_floor),
1510 below_cut.
AddTerm(y, IntegerValue(1));
1511 below_cut.
AddTerm(x, -IntegerValue(below_slope));
1512 manager->AddCut(below_cut.
Build(),
"SquareLower", lp_values);
1520void ImpliedBoundsProcessor::ProcessUpperBoundedConstraint(
1523 ProcessUpperBoundedConstraintWithSlackCreation(
1524 false, IntegerVariable(0), lp_values,
1529ImpliedBoundsProcessor::GetCachedImpliedBoundInfo(IntegerVariable
var) {
1530 auto it = cache_.find(
var);
1531 if (it != cache_.end())
return it->second;
1536ImpliedBoundsProcessor::ComputeBestImpliedBound(
1537 IntegerVariable
var,
1539 auto it = cache_.find(
var);
1540 if (it != cache_.end())
return it->second;
1541 BestImpliedBoundInfo result;
1542 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1544 implied_bounds_->GetImpliedBounds(
var)) {
1556 const IntegerValue diff = entry.lower_bound - lb;
1558 const double bool_lp_value = entry.is_positive
1559 ? lp_values[entry.literal_view]
1560 : 1.0 - lp_values[entry.literal_view];
1561 const double slack_lp_value =
1566 if (slack_lp_value < -1e-4) {
1567 LinearConstraint ib_cut;
1569 std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
1570 if (entry.is_positive) {
1572 terms.push_back({entry.literal_view, diff});
1573 terms.push_back({
var, IntegerValue(-1)});
1577 terms.push_back({entry.literal_view, -diff});
1578 terms.push_back({
var, IntegerValue(-1)});
1579 ib_cut.ub = -entry.lower_bound;
1582 ib_cut_pool_.AddCut(std::move(ib_cut),
"IB", lp_values);
1588 if (slack_lp_value + 1e-4 < result.slack_lp_value ||
1589 (slack_lp_value < result.slack_lp_value + 1e-4 &&
1590 diff > result.bound_diff)) {
1591 result.bool_lp_value = bool_lp_value;
1592 result.slack_lp_value = slack_lp_value;
1594 result.bound_diff = diff;
1595 result.is_positive = entry.is_positive;
1596 result.bool_var = entry.literal_view;
1599 cache_[
var] = result;
1603void ImpliedBoundsProcessor::RecomputeCacheAndSeparateSomeImpliedBoundCuts(
1606 for (
const IntegerVariable
var :
1607 implied_bounds_->VariablesWithImpliedBounds()) {
1609 ComputeBestImpliedBound(
var, lp_values);
1613void ImpliedBoundsProcessor::ProcessUpperBoundedConstraintWithSlackCreation(
1614 bool substitute_only_inner_variables, IntegerVariable first_slack,
1617 if (cache_.empty())
return;
1619 IntegerValue new_ub = cut->
ub;
1620 bool changed =
false;
1623 int64_t overflow_detection = 0;
1625 const int size = cut->
vars.size();
1626 for (
int i = 0; i < size; ++i) {
1627 IntegerVariable
var = cut->
vars[i];
1641 const int old_size = tmp_terms_.size();
1644 bool keep_term =
false;
1658 if (substitute_only_inner_variables) {
1659 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1660 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
1661 if (lp_values[
var] -
ToDouble(lb) < 1e-2) keep_term =
true;
1662 if (
ToDouble(ub) - lp_values[
var] < 1e-2) keep_term =
true;
1666 if (slack_infos ==
nullptr) {
1673 tmp_terms_.push_back({
var,
coeff});
1676 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1677 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
1682 slack_info.
ub = ub - lb;
1688 VLOG(2) <<
"Overflow";
1691 if (slack_infos !=
nullptr) {
1692 tmp_terms_.push_back({first_slack,
coeff});
1696 slack_info.
terms.push_back({
var, IntegerValue(1)});
1699 slack_infos->push_back(slack_info);
1706 VLOG(2) <<
"Overflow";
1709 if (slack_infos !=
nullptr) {
1710 tmp_terms_.push_back({first_slack,
coeff});
1714 slack_info.
terms.push_back({
var, IntegerValue(1)});
1717 slack_infos->push_back(slack_info);
1725 for (
int i = old_size; i < tmp_terms_.size(); ++i) {
1726 overflow_detection =
1727 CapAdd(overflow_detection, std::abs(tmp_terms_[i].second.value()));
1732 VLOG(2) <<
"Overflow";
1735 if (!changed)
return;
1746bool ImpliedBoundsProcessor::DebugSlack(IntegerVariable first_slack,
1749 const std::vector<SlackInfo>& info) {
1751 IntegerValue new_ub = cut.
ub;
1752 for (
int i = 0; i < cut.
vars.size(); ++i) {
1754 if (cut.
vars[i] < first_slack) {
1755 tmp_terms_.push_back({cut.
vars[i], cut.
coeffs[i]});
1760 const IntegerValue multiplier = cut.
coeffs[i];
1761 const int index = (cut.
vars[i].value() - first_slack.value()) / 2;
1762 for (
const std::pair<IntegerVariable, IntegerValue>& term :
1763 info[
index].terms) {
1764 tmp_terms_.push_back({term.first, term.second * multiplier});
1766 new_ub -= multiplier * info[
index].offset;
1771 tmp_cut.
ub = new_ub;
1781 for (
int i = 0; i < initial_cut.
vars.size(); ++i) {
1782 tmp_terms_.push_back({initial_cut.
vars[i], initial_cut.
coeffs[i]});
1785 tmp_copy.
ub = new_ub;
1789 if (tmp_cut == tmp_copy)
return true;
1800int64_t SumOfKMinValues(
const absl::btree_set<int64_t>& values,
int k) {
1803 for (
const int64_t
value : values) {
1805 if (++count >= k)
return sum;
1810void TryToGenerateAllDiffCut(
1811 const std::vector<std::pair<double, AffineExpression>>& sorted_exprs_lp,
1812 const IntegerTrail& integer_trail,
1814 LinearConstraintManager* manager, Model*
model) {
1815 std::vector<AffineExpression> current_set_exprs;
1816 const int num_exprs = sorted_exprs_lp.size();
1817 absl::btree_set<int64_t> min_values;
1818 absl::btree_set<int64_t> negated_max_values;
1820 for (
auto value_expr : sorted_exprs_lp) {
1821 sum += value_expr.first;
1822 const AffineExpression expr = value_expr.second;
1823 if (integer_trail.IsFixed(expr)) {
1824 const int64_t
value = integer_trail.FixedValue(expr).value();
1825 min_values.insert(
value);
1826 negated_max_values.insert(-
value);
1829 const int64_t
coeff = expr.coeff.value();
1830 const int64_t
constant = expr.constant.value();
1831 for (
const int64_t
value :
1832 integer_trail.InitialVariableDomain(expr.var).Values()) {
1838 if (++count >= num_exprs)
break;
1842 for (
const int64_t
value :
1843 integer_trail.InitialVariableDomain(expr.var).Negation().Values()) {
1849 if (++count >= num_exprs)
break;
1852 current_set_exprs.push_back(expr);
1853 const int64_t required_min_sum =
1854 SumOfKMinValues(min_values, current_set_exprs.size());
1855 const int64_t required_max_sum =
1856 -SumOfKMinValues(negated_max_values, current_set_exprs.size());
1857 if (sum < required_min_sum || sum > required_max_sum) {
1858 LinearConstraintBuilder cut(
model, IntegerValue(required_min_sum),
1859 IntegerValue(required_max_sum));
1860 for (AffineExpression expr : current_set_exprs) {
1861 cut.AddTerm(expr, IntegerValue(1));
1863 manager->AddCut(cut.Build(),
"all_diff", lp_values);
1867 current_set_exprs.clear();
1869 negated_max_values.clear();
1877 const std::vector<AffineExpression>& exprs,
Model*
model) {
1882 if (!integer_trail->
IsFixed(expr)) {
1883 result.
vars.push_back(expr.var);
1890 [exprs, integer_trail, trail,
model](
1897 std::vector<std::pair<double, AffineExpression>> sorted_exprs;
1903 sorted_exprs.push_back(std::make_pair(expr.LpValue(lp_values), expr));
1905 std::sort(sorted_exprs.begin(), sorted_exprs.end(),
1906 [](std::pair<double, AffineExpression>&
a,
1907 const std::pair<double, AffineExpression>&
b) {
1908 return a.first < b.first;
1910 TryToGenerateAllDiffCut(sorted_exprs, *integer_trail, lp_values,
1913 std::reverse(sorted_exprs.begin(), sorted_exprs.end());
1914 TryToGenerateAllDiffCut(sorted_exprs, *integer_trail, lp_values,
1918 VLOG(1) <<
"Created all_diff cut generator of size: " << exprs.size();
1924IntegerValue MaxCornerDifference(
const IntegerVariable
var,
1925 const IntegerValue w1_i,
1926 const IntegerValue w2_i,
1927 const IntegerTrail& integer_trail) {
1928 const IntegerValue lb = integer_trail.LevelZeroLowerBound(
var);
1929 const IntegerValue ub = integer_trail.LevelZeroUpperBound(
var);
1930 return std::max((w2_i - w1_i) * lb, (w2_i - w1_i) * ub);
1939IntegerValue MPlusCoefficient(
1940 const std::vector<IntegerVariable>& x_vars,
1941 const std::vector<LinearExpression>& exprs,
1943 const int max_index,
const IntegerTrail& integer_trail) {
1944 IntegerValue
coeff = exprs[max_index].offset;
1947 for (
const IntegerVariable
var : x_vars) {
1948 const int target_index = variable_partition[
var];
1949 if (max_index != target_index) {
1950 coeff += MaxCornerDifference(
1961double ComputeContribution(
1962 const IntegerVariable xi_var,
const std::vector<IntegerVariable>& z_vars,
1963 const std::vector<LinearExpression>& exprs,
1965 const IntegerTrail& integer_trail,
const int target_index) {
1967 CHECK_LT(target_index, exprs.size());
1968 const LinearExpression& target_expr = exprs[target_index];
1969 const double xi_value = lp_values[xi_var];
1971 double contrib =
ToDouble(wt_i) * xi_value;
1972 for (
int expr_index = 0; expr_index < exprs.size(); ++expr_index) {
1973 if (expr_index == target_index)
continue;
1974 const LinearExpression& max_expr = exprs[expr_index];
1975 const double z_max_value = lp_values[z_vars[expr_index]];
1976 const IntegerValue corner_value = MaxCornerDifference(
1979 contrib +=
ToDouble(corner_value) * z_max_value;
1986 const IntegerVariable target,
const std::vector<LinearExpression>& exprs,
1987 const std::vector<IntegerVariable>& z_vars,
Model*
model) {
1989 std::vector<IntegerVariable> x_vars;
1990 result.
vars = {target};
1991 const int num_exprs = exprs.size();
1992 for (
int i = 0; i < num_exprs; ++i) {
1993 result.
vars.push_back(z_vars[i]);
1994 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
1998 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
1999 return VariableIsPositive(var);
2001 result.
vars.insert(result.
vars.end(), x_vars.begin(), x_vars.end());
2005 [x_vars, z_vars, target, num_exprs, exprs, integer_trail,
model](
2009 lp_values.size(), -1);
2011 lp_values.size(), std::numeric_limits<double>::infinity());
2012 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
2013 for (
const IntegerVariable
var : x_vars) {
2014 const double contribution = ComputeContribution(
2015 var, z_vars, exprs, lp_values, *integer_trail, expr_index);
2016 const double prev_contribution = variable_partition_contrib[
var];
2017 if (contribution < prev_contribution) {
2018 variable_partition[
var] = expr_index;
2019 variable_partition_contrib[
var] = contribution;
2026 double violation = lp_values[target];
2027 cut.
AddTerm(target, IntegerValue(-1));
2029 for (
const IntegerVariable xi_var : x_vars) {
2030 const int input_index = variable_partition[xi_var];
2033 if (
coeff != IntegerValue(0)) {
2038 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
2039 const IntegerVariable z_var = z_vars[expr_index];
2040 const IntegerValue z_coeff = MPlusCoefficient(
2041 x_vars, exprs, variable_partition, expr_index, *integer_trail);
2042 if (z_coeff != IntegerValue(0)) {
2045 violation -=
ToDouble(z_coeff) * lp_values[z_var];
2047 if (violation > 1e-2) {
2048 manager->AddCut(cut.
Build(),
"LinMax", lp_values);
2057IntegerValue EvaluateMaxAffine(
2058 const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
2061 for (
const auto& p : affines) {
2062 y =
std::max(y, x * p.first + p.second);
2071 const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
2075 const IntegerValue x_max = integer_trail->LevelZeroUpperBound(
var);
2077 const IntegerValue y_at_min = EvaluateMaxAffine(affines, x_min);
2078 const IntegerValue y_at_max = EvaluateMaxAffine(affines, x_max);
2082 const IntegerValue delta_x = x_max - x_min;
2083 const IntegerValue delta_y = y_at_max - y_at_min;
2088 const IntegerValue rhs = delta_x * y_at_min - delta_y * x_min;
2096 VLOG(2) <<
"Linear constraint can cause overflow: " <<
ct;
2107 std::vector<std::pair<IntegerValue, IntegerValue>> affines,
2116 [target,
var, affines, cut_name, integer_trail,
model](
2119 if (integer_trail->
IsFixed(
var))
return true;
2121 cut_name, lp_values);
2128 const std::vector<IntegerVariable>& base_variables,
Model*
model) {
2131 std::vector<IntegerVariable> variables;
2132 std::vector<Literal> literals;
2133 absl::flat_hash_map<LiteralIndex, IntegerVariable> positive_map;
2134 absl::flat_hash_map<LiteralIndex, IntegerVariable> negative_map;
2137 for (
const IntegerVariable
var : base_variables) {
2138 if (integer_trail->LowerBound(
var) != IntegerValue(0))
continue;
2139 if (integer_trail->UpperBound(
var) != IntegerValue(1))
continue;
2140 const LiteralIndex literal_index = encoder->GetAssociatedLiteral(
2143 variables.push_back(
var);
2144 literals.push_back(
Literal(literal_index));
2145 positive_map[literal_index] =
var;
2150 result.
vars = variables;
2153 [variables, literals, implication_graph, positive_map, negative_map,
2156 std::vector<double> packed_values;
2157 for (
int i = 0; i < literals.size(); ++i) {
2158 packed_values.push_back(lp_values[variables[i]]);
2160 const std::vector<std::vector<Literal>> at_most_ones =
2161 implication_graph->GenerateAtMostOnesWithLargeWeight(literals,
2164 for (
const std::vector<Literal>& at_most_one : at_most_ones) {
2171 for (
const Literal l : at_most_one) {
2172 if (positive_map.contains(l.Index())) {
2173 builder.
AddTerm(positive_map.at(l.Index()), IntegerValue(1));
2176 builder.
AddTerm(negative_map.at(l.Index()), IntegerValue(-1));
2181 manager->AddCut(builder.
Build(),
"clique", lp_values);
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
bool best_solution(int item_id) const
void set_node_limit(const int64_t node_limit)
void Init(const std::vector< double > &profits, const std::vector< double > &weights, const double capacity)
double Solve(TimeLimit *time_limit, bool *is_solution_optimal)
void set_solution_upper_bound_threshold(const double solution_upper_bound_threshold)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
bool TrySimpleKnapsack(const LinearConstraint base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
void ProcessUpperBoundedConstraint(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraint *cut)
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
BestImpliedBoundInfo GetCachedImpliedBoundInfo(IntegerVariable var)
void ComputeCut(RoundingOptions options, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds, ImpliedBoundsProcessor *ib_processor, LinearConstraint *cut)
bool IsFixed(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void AddConstant(IntegerValue value)
void AddLinearExpression(const LinearExpression &expr)
void AddTerm(IntegerVariable var, IntegerValue coeff)
LiteralIndex NegatedIndex() const
Class that owns everything related to a particular optimization model.
int CurrentDecisionLevel() const
ModelSharedTimeLimit * time_limit
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
static double ToDouble(double f)
void ConvertToKnapsackForm(const LinearConstraint &constraint, std::vector< LinearConstraint > *knapsack_constraints, IntegerTrail *integer_trail)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
LinearConstraint GetPreprocessedLinearConstraint(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
IntType IntTypeAbs(IntType t)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< AffineExpression > &exprs, Model *model)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
bool CanFormValidKnapsackCover(const LinearConstraint &preprocessed_constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
void RemoveZeroTerms(LinearConstraint *constraint)
IntegerValue GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue max_t)
double GetKnapsackUpperBound(std::vector< KnapsackItem > items, const double capacity)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
bool CanBeFilteredUsingCutLowerBound(const LinearConstraint &preprocessed_constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
const IntegerVariable kNoIntegerVariable(-1)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue > > *terms, ClassWithVarsAndCoeffs *output)
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)
LinearConstraint BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue > > &affines, Model *model)
bool CanBeFilteredUsingKnapsackUpperBound(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
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)
void MakeAllVariablesPositive(LinearConstraint *constraint)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue > > affines, const std::string cut_name, Model *model)
IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var, const LinearExpression &expr)
CutGenerator CreateKnapsackCoverCutGenerator(const std::vector< LinearConstraint > &base_constraints, const std::vector< IntegerVariable > &vars, Model *model)
bool ConstraintIsTriviallyTrue(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
bool LiftKnapsackCut(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const std::vector< IntegerValue > &cut_vars_original_coefficients, const IntegerTrail &integer_trail, TimeLimit *time_limit, LinearConstraint *cut)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t FloorRatio(int64_t value, int64_t positive_coeff)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const
std::vector< IntegerVariable > vars
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
std::vector< std::pair< IntegerVariable, IntegerValue > > terms
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
std::string DebugString() const
void AddTerm(IntegerVariable var, IntegerValue coeff)
std::vector< IntegerVariable > vars