39 const double kMinCutViolation = 1e-4;
42 double GetLiteralLpValue(
44 const IntegerEncoder* encoder) {
45 const IntegerVariable direct_view = encoder->GetLiteralView(lit);
47 return lp_values[direct_view];
49 const IntegerVariable opposite_view = encoder->GetLiteralView(lit.Negated());
51 return 1.0 - lp_values[opposite_view];
57 LinearConstraint GenerateKnapsackCutForCover(
58 const std::vector<IntegerVariable>& vars,
59 const std::vector<IntegerValue>& coeffs,
const IntegerValue upper_bound,
60 const IntegerTrail& integer_trail) {
61 CHECK_EQ(vars.size(), coeffs.size());
62 CHECK_GT(vars.size(), 0);
64 IntegerValue cut_upper_bound = IntegerValue(0);
65 IntegerValue max_coeff = coeffs[0];
67 IntegerValue slack = -upper_bound;
68 for (
int i = 0; i < vars.size(); ++i) {
69 const IntegerValue var_upper_bound =
70 integer_trail.LevelZeroUpperBound(vars[i]);
71 cut_upper_bound += var_upper_bound;
72 cut.vars.push_back(vars[i]);
73 cut.coeffs.push_back(IntegerValue(1));
74 max_coeff =
std::max(max_coeff, coeffs[i]);
75 slack += coeffs[i] * var_upper_bound;
77 CHECK_GT(slack, 0.0) <<
"Invalid cover for knapsack cut.";
78 cut_upper_bound -=
CeilRatio(slack, max_coeff);
80 cut.ub = cut_upper_bound;
81 VLOG(2) <<
"Generated Knapsack Constraint:" << cut.DebugString();
85 bool SolutionSatisfiesConstraint(
86 const LinearConstraint& constraint,
89 const double tolerance = 1e-6;
90 return (activity <= constraint.ub.value() + tolerance &&
91 activity >= constraint.lb.value() - tolerance)
96 bool SmallRangeAndAllCoefficientsMagnitudeAreTheSame(
97 const LinearConstraint& constraint, IntegerTrail* integer_trail) {
98 if (constraint.vars.empty())
return true;
100 const int64 magnitude = std::abs(constraint.coeffs[0].value());
101 for (
int i = 1; i < constraint.coeffs.size(); ++i) {
102 const IntegerVariable
var = constraint.vars[i];
103 if (integer_trail->LevelZeroUpperBound(
var) -
104 integer_trail->LevelZeroLowerBound(
var) >
108 if (std::abs(constraint.coeffs[i].value()) != magnitude) {
115 bool AllVarsTakeIntegerValue(
116 const std::vector<IntegerVariable> vars,
118 for (IntegerVariable
var : vars) {
119 if (std::abs(lp_values[
var] - std::round(lp_values[
var])) > 1e-6) {
135 int GetSmallestCoverSize(
const LinearConstraint& constraint,
136 const IntegerTrail& integer_trail) {
137 IntegerValue ub = constraint.ub;
138 std::vector<IntegerValue> sorted_terms;
139 for (
int i = 0; i < constraint.vars.size(); ++i) {
140 const IntegerValue coeff = constraint.coeffs[i];
141 const IntegerVariable
var = constraint.vars[i];
142 const IntegerValue var_ub = integer_trail.LevelZeroUpperBound(
var);
143 const IntegerValue var_lb = integer_trail.LevelZeroLowerBound(
var);
144 ub -= var_lb * coeff;
145 sorted_terms.push_back(coeff * (var_ub - var_lb));
147 std::sort(sorted_terms.begin(), sorted_terms.end(),
148 std::greater<IntegerValue>());
149 int smallest_cover_size = 0;
150 IntegerValue sorted_term_sum = IntegerValue(0);
151 while (sorted_term_sum <= ub &&
152 smallest_cover_size < constraint.vars.size()) {
153 sorted_term_sum += sorted_terms[smallest_cover_size++];
155 return smallest_cover_size;
158 bool ConstraintIsEligibleForLifting(
const LinearConstraint& constraint,
159 const IntegerTrail& integer_trail) {
160 for (
const IntegerVariable
var : constraint.vars) {
161 if (integer_trail.LevelZeroLowerBound(
var) != IntegerValue(0) ||
162 integer_trail.LevelZeroUpperBound(
var) != IntegerValue(1)) {
173 const std::vector<IntegerValue>& cut_vars_original_coefficients,
176 std::set<IntegerVariable> vars_in_cut;
177 for (IntegerVariable
var : cut->
vars) {
178 vars_in_cut.insert(
var);
181 std::vector<std::pair<IntegerValue, IntegerVariable>> non_zero_vars;
182 std::vector<std::pair<IntegerValue, IntegerVariable>> zero_vars;
183 for (
int i = 0; i < constraint.
vars.size(); ++i) {
184 const IntegerVariable
var = constraint.
vars[i];
189 if (vars_in_cut.find(
var) != vars_in_cut.end())
continue;
190 const IntegerValue coeff = constraint.
coeffs[i];
191 if (lp_values[
var] <= 1e-6) {
192 zero_vars.push_back({coeff,
var});
194 non_zero_vars.push_back({coeff,
var});
200 std::sort(non_zero_vars.rbegin(), non_zero_vars.rend());
201 std::sort(zero_vars.rbegin(), zero_vars.rend());
203 std::vector<std::pair<IntegerValue, IntegerVariable>> lifting_sequence(
204 std::move(non_zero_vars));
206 lifting_sequence.insert(lifting_sequence.end(), zero_vars.begin(),
210 std::vector<double> lifting_profits;
211 std::vector<double> lifting_weights;
212 for (
int i = 0; i < cut->
vars.size(); ++i) {
213 lifting_profits.push_back(cut->
coeffs[i].value());
214 lifting_weights.push_back(cut_vars_original_coefficients[i].
value());
218 bool is_lifted =
false;
219 bool is_solution_optimal =
false;
221 for (
auto entry : lifting_sequence) {
222 is_solution_optimal =
false;
223 const IntegerValue var_original_coeff = entry.first;
224 const IntegerVariable
var = entry.second;
225 const IntegerValue lifting_capacity = constraint.
ub - entry.first;
226 if (lifting_capacity <= IntegerValue(0))
continue;
227 knapsack_solver.
Init(lifting_profits, lifting_weights,
228 lifting_capacity.value());
234 const double knapsack_upper_bound =
236 const IntegerValue cut_coeff = cut->
ub - knapsack_upper_bound;
237 if (cut_coeff > IntegerValue(0)) {
240 cut->
coeffs.push_back(cut_coeff);
241 lifting_profits.push_back(cut_coeff.value());
242 lifting_weights.push_back(var_original_coeff.value());
252 IntegerValue ub = constraint.
ub;
254 for (
int i = 0; i < constraint.
vars.size(); ++i) {
255 const IntegerVariable
var = constraint.
vars[i];
257 const IntegerValue coeff = constraint.
coeffs[i];
258 if (var_ub.value() - lp_values[
var] <= 1.0 - kMinCutViolation) {
259 constraint_with_left_vars.
vars.push_back(
var);
260 constraint_with_left_vars.
coeffs.push_back(coeff);
264 ub -= coeff * var_lb;
267 constraint_with_left_vars.
ub = ub;
268 constraint_with_left_vars.
lb = constraint.
lb;
269 return constraint_with_left_vars;
274 IntegerValue term_sum = IntegerValue(0);
275 for (
int i = 0; i < constraint.
vars.size(); ++i) {
276 const IntegerVariable
var = constraint.
vars[i];
278 const IntegerValue coeff = constraint.
coeffs[i];
279 term_sum += coeff * var_ub;
281 if (term_sum <= constraint.
ub) {
282 VLOG(2) <<
"Filtered by cover filter";
292 std::vector<double> variable_upper_bound_distances;
293 for (
const IntegerVariable
var : preprocessed_constraint.
vars) {
295 variable_upper_bound_distances.push_back(var_ub.value() - lp_values[
var]);
298 const int smallest_cover_size =
299 GetSmallestCoverSize(preprocessed_constraint, integer_trail);
302 variable_upper_bound_distances.begin(),
303 variable_upper_bound_distances.begin() + smallest_cover_size - 1,
304 variable_upper_bound_distances.end());
305 double cut_lower_bound = 0.0;
306 for (
int i = 0; i < smallest_cover_size; ++i) {
307 cut_lower_bound += variable_upper_bound_distances[i];
309 if (cut_lower_bound >= 1.0 - kMinCutViolation) {
310 VLOG(2) <<
"Filtered by kappa heuristic";
319 std::sort(items.begin(), items.end(), std::greater<KnapsackItem>());
323 if (item.weight <= left_capacity) {
324 profit += item.profit;
325 left_capacity -= item.weight;
327 profit += (left_capacity / item.weight) * item.profit;
338 std::vector<KnapsackItem> items;
339 double capacity = -constraint.
ub.value() - 1.0;
340 double sum_variable_profit = 0;
341 for (
int i = 0; i < constraint.
vars.size(); ++i) {
342 const IntegerVariable
var = constraint.
vars[i];
345 const IntegerValue coeff = constraint.
coeffs[i];
347 item.
profit = var_ub.value() - lp_values[
var];
348 item.
weight = (coeff * (var_ub - var_lb)).value();
349 items.push_back(item);
351 sum_variable_profit += item.
profit;
356 if (sum_variable_profit - 1.0 + kMinCutViolation < 0.0)
return false;
359 const double knapsack_upper_bound =
361 if (knapsack_upper_bound < sum_variable_profit - 1.0 + kMinCutViolation) {
362 VLOG(2) <<
"Filtered by knapsack upper bound";
387 std::vector<LinearConstraint>* knapsack_constraints,
393 if (SmallRangeAndAllCoefficientsMagnitudeAreTheSame(constraint,
401 for (
int i = 0; i < constraint.
vars.size(); ++i) {
402 const IntegerVariable
var = constraint.
vars[i];
403 const IntegerValue coeff = constraint.
coeffs[i];
404 if (coeff > IntegerValue(0)) {
410 canonical_knapsack_form.
ub = constraint.
ub;
412 knapsack_constraints->push_back(canonical_knapsack_form);
419 for (
int i = 0; i < constraint.
vars.size(); ++i) {
420 const IntegerVariable
var = constraint.
vars[i];
421 const IntegerValue coeff = constraint.
coeffs[i];
422 if (coeff > IntegerValue(0)) {
428 canonical_knapsack_form.
ub = -constraint.
lb;
430 knapsack_constraints->push_back(canonical_knapsack_form);
436 const std::vector<LinearConstraint>& base_constraints,
437 const std::vector<IntegerVariable>& vars,
Model*
model) {
442 std::vector<LinearConstraint> knapsack_constraints;
450 if (constraint.vars.size() <= 2)
continue;
454 VLOG(1) <<
"#knapsack constraints: " << knapsack_constraints.size();
464 result.
generate_cuts = [implied_bounds_processor, knapsack_constraints, vars,
465 model, integer_trail](
472 if (AllVarsTakeIntegerValue(vars, lp_values))
return;
475 "Knapsack on demand cover cut generator");
476 int64 skipped_constraints = 0;
483 VLOG(2) <<
"Processing constraint: " << constraint.DebugString();
485 mutable_constraint = constraint;
487 lp_values, &mutable_constraint);
493 if (preprocessed_constraint.
vars.empty())
continue;
497 skipped_constraints++;
502 std::vector<double> profits;
503 profits.reserve(preprocessed_constraint.
vars.size());
506 std::vector<double> weights;
507 weights.reserve(preprocessed_constraint.
vars.size());
509 double capacity = -preprocessed_constraint.
ub.value() - 1.0;
515 double sum_variable_profit = 0;
519 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
520 const IntegerVariable
var = preprocessed_constraint.
vars[i];
524 const double variable_profit = var_ub - lp_values[
var];
525 profits.push_back(variable_profit);
527 sum_variable_profit += variable_profit;
530 weights.push_back(
weight);
535 std::vector<IntegerVariable> cut_vars;
536 std::vector<IntegerValue> cut_vars_original_coefficients;
538 VLOG(2) <<
"Knapsack size: " << profits.size();
542 const double time_limit_for_knapsack_solver =
548 bool is_solution_optimal =
false;
550 sum_variable_profit - 1.0 + kMinCutViolation);
554 auto time_limit_for_solver =
555 absl::make_unique<TimeLimit>(time_limit_for_knapsack_solver);
556 const double sum_of_distance_to_ub_for_vars_in_cover =
557 sum_variable_profit -
558 knapsack_solver.
Solve(time_limit_for_solver.get(),
559 &is_solution_optimal);
560 if (is_solution_optimal) {
561 VLOG(2) <<
"Knapsack Optimal solution found yay !";
563 if (time_limit_for_solver->LimitReached()) {
564 VLOG(1) <<
"Knapsack Solver run out of time limit.";
566 if (sum_of_distance_to_ub_for_vars_in_cover < 1.0 - kMinCutViolation) {
569 IntegerValue constraint_ub_for_cut = preprocessed_constraint.
ub;
570 std::set<IntegerVariable> vars_in_cut;
571 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
572 const IntegerVariable
var = preprocessed_constraint.
vars[i];
575 cut_vars.push_back(
var);
576 cut_vars_original_coefficients.push_back(
coefficient);
577 vars_in_cut.insert(
var);
584 cut_vars, cut_vars_original_coefficients, constraint_ub_for_cut,
588 bool is_lifted =
false;
589 if (ConstraintIsEligibleForLifting(cut, *integer_trail)) {
591 cut_vars_original_coefficients, *integer_trail,
597 CHECK(!SolutionSatisfiesConstraint(cut, lp_values));
598 manager->AddCut(cut, is_lifted ?
"LiftedKnapsack" :
"Knapsack",
602 if (skipped_constraints > 0) {
603 VLOG(2) <<
"Skipped constraints: " << skipped_constraints;
614 IntegerValue
GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor,
615 IntegerValue max_t) {
617 return rhs_remainder == 0
623 IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t,
624 IntegerValue max_scaling) {
625 DCHECK_GE(max_scaling, 1);
629 DCHECK_LT(rhs_remainder, divisor);
636 const IntegerValue size = divisor - rhs_remainder;
637 if (max_scaling == 1 || size == 1) {
641 return [t, divisor](IntegerValue coeff) {
644 }
else if (size <= max_scaling) {
645 return [size, rhs_remainder, t, divisor](IntegerValue coeff) {
647 const IntegerValue remainder = t * coeff -
ratio * divisor;
648 const IntegerValue diff = remainder - rhs_remainder;
651 }
else if (max_scaling.value() * rhs_remainder.value() < divisor) {
661 return [t, divisor, max_scaling](IntegerValue coeff) {
663 const IntegerValue remainder = t * coeff -
ratio * divisor;
664 const IntegerValue bucket =
FloorRatio(remainder * max_scaling, divisor);
665 return max_scaling *
ratio + bucket;
690 return [size, rhs_remainder, t, divisor, max_scaling](IntegerValue coeff) {
692 const IntegerValue remainder = t * coeff -
ratio * divisor;
693 const IntegerValue diff = remainder - rhs_remainder;
694 const IntegerValue bucket =
695 diff > 0 ?
CeilRatio(diff * (max_scaling - 1), size)
697 return max_scaling *
ratio + bucket;
710 const int size = lp_values.size();
711 if (size == 0)
return;
714 CHECK_EQ(cut->
vars.size(), size);
715 CHECK_EQ(cut->
coeffs.size(), size);
724 relevant_indices_.clear();
725 relevant_lp_values_.clear();
726 relevant_coeffs_.clear();
727 relevant_bound_diffs_.clear();
729 adjusted_coeffs_.clear();
732 IntegerValue max_magnitude(0);
733 for (
int i = 0; i < size; ++i) {
736 max_magnitude =
std::max(max_magnitude, magnitude);
742 bool overflow =
false;
743 change_sign_at_postprocessing_.assign(size,
false);
744 for (
int i = 0; i < size; ++i) {
745 if (cut->
coeffs[i] == 0)
continue;
750 double lp_value = lp_values[i];
753 const IntegerValue bound_diff =
754 IntegerValue(
CapSub(ub.value(), lb.value()));
767 const double lb_dist = std::abs(lp_value -
ToDouble(lb));
768 const double ub_dist = std::abs(lp_value -
ToDouble(ub));
771 if ((bias * lb_dist > ub_dist && cut->
coeffs[i] < 0) ||
772 (lb_dist > bias * ub_dist && cut->
coeffs[i] > 0)) {
773 change_sign_at_postprocessing_[i] =
true;
775 lp_value = -lp_value;
790 if (bound_diff == 0) {
791 cut->
coeffs[i] = IntegerValue(0);
795 if (std::abs(lp_value) > 1e-2) {
796 relevant_coeffs_.push_back(cut->
coeffs[i]);
797 relevant_indices_.push_back(i);
798 relevant_lp_values_.push_back(lp_value);
799 relevant_bound_diffs_.push_back(bound_diff);
800 divisors_.push_back(magnitude);
805 if (relevant_coeffs_.empty()) {
806 VLOG(2) <<
"Issue, nothing to cut.";
810 CHECK_NE(max_magnitude, 0);
826 double best_scaled_violation = 0.01;
827 const IntegerValue remainder_threshold(max_magnitude / 1000);
838 if (overflow || max_magnitude >= threshold) {
839 VLOG(2) <<
"Issue, overflow.";
843 const IntegerValue max_t = threshold / max_magnitude;
854 const IntegerValue divisor_threshold = max_magnitude / 10;
855 for (
int i = 0; i < divisors_.size(); ++i) {
856 if (divisors_[i] <= divisor_threshold)
continue;
857 divisors_[new_size++] = divisors_[i];
859 divisors_.resize(new_size);
866 IntegerValue best_divisor(0);
867 for (
const IntegerValue divisor : divisors_) {
869 const IntegerValue initial_rhs_remainder =
871 if (initial_rhs_remainder <= remainder_threshold)
continue;
873 IntegerValue temp_ub = cut->
ub;
874 adjusted_coeffs_.clear();
891 const IntegerValue adjust_threshold =
892 (divisor - initial_rhs_remainder - 1) / IntegerValue(size);
893 if (adjust_threshold > 0) {
897 bool early_abort =
false;
898 double loss_lb = 0.0;
899 const double threshold =
ToDouble(initial_rhs_remainder);
901 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
903 const IntegerValue coeff = relevant_coeffs_[i];
904 const IntegerValue remainder =
905 CeilRatio(coeff, divisor) * divisor - coeff;
907 if (divisor - remainder <= initial_rhs_remainder) {
910 loss_lb +=
ToDouble(divisor - remainder) * relevant_lp_values_[i];
911 if (loss_lb >= threshold) {
918 const IntegerValue diff = relevant_bound_diffs_[i];
919 if (remainder > 0 && remainder <= adjust_threshold &&
920 CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
921 temp_ub += remainder * diff;
922 adjusted_coeffs_.push_back({i, coeff + remainder});
926 if (early_abort)
continue;
930 const IntegerValue rhs_remainder =
931 temp_ub -
FloorRatio(temp_ub, divisor) * divisor;
932 if (rhs_remainder == 0)
continue;
935 rhs_remainder, divisor,
GetFactorT(rhs_remainder, divisor, max_t),
946 const double threshold = scaling *
ToDouble(rhs_remainder);
953 double violation = -
ToDouble(f(temp_ub));
954 double l2_norm = 0.0;
955 bool early_abort =
false;
956 int adjusted_coeffs_index = 0;
957 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
958 IntegerValue coeff = relevant_coeffs_[i];
961 if (adjusted_coeffs_index < adjusted_coeffs_.size() &&
962 adjusted_coeffs_[adjusted_coeffs_index].first == i) {
963 coeff = adjusted_coeffs_[adjusted_coeffs_index].second;
964 adjusted_coeffs_index++;
967 if (coeff == 0)
continue;
968 const IntegerValue new_coeff = f(coeff);
969 const double new_coeff_double =
ToDouble(new_coeff);
970 const double lp_value = relevant_lp_values_[i];
972 l2_norm += new_coeff_double * new_coeff_double;
973 violation += new_coeff_double * lp_value;
974 loss += (scaling *
ToDouble(coeff) - new_coeff_double) * lp_value;
975 if (loss >= threshold) {
980 if (early_abort)
continue;
984 violation /= sqrt(l2_norm);
985 if (violation > best_scaled_violation) {
986 best_scaled_violation = violation;
987 best_divisor = divisor;
991 if (best_divisor == 0) {
1001 const IntegerValue initial_rhs_remainder =
1003 const IntegerValue adjust_threshold =
1004 (best_divisor - initial_rhs_remainder - 1) / IntegerValue(size);
1005 if (adjust_threshold > 0) {
1006 for (
int i = 0; i < relevant_indices_.size(); ++i) {
1007 const int index = relevant_indices_[i];
1008 const IntegerValue diff = relevant_bound_diffs_[i];
1009 if (diff > adjust_threshold)
continue;
1013 const IntegerValue remainder =
1014 CeilRatio(coeff, best_divisor) * best_divisor - coeff;
1015 if (
CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
1016 cut->
ub += remainder * diff;
1029 const IntegerValue rhs_remainder =
1031 IntegerValue factor_t =
GetFactorT(rhs_remainder, best_divisor, max_t);
1038 remainders_.clear();
1039 for (
int i = 0; i < size; ++i) {
1040 const IntegerValue coeff = cut->
coeffs[i];
1041 const IntegerValue r =
1042 coeff -
FloorRatio(coeff, best_divisor) * best_divisor;
1043 if (r > rhs_remainder) remainders_.push_back(r);
1046 if (remainders_.size() <= 100) {
1048 for (
const IntegerValue r : remainders_) {
1049 best_rs_.push_back(f(r));
1051 IntegerValue best_d = f(best_divisor);
1056 for (
const IntegerValue t :
1057 {IntegerValue(1),
GetFactorT(rhs_remainder, best_divisor, max_t)}) {
1058 for (IntegerValue s(2); s <= options.
max_scaling; ++s) {
1061 int num_strictly_better = 0;
1063 const IntegerValue d = g(best_divisor);
1064 for (
int i = 0; i < best_rs_.size(); ++i) {
1065 const IntegerValue temp = g(remainders_[i]);
1066 if (temp * best_d < best_rs_[i] * d)
break;
1067 if (temp * best_d > best_rs_[i] * d) num_strictly_better++;
1068 rs_.push_back(temp);
1070 if (rs_.size() == best_rs_.size() && num_strictly_better > 0) {
1082 cut->
ub = f(cut->
ub);
1087 num_lifted_booleans_ = 0;
1088 if (ib_processor !=
nullptr) {
1089 for (
int i = 0; i < size; ++i) {
1090 const IntegerValue coeff = cut->
coeffs[i];
1091 if (coeff == 0)
continue;
1093 IntegerVariable
var = cut->
vars[i];
1094 if (change_sign_at_postprocessing_[i]) {
1112 const IntegerValue coeff_b =
1114 CHECK_GE(coeff_b, 0);
1115 if (coeff_b == 0)
continue;
1117 ++num_lifted_booleans_;
1119 tmp_terms_.push_back({info.
bool_var, coeff_b});
1121 tmp_terms_.push_back({info.
bool_var, -coeff_b});
1122 cut->
ub =
CapAdd(-coeff_b.value(), cut->
ub.value());
1131 for (
int i = 0; i < size; ++i) {
1132 IntegerValue coeff = cut->
coeffs[i];
1133 if (coeff == 0)
continue;
1135 if (coeff == 0)
continue;
1136 if (change_sign_at_postprocessing_[i]) {
1137 cut->
ub = IntegerValue(
1139 tmp_terms_.push_back({cut->
vars[i], -coeff});
1141 cut->
ub = IntegerValue(
1143 tmp_terms_.push_back({cut->
vars[i], coeff});
1157 const int base_size = lp_values.size();
1163 IntegerValue rhs = base_ct.
ub;
1164 IntegerValue sum_of_diff(0);
1165 IntegerValue max_base_magnitude(0);
1166 for (
int i = 0; i < base_size; ++i) {
1167 const IntegerValue coeff = base_ct.
coeffs[i];
1168 const IntegerValue positive_coeff =
IntTypeAbs(coeff);
1169 max_base_magnitude =
std::max(max_base_magnitude, positive_coeff);
1171 if (!
AddProductTo(positive_coeff, bound_diff, &sum_of_diff)) {
1174 const IntegerValue diff = positive_coeff * bound_diff;
1188 double activity = 0.0;
1190 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1191 if (a.dist_to_max_value == b.dist_to_max_value) {
1193 return a.positive_coeff < b.positive_coeff;
1195 return a.dist_to_max_value <
b.dist_to_max_value;
1197 for (
int i = 0; i < terms_.size(); ++i) {
1198 const Term& term = terms_[i];
1199 activity += term.dist_to_max_value;
1208 if (activity > 1.0) {
1223 if (rhs >= 0)
return false;
1224 if (new_size == 0)
return false;
1232 terms_.resize(new_size);
1233 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1234 if (a.positive_coeff == b.positive_coeff) {
1235 return a.dist_to_max_value > b.dist_to_max_value;
1237 return a.positive_coeff >
b.positive_coeff;
1239 in_cut_.assign(base_ct.vars.size(),
false);
1242 cut_.ub = IntegerValue(-1);
1243 IntegerValue max_coeff(0);
1244 for (
const Term term : terms_) {
1245 if (term.diff + rhs < 0) {
1249 in_cut_[term.index] =
true;
1250 max_coeff =
std::max(max_coeff, term.positive_coeff);
1251 cut_.vars.push_back(base_ct.vars[term.index]);
1252 if (base_ct.coeffs[term.index] > 0) {
1253 cut_.coeffs.push_back(IntegerValue(1));
1256 cut_.coeffs.push_back(IntegerValue(-1));
1265 if (max_coeff == 0)
return true;
1266 if (max_coeff < -rhs) {
1267 const IntegerValue m =
FloorRatio(-rhs - 1, max_coeff);
1268 rhs += max_coeff * m;
1287 const IntegerValue slack = -rhs;
1288 const IntegerValue remainder = max_coeff - slack;
1289 const IntegerValue max_scaling(
std::min(
1292 IntegerValue(1), max_scaling);
1294 const IntegerValue scaling = f(max_coeff);
1296 for (
int i = 0; i < cut_.coeffs.size(); ++i) cut_.coeffs[i] *= scaling;
1301 for (
int i = 0; i < base_size; ++i) {
1302 if (in_cut_[i])
continue;
1303 const IntegerValue positive_coeff =
IntTypeAbs(base_ct.coeffs[i]);
1304 const IntegerValue new_coeff = f(positive_coeff);
1305 if (new_coeff == 0)
continue;
1308 if (base_ct.coeffs[i] > 0) {
1310 cut_.coeffs.push_back(new_coeff);
1311 cut_.vars.push_back(base_ct.vars[i]);
1315 cut_.coeffs.push_back(-new_coeff);
1316 cut_.vars.push_back(base_ct.vars[i]);
1330 result.
vars = {z, x, y};
1334 [z, x, y, integer_trail](
1343 const int64 kMaxSafeInteger = (
int64{1} << 53) - 1;
1345 if (
CapProd(x_ub, y_ub) >= kMaxSafeInteger) {
1346 VLOG(3) <<
"Potential overflow in PositiveMultiplicationCutGenerator";
1350 const double x_lp_value = lp_values[x];
1351 const double y_lp_value = lp_values[y];
1352 const double z_lp_value = lp_values[z];
1360 auto try_add_above_cut = [manager, z_lp_value, x_lp_value, y_lp_value,
1361 x, y, z, &lp_values](
1363 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff >=
1364 rhs + kMinCutViolation) {
1366 cut.
vars.push_back(z);
1367 cut.
coeffs.push_back(IntegerValue(-1));
1369 cut.
vars.push_back(x);
1370 cut.
coeffs.push_back(IntegerValue(x_coeff));
1373 cut.
vars.push_back(y);
1374 cut.
coeffs.push_back(IntegerValue(y_coeff));
1377 cut.
ub = IntegerValue(rhs);
1378 manager->AddCut(cut,
"PositiveProduct", lp_values);
1383 auto try_add_below_cut = [manager, z_lp_value, x_lp_value, y_lp_value,
1384 x, y, z, &lp_values](
1386 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff <=
1387 rhs - kMinCutViolation) {
1389 cut.
vars.push_back(z);
1390 cut.
coeffs.push_back(IntegerValue(-1));
1392 cut.
vars.push_back(x);
1393 cut.
coeffs.push_back(IntegerValue(x_coeff));
1396 cut.
vars.push_back(y);
1397 cut.
coeffs.push_back(IntegerValue(y_coeff));
1399 cut.
lb = IntegerValue(rhs);
1401 manager->AddCut(cut,
"PositiveProduct", lp_values);
1412 try_add_above_cut(y_lb, x_lb, x_lb * y_lb);
1413 try_add_above_cut(y_ub, x_ub, x_ub * y_ub);
1414 try_add_below_cut(y_ub, x_lb, x_lb * y_ub);
1415 try_add_below_cut(y_lb, x_ub, x_ub * y_lb);
1424 result.
vars = {y, x};
1428 [y, x, integer_trail](
1434 if (x_lb == x_ub)
return;
1437 if (x_ub > (
int64{1} << 31))
return;
1440 const double y_lp_value = lp_values[y];
1441 const double x_lp_value = lp_values[x];
1446 const int64 y_lb = x_lb * x_lb;
1447 const int64 above_slope = x_ub + x_lb;
1448 const double max_lp_y = y_lb + above_slope * (x_lp_value - x_lb);
1449 if (y_lp_value >= max_lp_y + kMinCutViolation) {
1452 above_cut.
vars.push_back(y);
1453 above_cut.
coeffs.push_back(IntegerValue(1));
1454 above_cut.
vars.push_back(x);
1455 above_cut.
coeffs.push_back(IntegerValue(-above_slope));
1457 above_cut.
ub = IntegerValue(-x_lb * x_ub);
1458 manager->AddCut(above_cut,
"SquareUpper", lp_values);
1467 const int64 x_floor =
static_cast<int64>(std::floor(x_lp_value));
1468 const int64 below_slope = 2 * x_floor + 1;
1469 const double min_lp_y =
1470 below_slope * x_lp_value - x_floor - x_floor * x_floor;
1471 if (min_lp_y >= y_lp_value + kMinCutViolation) {
1475 below_cut.
vars.push_back(y);
1476 below_cut.
coeffs.push_back(IntegerValue(1));
1477 below_cut.
vars.push_back(x);
1478 below_cut.
coeffs.push_back(-IntegerValue(below_slope));
1479 below_cut.
lb = IntegerValue(-x_floor - x_floor * x_floor);
1481 manager->AddCut(below_cut,
"SquareLower", lp_values);
1488 void ImpliedBoundsProcessor::ProcessUpperBoundedConstraint(
1491 ProcessUpperBoundedConstraintWithSlackCreation(
1492 false, IntegerVariable(0), lp_values,
1497 ImpliedBoundsProcessor::GetCachedImpliedBoundInfo(IntegerVariable
var) {
1498 auto it = cache_.find(
var);
1499 if (it != cache_.end())
return it->second;
1504 ImpliedBoundsProcessor::ComputeBestImpliedBound(
1505 IntegerVariable
var,
1507 auto it = cache_.find(
var);
1508 if (it != cache_.end())
return it->second;
1509 BestImpliedBoundInfo result;
1510 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1512 implied_bounds_->GetImpliedBounds(
var)) {
1524 const IntegerValue diff = entry.lower_bound - lb;
1526 const double bool_lp_value = entry.is_positive
1527 ? lp_values[entry.literal_view]
1528 : 1.0 - lp_values[entry.literal_view];
1529 const double slack_lp_value =
1534 if (slack_lp_value < -1e-4) {
1535 LinearConstraint ib_cut;
1537 std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
1538 if (entry.is_positive) {
1540 terms.push_back({entry.literal_view, diff});
1541 terms.push_back({
var, IntegerValue(-1)});
1545 terms.push_back({entry.literal_view, -diff});
1546 terms.push_back({
var, IntegerValue(-1)});
1547 ib_cut.ub = -entry.lower_bound;
1550 ib_cut_pool_.AddCut(std::move(ib_cut),
"IB", lp_values);
1556 if (slack_lp_value + 1e-4 < result.slack_lp_value ||
1557 (slack_lp_value < result.slack_lp_value + 1e-4 &&
1558 diff > result.bound_diff)) {
1559 result.bool_lp_value = bool_lp_value;
1560 result.slack_lp_value = slack_lp_value;
1562 result.bound_diff = diff;
1563 result.is_positive = entry.is_positive;
1564 result.bool_var = entry.literal_view;
1567 cache_[
var] = result;
1572 void ImpliedBoundsProcessor::SeparateSomeImpliedBoundCuts(
1574 for (
const IntegerVariable
var :
1575 implied_bounds_->VariablesWithImpliedBounds()) {
1577 ComputeBestImpliedBound(
var, lp_values);
1581 void ImpliedBoundsProcessor::ProcessUpperBoundedConstraintWithSlackCreation(
1582 bool substitute_only_inner_variables, IntegerVariable first_slack,
1586 IntegerValue new_ub = cut->
ub;
1587 bool changed =
false;
1590 int64 overflow_detection = 0;
1592 const int size = cut->
vars.size();
1593 for (
int i = 0; i < size; ++i) {
1594 IntegerVariable
var = cut->
vars[i];
1595 IntegerValue coeff = cut->
coeffs[i];
1617 const int old_size = tmp_terms_.size();
1620 bool keep_term =
false;
1634 if (substitute_only_inner_variables) {
1635 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1636 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
1637 if (lp_values[
var] -
ToDouble(lb) < 1e-2) keep_term =
true;
1638 if (
ToDouble(ub) - lp_values[
var] < 1e-2) keep_term =
true;
1642 if (slack_infos ==
nullptr) {
1649 tmp_terms_.push_back({
var, coeff});
1652 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1653 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
1658 slack_info.
ub = ub - lb;
1664 VLOG(2) <<
"Overflow";
1667 if (slack_infos !=
nullptr) {
1668 tmp_terms_.push_back({first_slack, coeff});
1672 slack_info.
terms.push_back({
var, IntegerValue(1)});
1675 slack_infos->push_back(slack_info);
1682 VLOG(2) <<
"Overflow";
1685 if (slack_infos !=
nullptr) {
1686 tmp_terms_.push_back({first_slack, coeff});
1690 slack_info.
terms.push_back({
var, IntegerValue(1)});
1693 slack_infos->push_back(slack_info);
1701 for (
int i = old_size; i < tmp_terms_.size(); ++i) {
1702 overflow_detection =
1703 CapAdd(overflow_detection, std::abs(tmp_terms_[i].second.value()));
1708 VLOG(2) <<
"Overflow";
1711 if (!changed)
return;
1722 bool ImpliedBoundsProcessor::DebugSlack(IntegerVariable first_slack,
1725 const std::vector<SlackInfo>& info) {
1727 IntegerValue new_ub = cut.
ub;
1728 for (
int i = 0; i < cut.
vars.size(); ++i) {
1730 if (cut.
vars[i] < first_slack) {
1731 tmp_terms_.push_back({cut.
vars[i], cut.
coeffs[i]});
1736 const IntegerValue multiplier = cut.
coeffs[i];
1737 const int index = (cut.
vars[i].value() - first_slack.value()) / 2;
1738 for (
const std::pair<IntegerVariable, IntegerValue>& term :
1739 info[
index].terms) {
1740 tmp_terms_.push_back({term.first, term.second * multiplier});
1742 new_ub -= multiplier * info[
index].offset;
1747 tmp_cut.
ub = new_ub;
1757 for (
int i = 0; i < initial_cut.
vars.size(); ++i) {
1758 tmp_terms_.push_back({initial_cut.
vars[i], initial_cut.
coeffs[i]});
1761 tmp_copy.
ub = new_ub;
1765 if (tmp_cut == tmp_copy)
return true;
1767 LOG(INFO) << first_slack;
1776 void TryToGenerateAllDiffCut(
1777 const std::vector<std::pair<double, IntegerVariable>>& sorted_vars_lp,
1782 std::vector<IntegerVariable> current_set_vars;
1784 for (
auto value_var : sorted_vars_lp) {
1785 sum += value_var.first;
1786 const IntegerVariable
var = value_var.second;
1791 current_set_vars.push_back(
var);
1792 const int64 required_min_sum =
1794 const int64 required_max_sum =
1796 if (sum < required_min_sum || sum > required_max_sum) {
1798 for (IntegerVariable
var : current_set_vars) {
1801 cut.
lb = IntegerValue(required_min_sum);
1802 cut.
ub = IntegerValue(required_max_sum);
1803 manager->
AddCut(cut,
"all_diff", lp_values);
1807 current_set_vars.clear();
1808 current_union =
Domain();
1816 const std::vector<IntegerVariable>& vars,
Model*
model) {
1822 [vars, integer_trail, trail](
1828 if (trail->CurrentDecisionLevel() > 0)
return;
1829 std::vector<std::pair<double, IntegerVariable>> sorted_vars;
1830 for (
const IntegerVariable
var : vars) {
1831 if (integer_trail->LevelZeroLowerBound(
var) ==
1832 integer_trail->LevelZeroUpperBound(
var)) {
1835 sorted_vars.push_back(std::make_pair(lp_values[
var],
var));
1837 std::sort(sorted_vars.begin(), sorted_vars.end());
1838 TryToGenerateAllDiffCut(sorted_vars, *integer_trail, lp_values,
1841 std::reverse(sorted_vars.begin(), sorted_vars.end());
1842 TryToGenerateAllDiffCut(sorted_vars, *integer_trail, lp_values,
1845 VLOG(1) <<
"Created all_diff cut generator of size: " << vars.size();
1851 IntegerValue MaxCornerDifference(
const IntegerVariable
var,
1852 const IntegerValue w1_i,
1853 const IntegerValue w2_i,
1854 const IntegerTrail& integer_trail) {
1855 const IntegerValue lb = integer_trail.LevelZeroLowerBound(
var);
1856 const IntegerValue ub = integer_trail.LevelZeroUpperBound(
var);
1857 return std::max((w2_i - w1_i) * lb, (w2_i - w1_i) * ub);
1866 IntegerValue MPlusCoefficient(
1867 const std::vector<IntegerVariable>& x_vars,
1868 const std::vector<LinearExpression>& exprs,
1870 const int max_index,
const IntegerTrail& integer_trail) {
1871 IntegerValue coeff = exprs[max_index].offset;
1874 for (
const IntegerVariable
var : x_vars) {
1875 const int target_index = variable_partition[
var];
1876 if (max_index != target_index) {
1877 coeff += MaxCornerDifference(
1888 double ComputeContribution(
1889 const IntegerVariable xi_var,
const std::vector<IntegerVariable>& z_vars,
1890 const std::vector<LinearExpression>& exprs,
1892 const IntegerTrail& integer_trail,
const int target_index) {
1893 CHECK_GE(target_index, 0);
1894 CHECK_LT(target_index, exprs.size());
1895 const LinearExpression& target_expr = exprs[target_index];
1896 const double xi_value = lp_values[xi_var];
1898 double contrib = wt_i.value() * xi_value;
1899 for (
int expr_index = 0; expr_index < exprs.size(); ++expr_index) {
1900 if (expr_index == target_index)
continue;
1901 const LinearExpression& max_expr = exprs[expr_index];
1902 const double z_max_value = lp_values[z_vars[expr_index]];
1903 const IntegerValue corner_value = MaxCornerDifference(
1906 contrib += corner_value.value() * z_max_value;
1913 const IntegerVariable target,
const std::vector<LinearExpression>& exprs,
1914 const std::vector<IntegerVariable>& z_vars,
Model*
model) {
1916 std::vector<IntegerVariable> x_vars;
1917 result.
vars = {target};
1918 const int num_exprs = exprs.size();
1919 for (
int i = 0; i < num_exprs; ++i) {
1920 result.
vars.push_back(z_vars[i]);
1921 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
1925 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
1926 return VariableIsPositive(var);
1928 result.
vars.insert(result.
vars.end(), x_vars.begin(), x_vars.end());
1932 [x_vars, z_vars, target, num_exprs, exprs, integer_trail,
model](
1936 lp_values.size(), -1);
1938 lp_values.size(), std::numeric_limits<double>::infinity());
1939 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
1940 for (
const IntegerVariable
var : x_vars) {
1941 const double contribution = ComputeContribution(
1942 var, z_vars, exprs, lp_values, *integer_trail, expr_index);
1943 const double prev_contribution = variable_partition_contrib[
var];
1944 if (contribution < prev_contribution) {
1945 variable_partition[
var] = expr_index;
1946 variable_partition_contrib[
var] = contribution;
1953 double violation = lp_values[target];
1954 cut.
AddTerm(target, IntegerValue(-1));
1956 for (
const IntegerVariable xi_var : x_vars) {
1957 const int input_index = variable_partition[xi_var];
1960 if (coeff != IntegerValue(0)) {
1963 violation -= coeff.value() * lp_values[xi_var];
1965 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
1966 const IntegerVariable z_var = z_vars[expr_index];
1967 const IntegerValue z_coeff = MPlusCoefficient(
1968 x_vars, exprs, variable_partition, expr_index, *integer_trail);
1969 if (z_coeff != IntegerValue(0)) {
1972 violation -= z_coeff.value() * lp_values[z_var];
1974 if (violation > 1e-2) {
1975 manager->
AddCut(cut.
Build(),
"LinMax", lp_values);
1983 std::vector<IntegerVariable>* vars) {
1984 vars->insert(vars->end(), helper->
StartVars().begin(),
1986 vars->insert(vars->end(), helper->
SizeVars().begin(),
1988 vars->insert(vars->end(), helper->
EndVars().begin(), helper->
EndVars().end());
1991 for (
int t = 0; t < helper->
NumTasks(); ++t) {
2001 vars->push_back(direct_view);
2011 LinearConstraintManager*)>
2014 const std::vector<IntegerVariable>& demands,
2020 return [
capacity, demands, trail, integer_trail, helper,
model, cut_name,
2023 if (trail->CurrentDecisionLevel() > 0)
return;
2025 const auto demand_is_fixed = [integer_trail, &demands](
int i) {
2026 return demands.empty() || integer_trail->IsFixed(demands[i]);
2028 const auto demand_min = [integer_trail, &demands](
int i) {
2029 return demands.empty() ? IntegerValue(1)
2030 : integer_trail->LowerBound(demands[i]);
2032 const auto demand_max = [integer_trail, &demands](
int i) {
2033 return demands.empty() ? IntegerValue(1)
2034 : integer_trail->UpperBound(demands[i]);
2037 std::vector<int> active_intervals;
2038 for (
int i = 0; i < helper->NumTasks(); ++i) {
2039 if (!helper->IsAbsent(i) && demand_max(i) > 0 && helper->SizeMin(i) > 0) {
2040 active_intervals.push_back(i);
2044 if (active_intervals.size() < 2)
return;
2046 std::sort(active_intervals.begin(), active_intervals.end(),
2047 [helper](
int a,
int b) {
2048 return helper->StartMin(a) < helper->StartMin(b) ||
2049 (helper->StartMin(a) == helper->StartMin(b) &&
2050 helper->EndMax(a) < helper->EndMax(b));
2053 const IntegerValue capacity_max = integer_trail->UpperBound(
capacity);
2055 for (
int i1 = 0; i1 + 1 < active_intervals.size(); ++i1) {
2056 const int start_index = active_intervals[i1];
2057 DCHECK(!helper->IsAbsent(start_index));
2061 if (helper->StartMin(start_index) == processed_start) {
2064 processed_start = helper->StartMin(start_index);
2069 int end_index_of_max_violation = -1;
2070 double max_relative_violation = 1.01;
2071 IntegerValue span_of_max_violation(0);
2074 double energy_lp = 0.0;
2080 std::vector<int> residual_tasks(active_intervals.begin() + i1,
2081 active_intervals.end());
2083 residual_tasks.begin(), residual_tasks.end(),
2084 [&](
int a,
int b) { return helper->EndMax(a) < helper->EndMax(b); });
2089 for (
int i2 = 0; i2 < residual_tasks.size(); ++i2) {
2090 const int t = residual_tasks[i2];
2091 if (helper->IsPresent(t)) {
2092 if (demand_is_fixed(t)) {
2093 if (helper->SizeIsFixed(t)) {
2094 energy_lp +=
ToDouble(helper->SizeMin(t) * demand_min(t));
2097 ToDouble(demand_min(t)) * lp_values[helper->SizeVars()[t]];
2099 }
else if (helper->SizeIsFixed(t)) {
2100 DCHECK(!demands.empty());
2101 energy_lp += lp_values[demands[t]] *
ToDouble(helper->SizeMin(t));
2103 DCHECK(!demands.empty());
2105 ToDouble(demand_min(t)) * lp_values[helper->SizeVars()[t]];
2106 energy_lp += lp_values[demands[t]] *
ToDouble(helper->SizeMin(t));
2107 energy_lp -=
ToDouble(demand_min(t) * helper->SizeMin(t));
2110 energy_lp += GetLiteralLpValue(helper->PresenceLiteral(t), lp_values,
2112 ToDouble(helper->SizeMin(t) * demand_min(t));
2115 min_of_starts =
std::min(min_of_starts, helper->StartMin(t));
2116 max_of_ends =
std::max(max_of_ends, helper->EndMax(t));
2119 const double relative_violation =
2120 energy_lp /
ToDouble((max_of_ends - min_of_starts) * capacity_max);
2121 if (relative_violation > max_relative_violation) {
2122 end_index_of_max_violation = i2;
2123 max_relative_violation = relative_violation;
2124 span_of_max_violation = max_of_ends - min_of_starts;
2128 if (end_index_of_max_violation == -1)
continue;
2131 bool cut_generated =
true;
2132 bool has_opt_cuts =
false;
2133 bool has_quadratic_cuts =
false;
2136 IntegerValue fixed_contribution(0);
2140 for (
int i2 = 0; i2 <= end_index_of_max_violation; ++i2) {
2141 const int t = residual_tasks[i2];
2142 if (helper->IsPresent(t)) {
2143 if (demand_is_fixed(t)) {
2144 if (helper->SizeIsFixed(t)) {
2145 fixed_contribution += helper->SizeMin(t) * demand_min(t);
2147 cut.
AddTerm(helper->SizeVars()[t], demand_min(t));
2149 }
else if (helper->SizeIsFixed(t)) {
2150 DCHECK(!demands.empty());
2151 cut.
AddTerm(demands[t], helper->SizeMin(t));
2153 DCHECK(!demands.empty());
2162 cut.
AddTerm(helper->SizeVars()[t], demand_min(t));
2163 cut.
AddTerm(demands[t], helper->SizeMin(t));
2165 fixed_contribution -= helper->SizeMin(t) * demand_min(t);
2166 has_quadratic_cuts =
true;
2169 has_opt_cuts =
true;
2170 if (!helper->SizeIsFixed(t) || !demand_is_fixed(t)) {
2171 has_quadratic_cuts =
true;
2174 helper->SizeMin(t) * demand_min(t))) {
2175 cut_generated =
false;
2182 if (cut_generated) {
2183 std::string full_name = cut_name;
2184 if (has_opt_cuts) full_name.append(
"_opt");
2185 if (has_quadratic_cuts) full_name.append(
"_quad");
2187 manager->
AddCut(cut.
Build(), cut_name, lp_values);
2194 const std::vector<IntervalVariable>& intervals,
2195 const IntegerVariable
capacity,
const std::vector<IntegerVariable>& demands,
2201 model->TakeOwnership(helper);
2203 result.
vars = demands;
2213 const std::vector<IntervalVariable>& intervals,
2214 const IntegerVariable
capacity,
const std::vector<IntegerVariable>& demands,
2220 model->TakeOwnership(helper);
2222 result.
vars = demands;
2240 if (trail->CurrentDecisionLevel() > 0)
return;
2242 std::vector<Event> events;
2245 for (
int i = 0; i < helper->
NumTasks(); ++i) {
2254 e1.interval_index = i;
2256 e1.demand = demands[i];
2261 e2.positive =
false;
2262 events.push_back(e1);
2263 events.push_back(e2);
2269 std::sort(events.begin(), events.end(),
2270 [](
const Event i,
const Event j) {
2271 if (i.time == j.time) {
2272 if (i.positive == j.positive) {
2273 return i.interval_index < j.interval_index;
2277 return i.time < j.time;
2280 std::vector<Event> cut_events;
2281 bool added_positive_event =
false;
2282 for (
const Event& e : events) {
2284 added_positive_event =
true;
2285 cut_events.push_back(e);
2288 if (added_positive_event && cut_events.size() > 1) {
2290 bool cut_generated =
true;
2294 for (
const Event& cut_event : cut_events) {
2295 if (helper->
IsPresent(cut_event.interval_index)) {
2296 cut.
AddTerm(cut_event.demand, IntegerValue(1));
2300 integer_trail->LowerBound(cut_event.demand));
2301 if (!cut_generated)
break;
2304 if (cut_generated) {
2307 manager->
AddCut(cut.
Build(),
"Cumulative", lp_values);
2312 for (
int i = 0; i < cut_events.size(); ++i) {
2313 if (cut_events[i].interval_index == e.interval_index) {
2316 cut_events[new_size] = cut_events[i];
2319 cut_events.resize(new_size);
2320 added_positive_event =
false;
2327 const std::vector<IntervalVariable>& intervals,
Model*
model) {
2332 model->TakeOwnership(helper);
2337 "NoOverlapEnergy", helper,
2344 const std::vector<IntervalVariable>& intervals,
Model*
model) {
2349 model->TakeOwnership(helper);
2356 [trail, helper,
model](
2369 for (
int index1 = 0; index1 < helper->NumTasks(); ++index1) {
2370 if (!helper->IsPresent(index1))
continue;
2371 for (
int index2 = index1 + 1; index2 < helper->NumTasks(); ++index2) {
2372 if (!helper->IsPresent(index2))
continue;
2375 if (helper->EndMax(index1) <= helper->StartMin(index2) ||
2376 helper->EndMax(index2) <= helper->StartMin(index1)) {
2380 const bool interval_1_can_precede_2 =
2381 helper->EndMin(index1) <= helper->StartMax(index2);
2382 const bool interval_2_can_precede_1 =
2383 helper->EndMin(index2) <= helper->StartMax(index1);
2385 if (interval_1_can_precede_2 && !interval_2_can_precede_1) {
2389 cut.
AddTerm(helper->EndVars()[index1], IntegerValue(1));
2390 cut.
AddTerm(helper->StartVars()[index2], IntegerValue(-1));
2391 manager->
AddCut(cut.
Build(),
"NoOverlapPrecedence", lp_values);
2392 }
else if (interval_2_can_precede_1 && !interval_1_can_precede_2) {
2396 cut.
AddTerm(helper->EndVars()[index2], IntegerValue(1));
2397 cut.
AddTerm(helper->StartVars()[index1], IntegerValue(-1));
2398 manager->
AddCut(cut.
Build(),
"NoOverlapPrecedence", lp_values);
2407 const std::vector<IntegerVariable>& base_variables,
Model*
model) {
2410 std::vector<IntegerVariable> variables;
2411 std::vector<Literal> literals;
2412 absl::flat_hash_map<LiteralIndex, IntegerVariable> positive_map;
2413 absl::flat_hash_map<LiteralIndex, IntegerVariable> negative_map;
2416 for (
const IntegerVariable
var : base_variables) {
2417 if (integer_trail->LowerBound(
var) != IntegerValue(0))
continue;
2418 if (integer_trail->UpperBound(
var) != IntegerValue(1))
continue;
2419 const LiteralIndex literal_index = encoder->GetAssociatedLiteral(
2422 variables.push_back(
var);
2423 literals.push_back(
Literal(literal_index));
2424 positive_map[literal_index] =
var;
2429 result.
vars = variables;
2432 [variables, literals, implication_graph, positive_map, negative_map,
2435 std::vector<double> packed_values;
2436 for (
int i = 0; i < literals.size(); ++i) {
2437 packed_values.push_back(lp_values[variables[i]]);
2439 const std::vector<std::vector<Literal>> at_most_ones =
2440 implication_graph->GenerateAtMostOnesWithLargeWeight(literals,
2443 for (
const std::vector<Literal>& at_most_one : at_most_ones) {
2449 for (
const Literal l : at_most_one) {
2451 builder.
AddTerm(positive_map.at(l.Index()), IntegerValue(1));
2454 builder.
AddTerm(negative_map.at(l.Index()), IntegerValue(-1));
2459 manager->
AddCut(builder.
Build(),
"clique", lp_values);