44 const double kMinCutViolation = 1e-4;
47 double GetLiteralLpValue(
50 const IntegerEncoder* encoder) {
51 const IntegerVariable direct_view = encoder->GetLiteralView(lit);
53 return lp_values[direct_view];
55 const IntegerVariable opposite_view = encoder->GetLiteralView(lit.Negated());
57 return 1.0 - lp_values[opposite_view];
63 LinearConstraint GenerateKnapsackCutForCover(
64 const std::vector<IntegerVariable>& vars,
65 const std::vector<IntegerValue>& coeffs,
const IntegerValue
upper_bound,
66 const IntegerTrail& integer_trail) {
67 CHECK_EQ(vars.size(), coeffs.size());
70 IntegerValue cut_upper_bound = IntegerValue(0);
71 IntegerValue max_coeff = coeffs[0];
74 for (
int i = 0; i < vars.size(); ++i) {
75 const IntegerValue var_upper_bound =
76 integer_trail.LevelZeroUpperBound(vars[i]);
77 cut_upper_bound += var_upper_bound;
78 cut.vars.push_back(vars[i]);
79 cut.coeffs.push_back(IntegerValue(1));
80 max_coeff =
std::max(max_coeff, coeffs[i]);
81 slack += coeffs[i] * var_upper_bound;
83 CHECK_GT(slack, 0.0) <<
"Invalid cover for knapsack cut.";
84 cut_upper_bound -=
CeilRatio(slack, max_coeff);
86 cut.ub = cut_upper_bound;
87 VLOG(2) <<
"Generated Knapsack Constraint:" << cut.DebugString();
91 bool SolutionSatisfiesConstraint(
92 const LinearConstraint& constraint,
95 const double tolerance = 1e-6;
96 return (activity <=
ToDouble(constraint.ub) + tolerance &&
97 activity >=
ToDouble(constraint.lb) - tolerance)
102 bool SmallRangeAndAllCoefficientsMagnitudeAreTheSame(
103 const LinearConstraint& constraint, IntegerTrail* integer_trail) {
104 if (constraint.vars.empty())
return true;
106 const int64_t magnitude = std::abs(constraint.coeffs[0].value());
107 for (
int i = 1; i < constraint.coeffs.size(); ++i) {
108 const IntegerVariable
var = constraint.vars[i];
109 if (integer_trail->LevelZeroUpperBound(
var) -
110 integer_trail->LevelZeroLowerBound(
var) >
114 if (std::abs(constraint.coeffs[i].value()) != magnitude) {
121 bool AllVarsTakeIntegerValue(
122 const std::vector<IntegerVariable> vars,
124 for (IntegerVariable
var : vars) {
125 if (std::abs(lp_values[
var] - std::round(lp_values[
var])) > 1e-6) {
141 int GetSmallestCoverSize(
const LinearConstraint& constraint,
142 const IntegerTrail& integer_trail) {
143 IntegerValue ub = constraint.ub;
144 std::vector<IntegerValue> sorted_terms;
145 for (
int i = 0; i < constraint.vars.size(); ++i) {
146 const IntegerValue coeff = constraint.coeffs[i];
147 const IntegerVariable
var = constraint.vars[i];
148 const IntegerValue var_ub = integer_trail.LevelZeroUpperBound(
var);
149 const IntegerValue var_lb = integer_trail.LevelZeroLowerBound(
var);
150 ub -= var_lb * coeff;
151 sorted_terms.push_back(coeff * (var_ub - var_lb));
153 std::sort(sorted_terms.begin(), sorted_terms.end(),
154 std::greater<IntegerValue>());
155 int smallest_cover_size = 0;
156 IntegerValue sorted_term_sum = IntegerValue(0);
157 while (sorted_term_sum <= ub &&
158 smallest_cover_size < constraint.vars.size()) {
159 sorted_term_sum += sorted_terms[smallest_cover_size++];
161 return smallest_cover_size;
164 bool ConstraintIsEligibleForLifting(
const LinearConstraint& constraint,
165 const IntegerTrail& integer_trail) {
166 for (
const IntegerVariable
var : constraint.vars) {
167 if (integer_trail.LevelZeroLowerBound(
var) != IntegerValue(0) ||
168 integer_trail.LevelZeroUpperBound(
var) != IntegerValue(1)) {
179 const std::vector<IntegerValue>& cut_vars_original_coefficients,
182 std::set<IntegerVariable> vars_in_cut;
183 for (IntegerVariable
var : cut->
vars) {
184 vars_in_cut.insert(
var);
187 std::vector<std::pair<IntegerValue, IntegerVariable>> non_zero_vars;
188 std::vector<std::pair<IntegerValue, IntegerVariable>> zero_vars;
189 for (
int i = 0; i < constraint.
vars.size(); ++i) {
190 const IntegerVariable
var = constraint.
vars[i];
195 if (vars_in_cut.find(
var) != vars_in_cut.end())
continue;
196 const IntegerValue coeff = constraint.
coeffs[i];
197 if (lp_values[
var] <= 1e-6) {
198 zero_vars.push_back({coeff,
var});
200 non_zero_vars.push_back({coeff,
var});
206 std::sort(non_zero_vars.rbegin(), non_zero_vars.rend());
207 std::sort(zero_vars.rbegin(), zero_vars.rend());
209 std::vector<std::pair<IntegerValue, IntegerVariable>> lifting_sequence(
210 std::move(non_zero_vars));
212 lifting_sequence.insert(lifting_sequence.end(), zero_vars.begin(),
216 std::vector<double> lifting_profits;
217 std::vector<double> lifting_weights;
218 for (
int i = 0; i < cut->
vars.size(); ++i) {
220 lifting_weights.push_back(
ToDouble(cut_vars_original_coefficients[i]));
224 bool is_lifted =
false;
225 bool is_solution_optimal =
false;
227 for (
auto entry : lifting_sequence) {
228 is_solution_optimal =
false;
229 const IntegerValue var_original_coeff = entry.first;
230 const IntegerVariable
var = entry.second;
231 const IntegerValue lifting_capacity = constraint.
ub - entry.first;
232 if (lifting_capacity <= IntegerValue(0))
continue;
233 knapsack_solver.
Init(lifting_profits, lifting_weights,
240 const double knapsack_upper_bound =
242 const IntegerValue cut_coeff =
243 cut->
ub - static_cast<int64_t>(knapsack_upper_bound);
244 if (cut_coeff > IntegerValue(0)) {
247 cut->
coeffs.push_back(cut_coeff);
248 lifting_profits.push_back(
ToDouble(cut_coeff));
249 lifting_weights.push_back(
ToDouble(var_original_coeff));
259 IntegerValue ub = constraint.
ub;
261 for (
int i = 0; i < constraint.
vars.size(); ++i) {
262 const IntegerVariable
var = constraint.
vars[i];
264 const IntegerValue coeff = constraint.
coeffs[i];
265 if (
ToDouble(var_ub) - lp_values[
var] <= 1.0 - kMinCutViolation) {
266 constraint_with_left_vars.
vars.push_back(
var);
267 constraint_with_left_vars.
coeffs.push_back(coeff);
271 ub -= coeff * var_lb;
274 constraint_with_left_vars.
ub = ub;
275 constraint_with_left_vars.
lb = constraint.
lb;
276 return constraint_with_left_vars;
281 IntegerValue term_sum = IntegerValue(0);
282 for (
int i = 0; i < constraint.
vars.size(); ++i) {
283 const IntegerVariable
var = constraint.
vars[i];
285 const IntegerValue coeff = constraint.
coeffs[i];
286 term_sum += coeff * var_ub;
288 if (term_sum <= constraint.
ub) {
289 VLOG(2) <<
"Filtered by cover filter";
299 std::vector<double> variable_upper_bound_distances;
300 for (
const IntegerVariable
var : preprocessed_constraint.
vars) {
302 variable_upper_bound_distances.push_back(
ToDouble(var_ub) - lp_values[
var]);
305 const int smallest_cover_size =
306 GetSmallestCoverSize(preprocessed_constraint, integer_trail);
309 variable_upper_bound_distances.begin(),
310 variable_upper_bound_distances.begin() + smallest_cover_size - 1,
311 variable_upper_bound_distances.end());
312 double cut_lower_bound = 0.0;
313 for (
int i = 0; i < smallest_cover_size; ++i) {
314 cut_lower_bound += variable_upper_bound_distances[i];
316 if (cut_lower_bound >= 1.0 - kMinCutViolation) {
317 VLOG(2) <<
"Filtered by kappa heuristic";
326 std::sort(items.begin(), items.end(), std::greater<KnapsackItem>());
330 if (item.weight <= left_capacity) {
331 profit += item.profit;
332 left_capacity -= item.weight;
334 profit += (left_capacity / item.weight) * item.profit;
345 std::vector<KnapsackItem> items;
347 double sum_variable_profit = 0;
348 for (
int i = 0; i < constraint.
vars.size(); ++i) {
349 const IntegerVariable
var = constraint.
vars[i];
352 const IntegerValue coeff = constraint.
coeffs[i];
356 items.push_back(item);
358 sum_variable_profit += item.
profit;
363 if (sum_variable_profit - 1.0 + kMinCutViolation < 0.0)
return false;
366 const double knapsack_upper_bound =
368 if (knapsack_upper_bound < sum_variable_profit - 1.0 + kMinCutViolation) {
369 VLOG(2) <<
"Filtered by knapsack upper bound";
394 std::vector<LinearConstraint>* knapsack_constraints,
400 if (SmallRangeAndAllCoefficientsMagnitudeAreTheSame(constraint,
408 for (
int i = 0; i < constraint.
vars.size(); ++i) {
409 const IntegerVariable
var = constraint.
vars[i];
410 const IntegerValue coeff = constraint.
coeffs[i];
411 if (coeff > IntegerValue(0)) {
417 canonical_knapsack_form.
ub = constraint.
ub;
419 knapsack_constraints->push_back(canonical_knapsack_form);
426 for (
int i = 0; i < constraint.
vars.size(); ++i) {
427 const IntegerVariable
var = constraint.
vars[i];
428 const IntegerValue coeff = constraint.
coeffs[i];
429 if (coeff > IntegerValue(0)) {
435 canonical_knapsack_form.
ub = -constraint.
lb;
437 knapsack_constraints->push_back(canonical_knapsack_form);
444 const std::vector<LinearConstraint>& base_constraints,
445 const std::vector<IntegerVariable>& vars,
Model*
model) {
450 std::vector<LinearConstraint> knapsack_constraints;
458 if (constraint.vars.size() <= 2)
continue;
462 VLOG(1) <<
"#knapsack constraints: " << knapsack_constraints.size();
472 result.
generate_cuts = [implied_bounds_processor, knapsack_constraints, vars,
473 model, integer_trail](
480 if (AllVarsTakeIntegerValue(vars, lp_values))
return true;
483 "Knapsack on demand cover cut generator");
484 int64_t skipped_constraints = 0;
492 VLOG(2) <<
"Processing constraint: " << constraint.DebugString();
494 mutable_constraint = constraint;
496 lp_values, &mutable_constraint);
502 if (preprocessed_constraint.
vars.empty())
continue;
506 skipped_constraints++;
511 std::vector<double> profits;
512 profits.reserve(preprocessed_constraint.
vars.size());
515 std::vector<double> weights;
516 weights.reserve(preprocessed_constraint.
vars.size());
524 double sum_variable_profit = 0;
528 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
529 const IntegerVariable
var = preprocessed_constraint.
vars[i];
533 const double variable_profit = var_ub - lp_values[
var];
534 profits.push_back(variable_profit);
536 sum_variable_profit += variable_profit;
539 weights.push_back(
weight);
544 std::vector<IntegerVariable> cut_vars;
545 std::vector<IntegerValue> cut_vars_original_coefficients;
547 VLOG(2) <<
"Knapsack size: " << profits.size();
551 const double time_limit_for_knapsack_solver =
557 bool is_solution_optimal =
false;
559 sum_variable_profit - 1.0 + kMinCutViolation);
563 auto time_limit_for_solver =
564 absl::make_unique<TimeLimit>(time_limit_for_knapsack_solver);
565 const double sum_of_distance_to_ub_for_vars_in_cover =
566 sum_variable_profit -
567 knapsack_solver.
Solve(time_limit_for_solver.get(),
568 &is_solution_optimal);
569 if (is_solution_optimal) {
570 VLOG(2) <<
"Knapsack Optimal solution found yay !";
572 if (time_limit_for_solver->LimitReached()) {
573 VLOG(1) <<
"Knapsack Solver run out of time limit.";
575 if (sum_of_distance_to_ub_for_vars_in_cover < 1.0 - kMinCutViolation) {
578 IntegerValue constraint_ub_for_cut = preprocessed_constraint.
ub;
579 std::set<IntegerVariable> vars_in_cut;
580 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
581 const IntegerVariable
var = preprocessed_constraint.
vars[i];
584 cut_vars.push_back(
var);
585 cut_vars_original_coefficients.push_back(
coefficient);
586 vars_in_cut.insert(
var);
593 cut_vars, cut_vars_original_coefficients, constraint_ub_for_cut,
597 bool is_lifted =
false;
598 if (ConstraintIsEligibleForLifting(cut, *integer_trail)) {
600 cut_vars_original_coefficients, *integer_trail,
606 CHECK(!SolutionSatisfiesConstraint(cut, lp_values));
607 manager->AddCut(cut, is_lifted ?
"LiftedKnapsack" :
"Knapsack",
611 if (skipped_constraints > 0) {
612 VLOG(2) <<
"Skipped constraints: " << skipped_constraints;
624 IntegerValue
GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor,
625 IntegerValue max_t) {
627 return rhs_remainder == 0
633 IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t,
634 IntegerValue max_scaling) {
648 const IntegerValue size = divisor - rhs_remainder;
649 if (max_scaling == 1 || size == 1) {
653 return [t, divisor](IntegerValue coeff) {
656 }
else if (size <= max_scaling) {
657 return [size, rhs_remainder, t, divisor](IntegerValue coeff) {
658 const IntegerValue t_coeff = t * coeff;
661 const IntegerValue diff = remainder - rhs_remainder;
664 }
else if (max_scaling.value() * rhs_remainder.value() < divisor) {
674 return [t, divisor, max_scaling](IntegerValue coeff) {
675 const IntegerValue t_coeff = t * coeff;
678 const IntegerValue bucket =
FloorRatio(remainder * max_scaling, divisor);
679 return max_scaling *
ratio + bucket;
704 return [size, rhs_remainder, t, divisor, max_scaling](IntegerValue coeff) {
705 const IntegerValue t_coeff = t * coeff;
708 const IntegerValue diff = remainder - rhs_remainder;
709 const IntegerValue bucket =
710 diff > 0 ?
CeilRatio(diff * (max_scaling - 1), size)
712 return max_scaling *
ratio + bucket;
725 const int size = lp_values.size();
726 if (size == 0)
return;
739 relevant_indices_.clear();
740 relevant_lp_values_.clear();
741 relevant_coeffs_.clear();
742 relevant_bound_diffs_.clear();
744 adjusted_coeffs_.clear();
747 IntegerValue max_magnitude(0);
748 for (
int i = 0; i < size; ++i) {
751 max_magnitude =
std::max(max_magnitude, magnitude);
757 bool overflow =
false;
758 change_sign_at_postprocessing_.assign(size,
false);
759 for (
int i = 0; i < size; ++i) {
760 if (cut->
coeffs[i] == 0)
continue;
765 double lp_value = lp_values[i];
768 const IntegerValue bound_diff =
769 IntegerValue(
CapSub(ub.value(), lb.value()));
781 const double lb_dist = std::abs(lp_value -
ToDouble(lb));
782 const double ub_dist = std::abs(lp_value -
ToDouble(ub));
785 if ((bias * lb_dist > ub_dist && cut->
coeffs[i] < 0) ||
786 (lb_dist > bias * ub_dist && cut->
coeffs[i] > 0)) {
787 change_sign_at_postprocessing_[i] =
true;
789 lp_value = -lp_value;
804 if (bound_diff == 0) {
805 cut->
coeffs[i] = IntegerValue(0);
809 if (std::abs(lp_value) > 1e-2) {
810 relevant_coeffs_.push_back(cut->
coeffs[i]);
811 relevant_indices_.push_back(i);
812 relevant_lp_values_.push_back(lp_value);
813 relevant_bound_diffs_.push_back(bound_diff);
814 divisors_.push_back(magnitude);
819 if (relevant_coeffs_.empty()) {
820 VLOG(2) <<
"Issue, nothing to cut.";
840 double best_scaled_violation = 0.01;
841 const IntegerValue remainder_threshold(max_magnitude / 1000);
852 if (overflow || max_magnitude >= threshold) {
853 VLOG(2) <<
"Issue, overflow.";
857 const IntegerValue max_t = threshold / max_magnitude;
868 const IntegerValue divisor_threshold = max_magnitude / 10;
869 for (
int i = 0; i < divisors_.size(); ++i) {
870 if (divisors_[i] <= divisor_threshold)
continue;
871 divisors_[new_size++] = divisors_[i];
873 divisors_.resize(new_size);
880 IntegerValue best_divisor(0);
881 for (
const IntegerValue divisor : divisors_) {
883 const IntegerValue initial_rhs_remainder =
885 if (initial_rhs_remainder <= remainder_threshold)
continue;
887 IntegerValue temp_ub = cut->
ub;
888 adjusted_coeffs_.clear();
905 const IntegerValue adjust_threshold =
906 (divisor - initial_rhs_remainder - 1) / IntegerValue(size);
907 if (adjust_threshold > 0) {
911 bool early_abort =
false;
912 double loss_lb = 0.0;
913 const double threshold =
ToDouble(initial_rhs_remainder);
915 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
917 const IntegerValue coeff = relevant_coeffs_[i];
918 const IntegerValue remainder =
919 CeilRatio(coeff, divisor) * divisor - coeff;
921 if (divisor - remainder <= initial_rhs_remainder) {
924 loss_lb +=
ToDouble(divisor - remainder) * relevant_lp_values_[i];
925 if (loss_lb >= threshold) {
932 const IntegerValue diff = relevant_bound_diffs_[i];
933 if (remainder > 0 && remainder <= adjust_threshold &&
934 CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
935 temp_ub += remainder * diff;
936 adjusted_coeffs_.push_back({i, coeff + remainder});
940 if (early_abort)
continue;
944 const IntegerValue rhs_remainder =
945 temp_ub -
FloorRatio(temp_ub, divisor) * divisor;
946 if (rhs_remainder == 0)
continue;
949 rhs_remainder, divisor,
GetFactorT(rhs_remainder, divisor, max_t),
960 const double threshold = scaling *
ToDouble(rhs_remainder);
967 double violation = -
ToDouble(f(temp_ub));
968 double l2_norm = 0.0;
969 bool early_abort =
false;
970 int adjusted_coeffs_index = 0;
971 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
972 IntegerValue coeff = relevant_coeffs_[i];
975 if (adjusted_coeffs_index < adjusted_coeffs_.size() &&
976 adjusted_coeffs_[adjusted_coeffs_index].first == i) {
977 coeff = adjusted_coeffs_[adjusted_coeffs_index].second;
978 adjusted_coeffs_index++;
981 if (coeff == 0)
continue;
982 const IntegerValue new_coeff = f(coeff);
983 const double new_coeff_double =
ToDouble(new_coeff);
984 const double lp_value = relevant_lp_values_[i];
986 l2_norm += new_coeff_double * new_coeff_double;
987 violation += new_coeff_double * lp_value;
988 loss += (scaling *
ToDouble(coeff) - new_coeff_double) * lp_value;
989 if (loss >= threshold) {
994 if (early_abort)
continue;
998 violation /= sqrt(l2_norm);
999 if (violation > best_scaled_violation) {
1000 best_scaled_violation = violation;
1001 best_divisor = divisor;
1005 if (best_divisor == 0) {
1015 const IntegerValue initial_rhs_remainder =
1017 const IntegerValue adjust_threshold =
1018 (best_divisor - initial_rhs_remainder - 1) / IntegerValue(size);
1019 if (adjust_threshold > 0) {
1020 for (
int i = 0; i < relevant_indices_.size(); ++i) {
1021 const int index = relevant_indices_[i];
1022 const IntegerValue diff = relevant_bound_diffs_[i];
1023 if (diff > adjust_threshold)
continue;
1027 const IntegerValue remainder =
1028 CeilRatio(coeff, best_divisor) * best_divisor - coeff;
1029 if (
CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
1030 cut->
ub += remainder * diff;
1043 const IntegerValue rhs_remainder =
1045 IntegerValue factor_t =
GetFactorT(rhs_remainder, best_divisor, max_t);
1052 remainders_.clear();
1053 for (
int i = 0; i < size; ++i) {
1054 const IntegerValue coeff = cut->
coeffs[i];
1055 const IntegerValue r =
1056 coeff -
FloorRatio(coeff, best_divisor) * best_divisor;
1057 if (r > rhs_remainder) remainders_.push_back(r);
1060 if (remainders_.size() <= 100) {
1062 for (
const IntegerValue r : remainders_) {
1063 best_rs_.push_back(f(r));
1065 IntegerValue best_d = f(best_divisor);
1070 for (
const IntegerValue t :
1071 {IntegerValue(1),
GetFactorT(rhs_remainder, best_divisor, max_t)}) {
1072 for (IntegerValue s(2); s <= options.
max_scaling; ++s) {
1075 int num_strictly_better = 0;
1077 const IntegerValue d = g(best_divisor);
1078 for (
int i = 0; i < best_rs_.size(); ++i) {
1079 const IntegerValue temp = g(remainders_[i]);
1080 if (temp * best_d < best_rs_[i] * d)
break;
1081 if (temp * best_d > best_rs_[i] * d) num_strictly_better++;
1082 rs_.push_back(temp);
1084 if (rs_.size() == best_rs_.size() && num_strictly_better > 0) {
1096 cut->
ub = f(cut->
ub);
1101 num_lifted_booleans_ = 0;
1102 if (ib_processor !=
nullptr) {
1103 for (
int i = 0; i < size; ++i) {
1104 const IntegerValue coeff = cut->
coeffs[i];
1105 if (coeff == 0)
continue;
1107 IntegerVariable
var = cut->
vars[i];
1108 if (change_sign_at_postprocessing_[i]) {
1127 const IntegerValue coeff_b =
1130 if (coeff_b == 0)
continue;
1132 ++num_lifted_booleans_;
1134 tmp_terms_.push_back({info.
bool_var, coeff_b});
1136 tmp_terms_.push_back({info.
bool_var, -coeff_b});
1137 cut->
ub =
CapAdd(-coeff_b.value(), cut->
ub.value());
1146 for (
int i = 0; i < size; ++i) {
1147 IntegerValue coeff = cut->
coeffs[i];
1148 if (coeff == 0)
continue;
1150 if (coeff == 0)
continue;
1151 if (change_sign_at_postprocessing_[i]) {
1152 cut->
ub = IntegerValue(
1154 tmp_terms_.push_back({cut->
vars[i], -coeff});
1156 cut->
ub = IntegerValue(
1158 tmp_terms_.push_back({cut->
vars[i], coeff});
1172 const int base_size = lp_values.size();
1178 IntegerValue rhs = base_ct.
ub;
1179 IntegerValue sum_of_diff(0);
1180 IntegerValue max_base_magnitude(0);
1181 for (
int i = 0; i < base_size; ++i) {
1182 const IntegerValue coeff = base_ct.
coeffs[i];
1183 const IntegerValue positive_coeff =
IntTypeAbs(coeff);
1184 max_base_magnitude =
std::max(max_base_magnitude, positive_coeff);
1186 if (!
AddProductTo(positive_coeff, bound_diff, &sum_of_diff)) {
1189 const IntegerValue diff = positive_coeff * bound_diff;
1203 double activity = 0.0;
1205 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1206 if (
a.dist_to_max_value ==
b.dist_to_max_value) {
1208 return a.positive_coeff <
b.positive_coeff;
1210 return a.dist_to_max_value <
b.dist_to_max_value;
1212 for (
int i = 0; i < terms_.size(); ++i) {
1213 const Term& term = terms_[i];
1214 activity += term.dist_to_max_value;
1223 if (activity > 1.0) {
1238 if (rhs >= 0)
return false;
1239 if (new_size == 0)
return false;
1247 terms_.resize(new_size);
1248 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1249 if (
a.positive_coeff ==
b.positive_coeff) {
1250 return a.dist_to_max_value >
b.dist_to_max_value;
1252 return a.positive_coeff >
b.positive_coeff;
1254 in_cut_.assign(base_ct.
vars.size(),
false);
1257 cut_.
ub = IntegerValue(-1);
1258 IntegerValue max_coeff(0);
1259 for (
const Term term : terms_) {
1260 if (term.diff + rhs < 0) {
1264 in_cut_[term.index] =
true;
1265 max_coeff =
std::max(max_coeff, term.positive_coeff);
1266 cut_.
vars.push_back(base_ct.
vars[term.index]);
1267 if (base_ct.
coeffs[term.index] > 0) {
1268 cut_.
coeffs.push_back(IntegerValue(1));
1271 cut_.
coeffs.push_back(IntegerValue(-1));
1280 if (max_coeff == 0)
return true;
1281 if (max_coeff < -rhs) {
1282 const IntegerValue m =
FloorRatio(-rhs - 1, max_coeff);
1283 rhs += max_coeff * m;
1302 const IntegerValue slack = -rhs;
1303 const IntegerValue remainder = max_coeff - slack;
1305 const IntegerValue max_scaling(
std::min(
1308 IntegerValue(1), max_scaling);
1310 const IntegerValue scaling = f(max_coeff);
1312 for (
int i = 0; i < cut_.
coeffs.size(); ++i) cut_.
coeffs[i] *= scaling;
1317 for (
int i = 0; i < base_size; ++i) {
1318 if (in_cut_[i])
continue;
1320 const IntegerValue new_coeff = f(positive_coeff);
1321 if (new_coeff == 0)
continue;
1324 if (base_ct.
coeffs[i] > 0) {
1326 cut_.
coeffs.push_back(new_coeff);
1327 cut_.
vars.push_back(base_ct.
vars[i]);
1331 cut_.
coeffs.push_back(-new_coeff);
1332 cut_.
vars.push_back(base_ct.
vars[i]);
1344 int linearization_level,
1347 result.
vars = {z, x, y};
1353 [z, x, y, linearization_level,
model, trail, integer_trail](
1356 if (trail->CurrentDecisionLevel() > 0 && linearization_level == 1) {
1365 const int64_t kMaxSafeInteger = (int64_t{1} << 53) - 1;
1367 if (
CapProd(x_ub, y_ub) >= kMaxSafeInteger) {
1368 VLOG(3) <<
"Potential overflow in PositiveMultiplicationCutGenerator";
1372 const double x_lp_value = lp_values[x];
1373 const double y_lp_value = lp_values[y];
1374 const double z_lp_value = lp_values[z];
1382 auto try_add_above_cut =
1383 [manager, z_lp_value, x_lp_value, y_lp_value, x, y, z,
model,
1384 &lp_values](int64_t x_coeff, int64_t y_coeff, int64_t rhs) {
1385 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff >=
1386 rhs + kMinCutViolation) {
1389 cut.
AddTerm(z, IntegerValue(-1));
1390 if (x_coeff != 0) cut.
AddTerm(x, IntegerValue(x_coeff));
1391 if (y_coeff != 0) cut.
AddTerm(y, IntegerValue(y_coeff));
1392 manager->AddCut(cut.
Build(),
"PositiveProduct", lp_values);
1397 auto try_add_below_cut =
1398 [manager, z_lp_value, x_lp_value, y_lp_value, x, y, z,
model,
1399 &lp_values](int64_t x_coeff, int64_t y_coeff, int64_t rhs) {
1400 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff <=
1401 rhs - kMinCutViolation) {
1404 cut.
AddTerm(z, IntegerValue(-1));
1405 if (x_coeff != 0) cut.
AddTerm(x, IntegerValue(x_coeff));
1406 if (y_coeff != 0) cut.
AddTerm(y, IntegerValue(y_coeff));
1407 manager->AddCut(cut.
Build(),
"PositiveProduct", lp_values);
1418 try_add_above_cut(y_lb, x_lb, x_lb * y_lb);
1419 try_add_above_cut(y_ub, x_ub, x_ub * y_ub);
1420 try_add_below_cut(y_ub, x_lb, x_lb * y_ub);
1421 try_add_below_cut(y_lb, x_ub, x_ub * y_lb);
1431 result.
vars = {y, x};
1436 [y, x, linearization_level, trail, integer_trail](
1439 if (trail->CurrentDecisionLevel() > 0 && linearization_level == 1) {
1442 const int64_t x_ub = integer_trail->LevelZeroUpperBound(x).value();
1443 const int64_t x_lb = integer_trail->LevelZeroLowerBound(x).value();
1445 if (x_lb == x_ub)
return true;
1448 if (x_ub > (int64_t{1} << 31))
return true;
1451 const double y_lp_value = lp_values[y];
1452 const double x_lp_value = lp_values[x];
1457 const int64_t y_lb = x_lb * x_lb;
1458 const int64_t above_slope = x_ub + x_lb;
1459 const double max_lp_y = y_lb + above_slope * (x_lp_value - x_lb);
1460 if (y_lp_value >= max_lp_y + kMinCutViolation) {
1463 above_cut.
vars.push_back(y);
1464 above_cut.
coeffs.push_back(IntegerValue(1));
1465 above_cut.
vars.push_back(x);
1466 above_cut.
coeffs.push_back(IntegerValue(-above_slope));
1468 above_cut.
ub = IntegerValue(-x_lb * x_ub);
1469 manager->AddCut(above_cut,
"SquareUpper", lp_values);
1478 const int64_t x_floor = static_cast<int64_t>(std::floor(x_lp_value));
1479 const int64_t below_slope = 2 * x_floor + 1;
1480 const double min_lp_y =
1481 below_slope * x_lp_value - x_floor - x_floor * x_floor;
1482 if (min_lp_y >= y_lp_value + kMinCutViolation) {
1486 below_cut.
vars.push_back(y);
1487 below_cut.
coeffs.push_back(IntegerValue(1));
1488 below_cut.
vars.push_back(x);
1489 below_cut.
coeffs.push_back(-IntegerValue(below_slope));
1490 below_cut.
lb = IntegerValue(-x_floor - x_floor * x_floor);
1492 manager->AddCut(below_cut,
"SquareLower", lp_values);
1504 false, IntegerVariable(0), lp_values,
1510 auto it = cache_.find(
var);
1511 if (it != cache_.end())
return it->second;
1516 ImpliedBoundsProcessor::ComputeBestImpliedBound(
1517 IntegerVariable
var,
1519 auto it = cache_.find(
var);
1520 if (it != cache_.end())
return it->second;
1521 BestImpliedBoundInfo result;
1536 const IntegerValue diff = entry.lower_bound - lb;
1538 const double bool_lp_value = entry.is_positive
1539 ? lp_values[entry.literal_view]
1540 : 1.0 - lp_values[entry.literal_view];
1541 const double slack_lp_value =
1546 if (slack_lp_value < -1e-4) {
1547 LinearConstraint ib_cut;
1549 std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
1550 if (entry.is_positive) {
1552 terms.push_back({entry.literal_view, diff});
1553 terms.push_back({
var, IntegerValue(-1)});
1557 terms.push_back({entry.literal_view, -diff});
1558 terms.push_back({
var, IntegerValue(-1)});
1559 ib_cut.ub = -entry.lower_bound;
1562 ib_cut_pool_.
AddCut(std::move(ib_cut),
"IB", lp_values);
1568 if (slack_lp_value + 1e-4 < result.slack_lp_value ||
1569 (slack_lp_value < result.slack_lp_value + 1e-4 &&
1570 diff > result.bound_diff)) {
1571 result.bool_lp_value = bool_lp_value;
1572 result.slack_lp_value = slack_lp_value;
1574 result.bound_diff = diff;
1575 result.is_positive = entry.is_positive;
1576 result.bool_var = entry.literal_view;
1579 cache_[
var] = result;
1586 for (
const IntegerVariable
var :
1589 ComputeBestImpliedBound(
var, lp_values);
1594 bool substitute_only_inner_variables, IntegerVariable first_slack,
1597 if (cache_.empty())
return;
1599 IntegerValue new_ub = cut->
ub;
1600 bool changed =
false;
1603 int64_t overflow_detection = 0;
1605 const int size = cut->
vars.size();
1606 for (
int i = 0; i < size; ++i) {
1607 IntegerVariable
var = cut->
vars[i];
1608 IntegerValue coeff = cut->
coeffs[i];
1621 const int old_size = tmp_terms_.size();
1624 bool keep_term =
false;
1638 if (substitute_only_inner_variables) {
1641 if (lp_values[
var] -
ToDouble(lb) < 1e-2) keep_term =
true;
1642 if (
ToDouble(ub) - lp_values[
var] < 1e-2) keep_term =
true;
1646 if (slack_infos ==
nullptr) {
1653 tmp_terms_.push_back({
var, coeff});
1662 slack_info.
ub = ub - lb;
1668 VLOG(2) <<
"Overflow";
1671 if (slack_infos !=
nullptr) {
1672 tmp_terms_.push_back({first_slack, coeff});
1676 slack_info.
terms.push_back({
var, IntegerValue(1)});
1679 slack_infos->push_back(slack_info);
1686 VLOG(2) <<
"Overflow";
1689 if (slack_infos !=
nullptr) {
1690 tmp_terms_.push_back({first_slack, coeff});
1694 slack_info.
terms.push_back({
var, IntegerValue(1)});
1697 slack_infos->push_back(slack_info);
1705 for (
int i = old_size; i < tmp_terms_.size(); ++i) {
1706 overflow_detection =
1707 CapAdd(overflow_detection, std::abs(tmp_terms_[i].second.value()));
1712 VLOG(2) <<
"Overflow";
1715 if (!changed)
return;
1729 const std::vector<SlackInfo>& info) {
1731 IntegerValue new_ub = cut.
ub;
1732 for (
int i = 0; i < cut.
vars.size(); ++i) {
1734 if (cut.
vars[i] < first_slack) {
1735 tmp_terms_.push_back({cut.
vars[i], cut.
coeffs[i]});
1740 const IntegerValue multiplier = cut.
coeffs[i];
1741 const int index = (cut.
vars[i].value() - first_slack.value()) / 2;
1742 for (
const std::pair<IntegerVariable, IntegerValue>& term :
1743 info[
index].terms) {
1744 tmp_terms_.push_back({term.first, term.second * multiplier});
1746 new_ub -= multiplier * info[
index].offset;
1751 tmp_cut.
ub = new_ub;
1761 for (
int i = 0; i < initial_cut.
vars.size(); ++i) {
1762 tmp_terms_.push_back({initial_cut.
vars[i], initial_cut.
coeffs[i]});
1765 tmp_copy.
ub = new_ub;
1769 if (tmp_cut == tmp_copy)
return true;
1780 void TryToGenerateAllDiffCut(
1781 const std::vector<std::pair<double, IntegerVariable>>& sorted_vars_lp,
1786 std::vector<IntegerVariable> current_set_vars;
1788 for (
auto value_var : sorted_vars_lp) {
1789 sum += value_var.first;
1790 const IntegerVariable
var = value_var.second;
1795 current_set_vars.push_back(
var);
1796 const int64_t required_min_sum =
1798 const int64_t required_max_sum =
1800 if (sum < required_min_sum || sum > required_max_sum) {
1802 for (IntegerVariable
var : current_set_vars) {
1805 cut.
lb = IntegerValue(required_min_sum);
1806 cut.
ub = IntegerValue(required_max_sum);
1807 manager->
AddCut(cut,
"all_diff", lp_values);
1811 current_set_vars.clear();
1812 current_union =
Domain();
1820 const std::vector<IntegerVariable>& vars,
Model*
model) {
1826 [vars, integer_trail, trail](
1832 if (trail->CurrentDecisionLevel() > 0)
return true;
1833 std::vector<std::pair<double, IntegerVariable>> sorted_vars;
1834 for (
const IntegerVariable
var : vars) {
1835 if (integer_trail->LevelZeroLowerBound(
var) ==
1836 integer_trail->LevelZeroUpperBound(
var)) {
1839 sorted_vars.push_back(std::make_pair(lp_values[
var],
var));
1841 std::sort(sorted_vars.begin(), sorted_vars.end());
1842 TryToGenerateAllDiffCut(sorted_vars, *integer_trail, lp_values,
1845 std::reverse(sorted_vars.begin(), sorted_vars.end());
1846 TryToGenerateAllDiffCut(sorted_vars, *integer_trail, lp_values,
1850 VLOG(1) <<
"Created all_diff cut generator of size: " << vars.size();
1856 IntegerValue MaxCornerDifference(
const IntegerVariable
var,
1857 const IntegerValue w1_i,
1858 const IntegerValue w2_i,
1859 const IntegerTrail& integer_trail) {
1860 const IntegerValue lb = integer_trail.LevelZeroLowerBound(
var);
1861 const IntegerValue ub = integer_trail.LevelZeroUpperBound(
var);
1862 return std::max((w2_i - w1_i) * lb, (w2_i - w1_i) * ub);
1871 IntegerValue MPlusCoefficient(
1872 const std::vector<IntegerVariable>& x_vars,
1873 const std::vector<LinearExpression>& exprs,
1875 const int max_index,
const IntegerTrail& integer_trail) {
1876 IntegerValue coeff = exprs[max_index].offset;
1879 for (
const IntegerVariable
var : x_vars) {
1880 const int target_index = variable_partition[
var];
1881 if (max_index != target_index) {
1882 coeff += MaxCornerDifference(
1893 double ComputeContribution(
1894 const IntegerVariable xi_var,
const std::vector<IntegerVariable>& z_vars,
1895 const std::vector<LinearExpression>& exprs,
1897 const IntegerTrail& integer_trail,
const int target_index) {
1899 CHECK_LT(target_index, exprs.size());
1900 const LinearExpression& target_expr = exprs[target_index];
1901 const double xi_value = lp_values[xi_var];
1903 double contrib =
ToDouble(wt_i) * xi_value;
1904 for (
int expr_index = 0; expr_index < exprs.size(); ++expr_index) {
1905 if (expr_index == target_index)
continue;
1906 const LinearExpression& max_expr = exprs[expr_index];
1907 const double z_max_value = lp_values[z_vars[expr_index]];
1908 const IntegerValue corner_value = MaxCornerDifference(
1911 contrib +=
ToDouble(corner_value) * z_max_value;
1918 const IntegerVariable target,
const std::vector<LinearExpression>& exprs,
1919 const std::vector<IntegerVariable>& z_vars,
Model*
model) {
1921 std::vector<IntegerVariable> x_vars;
1922 result.
vars = {target};
1923 const int num_exprs = exprs.size();
1924 for (
int i = 0; i < num_exprs; ++i) {
1925 result.
vars.push_back(z_vars[i]);
1926 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
1930 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
1933 result.
vars.insert(result.
vars.end(), x_vars.begin(), x_vars.end());
1937 [x_vars, z_vars, target, num_exprs, exprs, integer_trail,
model](
1941 lp_values.size(), -1);
1943 lp_values.size(), std::numeric_limits<double>::infinity());
1944 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
1945 for (
const IntegerVariable
var : x_vars) {
1946 const double contribution = ComputeContribution(
1947 var, z_vars, exprs, lp_values, *integer_trail, expr_index);
1948 const double prev_contribution = variable_partition_contrib[
var];
1949 if (contribution < prev_contribution) {
1950 variable_partition[
var] = expr_index;
1951 variable_partition_contrib[
var] = contribution;
1958 double violation = lp_values[target];
1959 cut.
AddTerm(target, IntegerValue(-1));
1961 for (
const IntegerVariable xi_var : x_vars) {
1962 const int input_index = variable_partition[xi_var];
1965 if (coeff != IntegerValue(0)) {
1968 violation -=
ToDouble(coeff) * lp_values[xi_var];
1970 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
1971 const IntegerVariable z_var = z_vars[expr_index];
1972 const IntegerValue z_coeff = MPlusCoefficient(
1973 x_vars, exprs, variable_partition, expr_index, *integer_trail);
1974 if (z_coeff != IntegerValue(0)) {
1977 violation -=
ToDouble(z_coeff) * lp_values[z_var];
1979 if (violation > 1e-2) {
1980 manager->
AddCut(cut.
Build(),
"LinMax", lp_values);
1989 IntegerValue EvaluateMaxAffine(
1990 const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
1993 for (
const auto& p : affines) {
1994 y =
std::max(y, x * p.first + p.second);
2003 const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
2007 const IntegerValue x_max = integer_trail->LevelZeroUpperBound(
var);
2009 const IntegerValue y_at_min = EvaluateMaxAffine(affines, x_min);
2010 const IntegerValue y_at_max = EvaluateMaxAffine(affines, x_max);
2014 const IntegerValue delta_x = x_max - x_min;
2015 const IntegerValue delta_y = y_at_max - y_at_min;
2020 const IntegerValue rhs = delta_x * y_at_min - delta_y * x_min;
2028 VLOG(2) <<
"Linear constraint can cause overflow: " <<
ct;
2039 std::vector<std::pair<IntegerValue, IntegerValue>> affines,
2048 [target,
var, affines, cut_name, integer_trail,
model](
2051 if (integer_trail->IsFixed(
var))
return true;
2053 cut_name, lp_values);
2060 const std::vector<IntegerVariable>& base_variables,
Model*
model) {
2063 std::vector<IntegerVariable> variables;
2064 std::vector<Literal> literals;
2065 absl::flat_hash_map<LiteralIndex, IntegerVariable> positive_map;
2066 absl::flat_hash_map<LiteralIndex, IntegerVariable> negative_map;
2069 for (
const IntegerVariable
var : base_variables) {
2070 if (integer_trail->LowerBound(
var) != IntegerValue(0))
continue;
2071 if (integer_trail->UpperBound(
var) != IntegerValue(1))
continue;
2072 const LiteralIndex literal_index = encoder->GetAssociatedLiteral(
2075 variables.push_back(
var);
2076 literals.push_back(
Literal(literal_index));
2077 positive_map[literal_index] =
var;
2082 result.
vars = variables;
2085 [variables, literals, implication_graph, positive_map, negative_map,
2088 std::vector<double> packed_values;
2089 for (
int i = 0; i < literals.size(); ++i) {
2090 packed_values.push_back(lp_values[variables[i]]);
2092 const std::vector<std::vector<Literal>> at_most_ones =
2093 implication_graph->GenerateAtMostOnesWithLargeWeight(literals,
2096 for (
const std::vector<Literal>& at_most_one : at_most_ones) {
2103 for (
const Literal l : at_most_one) {
2105 builder.
AddTerm(positive_map.at(l.Index()), IntegerValue(1));
2108 builder.
AddTerm(negative_map.at(l.Index()), IntegerValue(-1));
2113 manager->
AddCut(builder.
Build(),
"clique", lp_values);
void set_node_limit(const int64_t node_limit)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
std::vector< std::pair< IntegerVariable, IntegerValue > > terms
int64_t CapSub(int64_t x, int64_t y)
bool CanBeFilteredUsingKnapsackUpperBound(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
void AddTerm(IntegerVariable var, IntegerValue coeff)
#define CHECK_GE(val1, val2)
Class that owns everything related to a particular optimization model.
ModelSharedTimeLimit * time_limit
std::vector< IntegerValue > coeffs
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
LinearConstraint BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue >> &affines, Model *model)
const Domain & InitialVariableDomain(IntegerVariable var) const
#define CHECK_GT(val1, val2)
#define VLOG(verboselevel)
std::vector< double > lower_bounds
std::vector< IntegerVariable > vars
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue >> affines, const std::string cut_name, Model *model)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue >> *terms, LinearConstraint *constraint)
LinearConstraint GetPreprocessedLinearConstraint(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
void AddLinearExpression(const LinearExpression &expr)
void AddTerm(IntegerVariable var, IntegerValue coeff)
int64_t CapProd(int64_t x, int64_t y)
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)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, int linearization_level, Model *model)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
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)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void ProcessUpperBoundedConstraint(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraint *cut)
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double Solve(TimeLimit *time_limit, bool *is_solution_optimal)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
std::vector< IntegerVariable > vars
std::vector< IntegerVariable > vars
#define CHECK_LT(val1, val2)
double ToDouble(IntegerValue value)
IntegerVariable PositiveVariable(IntegerVariable i)
BestImpliedBoundInfo GetCachedImpliedBoundInfo(IntegerVariable var)
void DivideByGCD(LinearConstraint *constraint)
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
LiteralIndex NegatedIndex() const
CutGenerator CreateKnapsackCoverCutGenerator(const std::vector< LinearConstraint > &base_constraints, const std::vector< IntegerVariable > &vars, Model *model)
int64_t SumOfKMinValueInDomain(const Domain &domain, int k)
int64_t CapAdd(int64_t x, int64_t y)
bool CanBeFilteredUsingCutLowerBound(const LinearConstraint &preprocessed_constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
#define DCHECK_NE(val1, val2)
const std::vector< IntegerVariable > & VariablesWithImpliedBounds() const
int64_t SumOfKMaxValueInDomain(const Domain &domain, int k)
bool ConstraintIsTriviallyTrue(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
bool ContainsKey(const Collection &collection, const Key &key)
bool DebugSlack(IntegerVariable first_slack, const LinearConstraint &initial_cut, const LinearConstraint &cut, const std::vector< SlackInfo > &info)
bool VariableIsPositive(IntegerVariable i)
#define DCHECK_GE(val1, val2)
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
void ProcessUpperBoundedConstraintWithSlackCreation(bool substitute_only_inner_variables, IntegerVariable first_slack, const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraint *cut, std::vector< SlackInfo > *slack_infos)
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
#define CHECK_EQ(val1, val2)
IntegerValue PositiveRemainder(IntegerValue dividend, IntegerValue positive_divisor)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< IntegerVariable > &vars, Model *model)
bool best_solution(int item_id) const
IntegerValue GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue max_t)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
#define DCHECK(condition)
const std::vector< ImpliedBoundEntry > & GetImpliedBounds(IntegerVariable var)
We call domain any subset of Int64 = [kint64min, kint64max].
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
std::function< IntegerValue(IntegerValue)> GetSuperAdditiveRoundingFunction(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t, IntegerValue max_scaling)
void AddConstant(IntegerValue value)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
void ConvertToKnapsackForm(const LinearConstraint &constraint, std::vector< LinearConstraint > *knapsack_constraints, IntegerTrail *integer_trail)
void AddCut(LinearConstraint ct, const std::string &name, const absl::StrongVector< IntegerVariable, double > &lp_solution)
void set_solution_upper_bound_threshold(const double solution_upper_bound_threshold)
Collection of objects used to extend the Constraint Solver library.
void Init(const std::vector< double > &profits, const std::vector< double > &weights, const double capacity)
const IntegerVariable kNoIntegerVariable(-1)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< double > upper_bounds
const LiteralIndex kNoLiteralIndex(-1)
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
double GetKnapsackUpperBound(std::vector< KnapsackItem > items, const double capacity)
IntType IntTypeAbs(IntType t)
CutGenerator CreatePositiveMultiplicationCutGenerator(IntegerVariable z, IntegerVariable x, IntegerVariable y, int linearization_level, Model *model)
void MakeAllVariablesPositive(LinearConstraint *constraint)
std::string DebugString() const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
bool TrySimpleKnapsack(const LinearConstraint base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
bool CanFormValidKnapsackCover(const LinearConstraint &preprocessed_constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
#define CHECK_NE(val1, val2)
void RemoveZeroTerms(LinearConstraint *constraint)
#define DCHECK_LT(val1, val2)
IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var, const LinearExpression &expr)