42 const double kMinCutViolation = 1e-4;
45 double GetLiteralLpValue(
48 const IntegerEncoder* encoder) {
49 const IntegerVariable direct_view = encoder->GetLiteralView(lit);
51 return lp_values[direct_view];
53 const IntegerVariable opposite_view = encoder->GetLiteralView(lit.Negated());
55 return 1.0 - lp_values[opposite_view];
61 LinearConstraint GenerateKnapsackCutForCover(
62 const std::vector<IntegerVariable>& vars,
63 const std::vector<IntegerValue>& coeffs,
const IntegerValue
upper_bound,
64 const IntegerTrail& integer_trail) {
65 CHECK_EQ(vars.size(), coeffs.size());
68 IntegerValue cut_upper_bound = IntegerValue(0);
69 IntegerValue max_coeff = coeffs[0];
72 for (
int i = 0; i < vars.size(); ++i) {
73 const IntegerValue var_upper_bound =
74 integer_trail.LevelZeroUpperBound(vars[i]);
75 cut_upper_bound += var_upper_bound;
76 cut.vars.push_back(vars[i]);
77 cut.coeffs.push_back(IntegerValue(1));
78 max_coeff =
std::max(max_coeff, coeffs[i]);
79 slack += coeffs[i] * var_upper_bound;
81 CHECK_GT(slack, 0.0) <<
"Invalid cover for knapsack cut.";
82 cut_upper_bound -=
CeilRatio(slack, max_coeff);
84 cut.ub = cut_upper_bound;
85 VLOG(2) <<
"Generated Knapsack Constraint:" << cut.DebugString();
89 bool SolutionSatisfiesConstraint(
90 const LinearConstraint& constraint,
93 const double tolerance = 1e-6;
94 return (activity <= constraint.ub.value() + tolerance &&
95 activity >= constraint.lb.value() - tolerance)
100 bool SmallRangeAndAllCoefficientsMagnitudeAreTheSame(
101 const LinearConstraint& constraint, IntegerTrail* integer_trail) {
102 if (constraint.vars.empty())
return true;
104 const int64_t magnitude = std::abs(constraint.coeffs[0].value());
105 for (
int i = 1; i < constraint.coeffs.size(); ++i) {
106 const IntegerVariable
var = constraint.vars[i];
107 if (integer_trail->LevelZeroUpperBound(
var) -
108 integer_trail->LevelZeroLowerBound(
var) >
112 if (std::abs(constraint.coeffs[i].value()) != magnitude) {
119 bool AllVarsTakeIntegerValue(
120 const std::vector<IntegerVariable> vars,
122 for (IntegerVariable
var : vars) {
123 if (std::abs(lp_values[
var] - std::round(lp_values[
var])) > 1e-6) {
139 int GetSmallestCoverSize(
const LinearConstraint& constraint,
140 const IntegerTrail& integer_trail) {
141 IntegerValue ub = constraint.ub;
142 std::vector<IntegerValue> sorted_terms;
143 for (
int i = 0; i < constraint.vars.size(); ++i) {
144 const IntegerValue coeff = constraint.coeffs[i];
145 const IntegerVariable
var = constraint.vars[i];
146 const IntegerValue var_ub = integer_trail.LevelZeroUpperBound(
var);
147 const IntegerValue var_lb = integer_trail.LevelZeroLowerBound(
var);
148 ub -= var_lb * coeff;
149 sorted_terms.push_back(coeff * (var_ub - var_lb));
151 std::sort(sorted_terms.begin(), sorted_terms.end(),
152 std::greater<IntegerValue>());
153 int smallest_cover_size = 0;
154 IntegerValue sorted_term_sum = IntegerValue(0);
155 while (sorted_term_sum <= ub &&
156 smallest_cover_size < constraint.vars.size()) {
157 sorted_term_sum += sorted_terms[smallest_cover_size++];
159 return smallest_cover_size;
162 bool ConstraintIsEligibleForLifting(
const LinearConstraint& constraint,
163 const IntegerTrail& integer_trail) {
164 for (
const IntegerVariable
var : constraint.vars) {
165 if (integer_trail.LevelZeroLowerBound(
var) != IntegerValue(0) ||
166 integer_trail.LevelZeroUpperBound(
var) != IntegerValue(1)) {
177 const std::vector<IntegerValue>& cut_vars_original_coefficients,
180 std::set<IntegerVariable> vars_in_cut;
181 for (IntegerVariable
var : cut->
vars) {
182 vars_in_cut.insert(
var);
185 std::vector<std::pair<IntegerValue, IntegerVariable>> non_zero_vars;
186 std::vector<std::pair<IntegerValue, IntegerVariable>> zero_vars;
187 for (
int i = 0; i < constraint.
vars.size(); ++i) {
188 const IntegerVariable
var = constraint.
vars[i];
193 if (vars_in_cut.find(
var) != vars_in_cut.end())
continue;
194 const IntegerValue coeff = constraint.
coeffs[i];
195 if (lp_values[
var] <= 1e-6) {
196 zero_vars.push_back({coeff,
var});
198 non_zero_vars.push_back({coeff,
var});
204 std::sort(non_zero_vars.rbegin(), non_zero_vars.rend());
205 std::sort(zero_vars.rbegin(), zero_vars.rend());
207 std::vector<std::pair<IntegerValue, IntegerVariable>> lifting_sequence(
208 std::move(non_zero_vars));
210 lifting_sequence.insert(lifting_sequence.end(), zero_vars.begin(),
214 std::vector<double> lifting_profits;
215 std::vector<double> lifting_weights;
216 for (
int i = 0; i < cut->
vars.size(); ++i) {
217 lifting_profits.push_back(cut->
coeffs[i].value());
218 lifting_weights.push_back(cut_vars_original_coefficients[i].
value());
222 bool is_lifted =
false;
223 bool is_solution_optimal =
false;
225 for (
auto entry : lifting_sequence) {
226 is_solution_optimal =
false;
227 const IntegerValue var_original_coeff = entry.first;
228 const IntegerVariable
var = entry.second;
229 const IntegerValue lifting_capacity = constraint.
ub - entry.first;
230 if (lifting_capacity <= IntegerValue(0))
continue;
231 knapsack_solver.
Init(lifting_profits, lifting_weights,
232 lifting_capacity.value());
238 const double knapsack_upper_bound =
240 const IntegerValue cut_coeff = cut->
ub - knapsack_upper_bound;
241 if (cut_coeff > IntegerValue(0)) {
244 cut->
coeffs.push_back(cut_coeff);
245 lifting_profits.push_back(cut_coeff.value());
246 lifting_weights.push_back(var_original_coeff.value());
256 IntegerValue ub = constraint.
ub;
258 for (
int i = 0; i < constraint.
vars.size(); ++i) {
259 const IntegerVariable
var = constraint.
vars[i];
261 const IntegerValue coeff = constraint.
coeffs[i];
262 if (var_ub.value() - lp_values[
var] <= 1.0 - kMinCutViolation) {
263 constraint_with_left_vars.
vars.push_back(
var);
264 constraint_with_left_vars.
coeffs.push_back(coeff);
268 ub -= coeff * var_lb;
271 constraint_with_left_vars.
ub = ub;
272 constraint_with_left_vars.
lb = constraint.
lb;
273 return constraint_with_left_vars;
278 IntegerValue term_sum = IntegerValue(0);
279 for (
int i = 0; i < constraint.
vars.size(); ++i) {
280 const IntegerVariable
var = constraint.
vars[i];
282 const IntegerValue coeff = constraint.
coeffs[i];
283 term_sum += coeff * var_ub;
285 if (term_sum <= constraint.
ub) {
286 VLOG(2) <<
"Filtered by cover filter";
296 std::vector<double> variable_upper_bound_distances;
297 for (
const IntegerVariable
var : preprocessed_constraint.
vars) {
299 variable_upper_bound_distances.push_back(var_ub.value() - lp_values[
var]);
302 const int smallest_cover_size =
303 GetSmallestCoverSize(preprocessed_constraint, integer_trail);
306 variable_upper_bound_distances.begin(),
307 variable_upper_bound_distances.begin() + smallest_cover_size - 1,
308 variable_upper_bound_distances.end());
309 double cut_lower_bound = 0.0;
310 for (
int i = 0; i < smallest_cover_size; ++i) {
311 cut_lower_bound += variable_upper_bound_distances[i];
313 if (cut_lower_bound >= 1.0 - kMinCutViolation) {
314 VLOG(2) <<
"Filtered by kappa heuristic";
323 std::sort(items.begin(), items.end(), std::greater<KnapsackItem>());
327 if (item.weight <= left_capacity) {
328 profit += item.profit;
329 left_capacity -= item.weight;
331 profit += (left_capacity / item.weight) * item.profit;
342 std::vector<KnapsackItem> items;
343 double capacity = -constraint.
ub.value() - 1.0;
344 double sum_variable_profit = 0;
345 for (
int i = 0; i < constraint.
vars.size(); ++i) {
346 const IntegerVariable
var = constraint.
vars[i];
349 const IntegerValue coeff = constraint.
coeffs[i];
351 item.
profit = var_ub.value() - lp_values[
var];
352 item.
weight = (coeff * (var_ub - var_lb)).value();
353 items.push_back(item);
355 sum_variable_profit += item.
profit;
360 if (sum_variable_profit - 1.0 + kMinCutViolation < 0.0)
return false;
363 const double knapsack_upper_bound =
365 if (knapsack_upper_bound < sum_variable_profit - 1.0 + kMinCutViolation) {
366 VLOG(2) <<
"Filtered by knapsack upper bound";
391 std::vector<LinearConstraint>* knapsack_constraints,
397 if (SmallRangeAndAllCoefficientsMagnitudeAreTheSame(constraint,
405 for (
int i = 0; i < constraint.
vars.size(); ++i) {
406 const IntegerVariable
var = constraint.
vars[i];
407 const IntegerValue coeff = constraint.
coeffs[i];
408 if (coeff > IntegerValue(0)) {
414 canonical_knapsack_form.
ub = constraint.
ub;
416 knapsack_constraints->push_back(canonical_knapsack_form);
423 for (
int i = 0; i < constraint.
vars.size(); ++i) {
424 const IntegerVariable
var = constraint.
vars[i];
425 const IntegerValue coeff = constraint.
coeffs[i];
426 if (coeff > IntegerValue(0)) {
432 canonical_knapsack_form.
ub = -constraint.
lb;
434 knapsack_constraints->push_back(canonical_knapsack_form);
440 const std::vector<LinearConstraint>& base_constraints,
441 const std::vector<IntegerVariable>& vars,
Model*
model) {
446 std::vector<LinearConstraint> knapsack_constraints;
454 if (constraint.vars.size() <= 2)
continue;
458 VLOG(1) <<
"#knapsack constraints: " << knapsack_constraints.size();
468 result.
generate_cuts = [implied_bounds_processor, knapsack_constraints, vars,
469 model, integer_trail](
476 if (AllVarsTakeIntegerValue(vars, lp_values))
return;
479 "Knapsack on demand cover cut generator");
480 int64_t skipped_constraints = 0;
487 VLOG(2) <<
"Processing constraint: " << constraint.DebugString();
489 mutable_constraint = constraint;
491 lp_values, &mutable_constraint);
497 if (preprocessed_constraint.
vars.empty())
continue;
501 skipped_constraints++;
506 std::vector<double> profits;
507 profits.reserve(preprocessed_constraint.
vars.size());
510 std::vector<double> weights;
511 weights.reserve(preprocessed_constraint.
vars.size());
513 double capacity = -preprocessed_constraint.
ub.value() - 1.0;
519 double sum_variable_profit = 0;
523 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
524 const IntegerVariable
var = preprocessed_constraint.
vars[i];
528 const double variable_profit = var_ub - lp_values[
var];
529 profits.push_back(variable_profit);
531 sum_variable_profit += variable_profit;
534 weights.push_back(
weight);
539 std::vector<IntegerVariable> cut_vars;
540 std::vector<IntegerValue> cut_vars_original_coefficients;
542 VLOG(2) <<
"Knapsack size: " << profits.size();
546 const double time_limit_for_knapsack_solver =
552 bool is_solution_optimal =
false;
554 sum_variable_profit - 1.0 + kMinCutViolation);
558 auto time_limit_for_solver =
559 absl::make_unique<TimeLimit>(time_limit_for_knapsack_solver);
560 const double sum_of_distance_to_ub_for_vars_in_cover =
561 sum_variable_profit -
562 knapsack_solver.
Solve(time_limit_for_solver.get(),
563 &is_solution_optimal);
564 if (is_solution_optimal) {
565 VLOG(2) <<
"Knapsack Optimal solution found yay !";
567 if (time_limit_for_solver->LimitReached()) {
568 VLOG(1) <<
"Knapsack Solver run out of time limit.";
570 if (sum_of_distance_to_ub_for_vars_in_cover < 1.0 - kMinCutViolation) {
573 IntegerValue constraint_ub_for_cut = preprocessed_constraint.
ub;
574 std::set<IntegerVariable> vars_in_cut;
575 for (
int i = 0; i < preprocessed_constraint.
vars.size(); ++i) {
576 const IntegerVariable
var = preprocessed_constraint.
vars[i];
579 cut_vars.push_back(
var);
580 cut_vars_original_coefficients.push_back(
coefficient);
581 vars_in_cut.insert(
var);
588 cut_vars, cut_vars_original_coefficients, constraint_ub_for_cut,
592 bool is_lifted =
false;
593 if (ConstraintIsEligibleForLifting(cut, *integer_trail)) {
595 cut_vars_original_coefficients, *integer_trail,
601 CHECK(!SolutionSatisfiesConstraint(cut, lp_values));
602 manager->AddCut(cut, is_lifted ?
"LiftedKnapsack" :
"Knapsack",
606 if (skipped_constraints > 0) {
607 VLOG(2) <<
"Skipped constraints: " << skipped_constraints;
618 IntegerValue
GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor,
619 IntegerValue max_t) {
621 return rhs_remainder == 0
627 IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t,
628 IntegerValue max_scaling) {
641 const IntegerValue size = divisor - rhs_remainder;
642 if (max_scaling == 1 || size == 1) {
646 return [t, divisor](IntegerValue coeff) {
649 }
else if (size <= max_scaling) {
650 return [size, rhs_remainder, t, divisor](IntegerValue coeff) {
652 const IntegerValue remainder = t * coeff -
ratio * divisor;
653 const IntegerValue diff = remainder - rhs_remainder;
656 }
else if (max_scaling.value() * rhs_remainder.value() < divisor) {
666 return [t, divisor, max_scaling](IntegerValue coeff) {
668 const IntegerValue remainder = t * coeff -
ratio * divisor;
669 const IntegerValue bucket =
FloorRatio(remainder * max_scaling, divisor);
670 return max_scaling *
ratio + bucket;
695 return [size, rhs_remainder, t, divisor, max_scaling](IntegerValue coeff) {
697 const IntegerValue remainder = t * coeff -
ratio * divisor;
698 const IntegerValue diff = remainder - rhs_remainder;
699 const IntegerValue bucket =
700 diff > 0 ?
CeilRatio(diff * (max_scaling - 1), size)
702 return max_scaling *
ratio + bucket;
715 const int size = lp_values.size();
716 if (size == 0)
return;
729 relevant_indices_.clear();
730 relevant_lp_values_.clear();
731 relevant_coeffs_.clear();
732 relevant_bound_diffs_.clear();
734 adjusted_coeffs_.clear();
737 IntegerValue max_magnitude(0);
738 for (
int i = 0; i < size; ++i) {
741 max_magnitude =
std::max(max_magnitude, magnitude);
747 bool overflow =
false;
748 change_sign_at_postprocessing_.assign(size,
false);
749 for (
int i = 0; i < size; ++i) {
750 if (cut->
coeffs[i] == 0)
continue;
755 double lp_value = lp_values[i];
758 const IntegerValue bound_diff =
759 IntegerValue(
CapSub(ub.value(), lb.value()));
772 const double lb_dist = std::abs(lp_value -
ToDouble(lb));
773 const double ub_dist = std::abs(lp_value -
ToDouble(ub));
776 if ((bias * lb_dist > ub_dist && cut->
coeffs[i] < 0) ||
777 (lb_dist > bias * ub_dist && cut->
coeffs[i] > 0)) {
778 change_sign_at_postprocessing_[i] =
true;
780 lp_value = -lp_value;
795 if (bound_diff == 0) {
796 cut->
coeffs[i] = IntegerValue(0);
800 if (std::abs(lp_value) > 1e-2) {
801 relevant_coeffs_.push_back(cut->
coeffs[i]);
802 relevant_indices_.push_back(i);
803 relevant_lp_values_.push_back(lp_value);
804 relevant_bound_diffs_.push_back(bound_diff);
805 divisors_.push_back(magnitude);
810 if (relevant_coeffs_.empty()) {
811 VLOG(2) <<
"Issue, nothing to cut.";
831 double best_scaled_violation = 0.01;
832 const IntegerValue remainder_threshold(max_magnitude / 1000);
843 if (overflow || max_magnitude >= threshold) {
844 VLOG(2) <<
"Issue, overflow.";
848 const IntegerValue max_t = threshold / max_magnitude;
859 const IntegerValue divisor_threshold = max_magnitude / 10;
860 for (
int i = 0; i < divisors_.size(); ++i) {
861 if (divisors_[i] <= divisor_threshold)
continue;
862 divisors_[new_size++] = divisors_[i];
864 divisors_.resize(new_size);
871 IntegerValue best_divisor(0);
872 for (
const IntegerValue divisor : divisors_) {
874 const IntegerValue initial_rhs_remainder =
876 if (initial_rhs_remainder <= remainder_threshold)
continue;
878 IntegerValue temp_ub = cut->
ub;
879 adjusted_coeffs_.clear();
896 const IntegerValue adjust_threshold =
897 (divisor - initial_rhs_remainder - 1) / IntegerValue(size);
898 if (adjust_threshold > 0) {
902 bool early_abort =
false;
903 double loss_lb = 0.0;
904 const double threshold =
ToDouble(initial_rhs_remainder);
906 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
908 const IntegerValue coeff = relevant_coeffs_[i];
909 const IntegerValue remainder =
910 CeilRatio(coeff, divisor) * divisor - coeff;
912 if (divisor - remainder <= initial_rhs_remainder) {
915 loss_lb +=
ToDouble(divisor - remainder) * relevant_lp_values_[i];
916 if (loss_lb >= threshold) {
923 const IntegerValue diff = relevant_bound_diffs_[i];
924 if (remainder > 0 && remainder <= adjust_threshold &&
925 CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
926 temp_ub += remainder * diff;
927 adjusted_coeffs_.push_back({i, coeff + remainder});
931 if (early_abort)
continue;
935 const IntegerValue rhs_remainder =
936 temp_ub -
FloorRatio(temp_ub, divisor) * divisor;
937 if (rhs_remainder == 0)
continue;
940 rhs_remainder, divisor,
GetFactorT(rhs_remainder, divisor, max_t),
951 const double threshold = scaling *
ToDouble(rhs_remainder);
958 double violation = -
ToDouble(f(temp_ub));
959 double l2_norm = 0.0;
960 bool early_abort =
false;
961 int adjusted_coeffs_index = 0;
962 for (
int i = 0; i < relevant_coeffs_.size(); ++i) {
963 IntegerValue coeff = relevant_coeffs_[i];
966 if (adjusted_coeffs_index < adjusted_coeffs_.size() &&
967 adjusted_coeffs_[adjusted_coeffs_index].first == i) {
968 coeff = adjusted_coeffs_[adjusted_coeffs_index].second;
969 adjusted_coeffs_index++;
972 if (coeff == 0)
continue;
973 const IntegerValue new_coeff = f(coeff);
974 const double new_coeff_double =
ToDouble(new_coeff);
975 const double lp_value = relevant_lp_values_[i];
977 l2_norm += new_coeff_double * new_coeff_double;
978 violation += new_coeff_double * lp_value;
979 loss += (scaling *
ToDouble(coeff) - new_coeff_double) * lp_value;
980 if (loss >= threshold) {
985 if (early_abort)
continue;
989 violation /= sqrt(l2_norm);
990 if (violation > best_scaled_violation) {
991 best_scaled_violation = violation;
992 best_divisor = divisor;
996 if (best_divisor == 0) {
1006 const IntegerValue initial_rhs_remainder =
1008 const IntegerValue adjust_threshold =
1009 (best_divisor - initial_rhs_remainder - 1) / IntegerValue(size);
1010 if (adjust_threshold > 0) {
1011 for (
int i = 0; i < relevant_indices_.size(); ++i) {
1012 const int index = relevant_indices_[i];
1013 const IntegerValue diff = relevant_bound_diffs_[i];
1014 if (diff > adjust_threshold)
continue;
1018 const IntegerValue remainder =
1019 CeilRatio(coeff, best_divisor) * best_divisor - coeff;
1020 if (
CapProd(diff.value(), remainder.value()) <= adjust_threshold) {
1021 cut->
ub += remainder * diff;
1034 const IntegerValue rhs_remainder =
1036 IntegerValue factor_t =
GetFactorT(rhs_remainder, best_divisor, max_t);
1043 remainders_.clear();
1044 for (
int i = 0; i < size; ++i) {
1045 const IntegerValue coeff = cut->
coeffs[i];
1046 const IntegerValue r =
1047 coeff -
FloorRatio(coeff, best_divisor) * best_divisor;
1048 if (r > rhs_remainder) remainders_.push_back(r);
1051 if (remainders_.size() <= 100) {
1053 for (
const IntegerValue r : remainders_) {
1054 best_rs_.push_back(f(r));
1056 IntegerValue best_d = f(best_divisor);
1061 for (
const IntegerValue t :
1062 {IntegerValue(1),
GetFactorT(rhs_remainder, best_divisor, max_t)}) {
1063 for (IntegerValue s(2); s <= options.
max_scaling; ++s) {
1066 int num_strictly_better = 0;
1068 const IntegerValue d = g(best_divisor);
1069 for (
int i = 0; i < best_rs_.size(); ++i) {
1070 const IntegerValue temp = g(remainders_[i]);
1071 if (temp * best_d < best_rs_[i] * d)
break;
1072 if (temp * best_d > best_rs_[i] * d) num_strictly_better++;
1073 rs_.push_back(temp);
1075 if (rs_.size() == best_rs_.size() && num_strictly_better > 0) {
1087 cut->
ub = f(cut->
ub);
1092 num_lifted_booleans_ = 0;
1093 if (ib_processor !=
nullptr) {
1094 for (
int i = 0; i < size; ++i) {
1095 const IntegerValue coeff = cut->
coeffs[i];
1096 if (coeff == 0)
continue;
1098 IntegerVariable
var = cut->
vars[i];
1099 if (change_sign_at_postprocessing_[i]) {
1118 const IntegerValue coeff_b =
1121 if (coeff_b == 0)
continue;
1123 ++num_lifted_booleans_;
1125 tmp_terms_.push_back({info.
bool_var, coeff_b});
1127 tmp_terms_.push_back({info.
bool_var, -coeff_b});
1128 cut->
ub =
CapAdd(-coeff_b.value(), cut->
ub.value());
1137 for (
int i = 0; i < size; ++i) {
1138 IntegerValue coeff = cut->
coeffs[i];
1139 if (coeff == 0)
continue;
1141 if (coeff == 0)
continue;
1142 if (change_sign_at_postprocessing_[i]) {
1143 cut->
ub = IntegerValue(
1145 tmp_terms_.push_back({cut->
vars[i], -coeff});
1147 cut->
ub = IntegerValue(
1149 tmp_terms_.push_back({cut->
vars[i], coeff});
1163 const int base_size = lp_values.size();
1169 IntegerValue rhs = base_ct.
ub;
1170 IntegerValue sum_of_diff(0);
1171 IntegerValue max_base_magnitude(0);
1172 for (
int i = 0; i < base_size; ++i) {
1173 const IntegerValue coeff = base_ct.
coeffs[i];
1174 const IntegerValue positive_coeff =
IntTypeAbs(coeff);
1175 max_base_magnitude =
std::max(max_base_magnitude, positive_coeff);
1177 if (!
AddProductTo(positive_coeff, bound_diff, &sum_of_diff)) {
1180 const IntegerValue diff = positive_coeff * bound_diff;
1194 double activity = 0.0;
1196 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1197 if (a.dist_to_max_value == b.dist_to_max_value) {
1199 return a.positive_coeff < b.positive_coeff;
1201 return a.dist_to_max_value <
b.dist_to_max_value;
1203 for (
int i = 0; i < terms_.size(); ++i) {
1204 const Term& term = terms_[i];
1205 activity += term.dist_to_max_value;
1214 if (activity > 1.0) {
1229 if (rhs >= 0)
return false;
1230 if (new_size == 0)
return false;
1238 terms_.resize(new_size);
1239 std::sort(terms_.begin(), terms_.end(), [](
const Term&
a,
const Term&
b) {
1240 if (a.positive_coeff == b.positive_coeff) {
1241 return a.dist_to_max_value > b.dist_to_max_value;
1243 return a.positive_coeff >
b.positive_coeff;
1245 in_cut_.assign(base_ct.vars.size(),
false);
1248 cut_.ub = IntegerValue(-1);
1249 IntegerValue max_coeff(0);
1250 for (
const Term term : terms_) {
1251 if (term.diff + rhs < 0) {
1255 in_cut_[term.index] =
true;
1256 max_coeff =
std::max(max_coeff, term.positive_coeff);
1257 cut_.vars.push_back(base_ct.vars[term.index]);
1258 if (base_ct.coeffs[term.index] > 0) {
1259 cut_.coeffs.push_back(IntegerValue(1));
1262 cut_.coeffs.push_back(IntegerValue(-1));
1271 if (max_coeff == 0)
return true;
1272 if (max_coeff < -rhs) {
1273 const IntegerValue m =
FloorRatio(-rhs - 1, max_coeff);
1274 rhs += max_coeff * m;
1293 const IntegerValue slack = -rhs;
1294 const IntegerValue remainder = max_coeff - slack;
1296 const IntegerValue max_scaling(
std::min(
1299 IntegerValue(1), max_scaling);
1301 const IntegerValue scaling = f(max_coeff);
1303 for (
int i = 0; i < cut_.coeffs.size(); ++i) cut_.coeffs[i] *= scaling;
1308 for (
int i = 0; i < base_size; ++i) {
1309 if (in_cut_[i])
continue;
1310 const IntegerValue positive_coeff =
IntTypeAbs(base_ct.coeffs[i]);
1311 const IntegerValue new_coeff = f(positive_coeff);
1312 if (new_coeff == 0)
continue;
1315 if (base_ct.coeffs[i] > 0) {
1317 cut_.coeffs.push_back(new_coeff);
1318 cut_.vars.push_back(base_ct.vars[i]);
1322 cut_.coeffs.push_back(-new_coeff);
1323 cut_.vars.push_back(base_ct.vars[i]);
1337 result.
vars = {z, x, y};
1341 [z, x, y, integer_trail](
1350 const int64_t kMaxSafeInteger = (int64_t{1} << 53) - 1;
1352 if (
CapProd(x_ub, y_ub) >= kMaxSafeInteger) {
1353 VLOG(3) <<
"Potential overflow in PositiveMultiplicationCutGenerator";
1357 const double x_lp_value = lp_values[x];
1358 const double y_lp_value = lp_values[y];
1359 const double z_lp_value = lp_values[z];
1367 auto try_add_above_cut =
1368 [manager, z_lp_value, x_lp_value, y_lp_value, x, y, z, &lp_values](
1369 int64_t x_coeff, int64_t y_coeff, int64_t rhs) {
1370 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff >=
1371 rhs + kMinCutViolation) {
1373 cut.
vars.push_back(z);
1374 cut.
coeffs.push_back(IntegerValue(-1));
1376 cut.
vars.push_back(x);
1377 cut.
coeffs.push_back(IntegerValue(x_coeff));
1380 cut.
vars.push_back(y);
1381 cut.
coeffs.push_back(IntegerValue(y_coeff));
1384 cut.
ub = IntegerValue(rhs);
1385 manager->AddCut(cut,
"PositiveProduct", lp_values);
1390 auto try_add_below_cut =
1391 [manager, z_lp_value, x_lp_value, y_lp_value, x, y, z, &lp_values](
1392 int64_t x_coeff, int64_t y_coeff, int64_t rhs) {
1393 if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff <=
1394 rhs - kMinCutViolation) {
1396 cut.
vars.push_back(z);
1397 cut.
coeffs.push_back(IntegerValue(-1));
1399 cut.
vars.push_back(x);
1400 cut.
coeffs.push_back(IntegerValue(x_coeff));
1403 cut.
vars.push_back(y);
1404 cut.
coeffs.push_back(IntegerValue(y_coeff));
1406 cut.
lb = IntegerValue(rhs);
1408 manager->AddCut(cut,
"PositiveProduct", lp_values);
1419 try_add_above_cut(y_lb, x_lb, x_lb * y_lb);
1420 try_add_above_cut(y_ub, x_ub, x_ub * y_ub);
1421 try_add_below_cut(y_ub, x_lb, x_lb * y_ub);
1422 try_add_below_cut(y_lb, x_ub, x_ub * y_lb);
1431 result.
vars = {y, x};
1435 [y, x, integer_trail](
1441 if (x_lb == x_ub)
return;
1444 if (x_ub > (int64_t{1} << 31))
return;
1447 const double y_lp_value = lp_values[y];
1448 const double x_lp_value = lp_values[x];
1453 const int64_t y_lb = x_lb * x_lb;
1454 const int64_t above_slope = x_ub + x_lb;
1455 const double max_lp_y = y_lb + above_slope * (x_lp_value - x_lb);
1456 if (y_lp_value >= max_lp_y + kMinCutViolation) {
1459 above_cut.
vars.push_back(y);
1460 above_cut.
coeffs.push_back(IntegerValue(1));
1461 above_cut.
vars.push_back(x);
1462 above_cut.
coeffs.push_back(IntegerValue(-above_slope));
1464 above_cut.
ub = IntegerValue(-x_lb * x_ub);
1465 manager->AddCut(above_cut,
"SquareUpper", lp_values);
1474 const int64_t x_floor =
static_cast<int64_t
>(std::floor(x_lp_value));
1475 const int64_t below_slope = 2 * x_floor + 1;
1476 const double min_lp_y =
1477 below_slope * x_lp_value - x_floor - x_floor * x_floor;
1478 if (min_lp_y >= y_lp_value + kMinCutViolation) {
1482 below_cut.
vars.push_back(y);
1483 below_cut.
coeffs.push_back(IntegerValue(1));
1484 below_cut.
vars.push_back(x);
1485 below_cut.
coeffs.push_back(-IntegerValue(below_slope));
1486 below_cut.
lb = IntegerValue(-x_floor - x_floor * x_floor);
1488 manager->AddCut(below_cut,
"SquareLower", lp_values);
1495 void ImpliedBoundsProcessor::ProcessUpperBoundedConstraint(
1498 ProcessUpperBoundedConstraintWithSlackCreation(
1499 false, IntegerVariable(0), lp_values,
1504 ImpliedBoundsProcessor::GetCachedImpliedBoundInfo(IntegerVariable
var) {
1505 auto it = cache_.find(
var);
1506 if (it != cache_.end())
return it->second;
1511 ImpliedBoundsProcessor::ComputeBestImpliedBound(
1512 IntegerVariable
var,
1514 auto it = cache_.find(
var);
1515 if (it != cache_.end())
return it->second;
1516 BestImpliedBoundInfo result;
1517 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1519 implied_bounds_->GetImpliedBounds(
var)) {
1531 const IntegerValue diff = entry.lower_bound - lb;
1533 const double bool_lp_value = entry.is_positive
1534 ? lp_values[entry.literal_view]
1535 : 1.0 - lp_values[entry.literal_view];
1536 const double slack_lp_value =
1541 if (slack_lp_value < -1e-4) {
1542 LinearConstraint ib_cut;
1544 std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
1545 if (entry.is_positive) {
1547 terms.push_back({entry.literal_view, diff});
1548 terms.push_back({
var, IntegerValue(-1)});
1552 terms.push_back({entry.literal_view, -diff});
1553 terms.push_back({
var, IntegerValue(-1)});
1554 ib_cut.ub = -entry.lower_bound;
1557 ib_cut_pool_.AddCut(std::move(ib_cut),
"IB", lp_values);
1563 if (slack_lp_value + 1e-4 < result.slack_lp_value ||
1564 (slack_lp_value < result.slack_lp_value + 1e-4 &&
1565 diff > result.bound_diff)) {
1566 result.bool_lp_value = bool_lp_value;
1567 result.slack_lp_value = slack_lp_value;
1569 result.bound_diff = diff;
1570 result.is_positive = entry.is_positive;
1571 result.bool_var = entry.literal_view;
1574 cache_[
var] = result;
1579 void ImpliedBoundsProcessor::SeparateSomeImpliedBoundCuts(
1581 for (
const IntegerVariable
var :
1582 implied_bounds_->VariablesWithImpliedBounds()) {
1584 ComputeBestImpliedBound(
var, lp_values);
1588 void ImpliedBoundsProcessor::ProcessUpperBoundedConstraintWithSlackCreation(
1589 bool substitute_only_inner_variables, IntegerVariable first_slack,
1593 IntegerValue new_ub = cut->
ub;
1594 bool changed =
false;
1597 int64_t overflow_detection = 0;
1599 const int size = cut->
vars.size();
1600 for (
int i = 0; i < size; ++i) {
1601 IntegerVariable
var = cut->
vars[i];
1602 IntegerValue coeff = cut->
coeffs[i];
1624 const int old_size = tmp_terms_.size();
1627 bool keep_term =
false;
1641 if (substitute_only_inner_variables) {
1642 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1643 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
1644 if (lp_values[
var] -
ToDouble(lb) < 1e-2) keep_term =
true;
1645 if (
ToDouble(ub) - lp_values[
var] < 1e-2) keep_term =
true;
1649 if (slack_infos ==
nullptr) {
1656 tmp_terms_.push_back({
var, coeff});
1659 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
1660 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
1665 slack_info.
ub = ub - lb;
1671 VLOG(2) <<
"Overflow";
1674 if (slack_infos !=
nullptr) {
1675 tmp_terms_.push_back({first_slack, coeff});
1679 slack_info.
terms.push_back({
var, IntegerValue(1)});
1682 slack_infos->push_back(slack_info);
1689 VLOG(2) <<
"Overflow";
1692 if (slack_infos !=
nullptr) {
1693 tmp_terms_.push_back({first_slack, coeff});
1697 slack_info.
terms.push_back({
var, IntegerValue(1)});
1700 slack_infos->push_back(slack_info);
1708 for (
int i = old_size; i < tmp_terms_.size(); ++i) {
1709 overflow_detection =
1710 CapAdd(overflow_detection, std::abs(tmp_terms_[i].second.value()));
1715 VLOG(2) <<
"Overflow";
1718 if (!changed)
return;
1729 bool ImpliedBoundsProcessor::DebugSlack(IntegerVariable first_slack,
1732 const std::vector<SlackInfo>& info) {
1734 IntegerValue new_ub = cut.
ub;
1735 for (
int i = 0; i < cut.
vars.size(); ++i) {
1737 if (cut.
vars[i] < first_slack) {
1738 tmp_terms_.push_back({cut.
vars[i], cut.
coeffs[i]});
1743 const IntegerValue multiplier = cut.
coeffs[i];
1744 const int index = (cut.
vars[i].value() - first_slack.value()) / 2;
1745 for (
const std::pair<IntegerVariable, IntegerValue>& term :
1746 info[
index].terms) {
1747 tmp_terms_.push_back({term.first, term.second * multiplier});
1749 new_ub -= multiplier * info[
index].offset;
1754 tmp_cut.
ub = new_ub;
1764 for (
int i = 0; i < initial_cut.
vars.size(); ++i) {
1765 tmp_terms_.push_back({initial_cut.
vars[i], initial_cut.
coeffs[i]});
1768 tmp_copy.
ub = new_ub;
1772 if (tmp_cut == tmp_copy)
return true;
1783 void TryToGenerateAllDiffCut(
1784 const std::vector<std::pair<double, IntegerVariable>>& sorted_vars_lp,
1789 std::vector<IntegerVariable> current_set_vars;
1791 for (
auto value_var : sorted_vars_lp) {
1792 sum += value_var.first;
1793 const IntegerVariable
var = value_var.second;
1798 current_set_vars.push_back(
var);
1799 const int64_t required_min_sum =
1801 const int64_t required_max_sum =
1803 if (sum < required_min_sum || sum > required_max_sum) {
1805 for (IntegerVariable
var : current_set_vars) {
1808 cut.
lb = IntegerValue(required_min_sum);
1809 cut.
ub = IntegerValue(required_max_sum);
1810 manager->
AddCut(cut,
"all_diff", lp_values);
1814 current_set_vars.clear();
1815 current_union =
Domain();
1823 const std::vector<IntegerVariable>& vars,
Model*
model) {
1829 [vars, integer_trail, trail](
1835 if (trail->CurrentDecisionLevel() > 0)
return;
1836 std::vector<std::pair<double, IntegerVariable>> sorted_vars;
1837 for (
const IntegerVariable
var : vars) {
1842 sorted_vars.push_back(std::make_pair(lp_values[
var],
var));
1844 std::sort(sorted_vars.begin(), sorted_vars.end());
1845 TryToGenerateAllDiffCut(sorted_vars, *integer_trail, lp_values,
1848 std::reverse(sorted_vars.begin(), sorted_vars.end());
1849 TryToGenerateAllDiffCut(sorted_vars, *integer_trail, lp_values,
1852 VLOG(1) <<
"Created all_diff cut generator of size: " << vars.size();
1858 IntegerValue MaxCornerDifference(
const IntegerVariable
var,
1859 const IntegerValue w1_i,
1860 const IntegerValue w2_i,
1861 const IntegerTrail& integer_trail) {
1862 const IntegerValue lb = integer_trail.LevelZeroLowerBound(
var);
1863 const IntegerValue ub = integer_trail.LevelZeroUpperBound(
var);
1864 return std::max((w2_i - w1_i) * lb, (w2_i - w1_i) * ub);
1873 IntegerValue MPlusCoefficient(
1874 const std::vector<IntegerVariable>& x_vars,
1875 const std::vector<LinearExpression>& exprs,
1877 const int max_index,
const IntegerTrail& integer_trail) {
1878 IntegerValue coeff = exprs[max_index].offset;
1881 for (
const IntegerVariable
var : x_vars) {
1882 const int target_index = variable_partition[
var];
1883 if (max_index != target_index) {
1884 coeff += MaxCornerDifference(
1895 double ComputeContribution(
1896 const IntegerVariable xi_var,
const std::vector<IntegerVariable>& z_vars,
1897 const std::vector<LinearExpression>& exprs,
1899 const IntegerTrail& integer_trail,
const int target_index) {
1901 CHECK_LT(target_index, exprs.size());
1902 const LinearExpression& target_expr = exprs[target_index];
1903 const double xi_value = lp_values[xi_var];
1905 double contrib = wt_i.value() * xi_value;
1906 for (
int expr_index = 0; expr_index < exprs.size(); ++expr_index) {
1907 if (expr_index == target_index)
continue;
1908 const LinearExpression& max_expr = exprs[expr_index];
1909 const double z_max_value = lp_values[z_vars[expr_index]];
1910 const IntegerValue corner_value = MaxCornerDifference(
1913 contrib += corner_value.value() * z_max_value;
1920 const IntegerVariable target,
const std::vector<LinearExpression>& exprs,
1921 const std::vector<IntegerVariable>& z_vars,
Model*
model) {
1923 std::vector<IntegerVariable> x_vars;
1924 result.
vars = {target};
1925 const int num_exprs = exprs.size();
1926 for (
int i = 0; i < num_exprs; ++i) {
1927 result.
vars.push_back(z_vars[i]);
1928 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
1932 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
1933 return VariableIsPositive(var);
1935 result.
vars.insert(result.
vars.end(), x_vars.begin(), x_vars.end());
1939 [x_vars, z_vars, target, num_exprs, exprs, integer_trail,
model](
1943 lp_values.size(), -1);
1945 lp_values.size(), std::numeric_limits<double>::infinity());
1946 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
1947 for (
const IntegerVariable
var : x_vars) {
1948 const double contribution = ComputeContribution(
1949 var, z_vars, exprs, lp_values, *integer_trail, expr_index);
1950 const double prev_contribution = variable_partition_contrib[
var];
1951 if (contribution < prev_contribution) {
1952 variable_partition[
var] = expr_index;
1953 variable_partition_contrib[
var] = contribution;
1960 double violation = lp_values[target];
1961 cut.
AddTerm(target, IntegerValue(-1));
1963 for (
const IntegerVariable xi_var : x_vars) {
1964 const int input_index = variable_partition[xi_var];
1967 if (coeff != IntegerValue(0)) {
1970 violation -= coeff.value() * lp_values[xi_var];
1972 for (
int expr_index = 0; expr_index < num_exprs; ++expr_index) {
1973 const IntegerVariable z_var = z_vars[expr_index];
1974 const IntegerValue z_coeff = MPlusCoefficient(
1975 x_vars, exprs, variable_partition, expr_index, *integer_trail);
1976 if (z_coeff != IntegerValue(0)) {
1979 violation -= z_coeff.value() * lp_values[z_var];
1981 if (violation > 1e-2) {
1982 manager->
AddCut(cut.
Build(),
"LinMax", lp_values);
1990 std::vector<IntegerVariable>* vars) {
1992 for (
int t = 0; t < helper->
NumTasks(); ++t) {
1994 vars->push_back(helper->
Starts()[t].var);
1997 vars->push_back(helper->
Sizes()[t].var);
2000 vars->push_back(helper->
Ends()[t].var);
2011 vars->push_back(direct_view);
2022 LinearConstraintManager*)>
2025 const std::vector<IntegerVariable>& demands,
2031 return [
capacity, demands, trail, integer_trail, helper,
model, cut_name,
2036 const auto demand_is_fixed = [integer_trail, &demands](
int i) {
2037 return demands.empty() || integer_trail->IsFixed(demands[i]);
2039 const auto demand_min = [integer_trail, &demands](
int i) {
2040 return demands.empty() ? IntegerValue(1)
2041 : integer_trail->LowerBound(demands[i]);
2043 const auto demand_max = [integer_trail, &demands](
int i) {
2044 return demands.empty() ? IntegerValue(1)
2045 : integer_trail->UpperBound(demands[i]);
2048 std::vector<int> active_intervals;
2049 for (
int i = 0; i < helper->
NumTasks(); ++i) {
2050 if (!helper->
IsAbsent(i) && demand_max(i) > 0 && helper->
SizeMin(i) > 0) {
2051 active_intervals.push_back(i);
2055 if (active_intervals.size() < 2)
return;
2057 std::sort(active_intervals.begin(), active_intervals.end(),
2058 [helper](
int a,
int b) {
2059 return helper->StartMin(a) < helper->StartMin(b) ||
2060 (helper->StartMin(a) == helper->StartMin(b) &&
2061 helper->EndMax(a) < helper->EndMax(b));
2064 const IntegerValue capacity_max = integer_trail->UpperBound(
capacity);
2066 for (
int i1 = 0; i1 + 1 < active_intervals.size(); ++i1) {
2067 const int start_index = active_intervals[i1];
2072 if (helper->
StartMin(start_index) == processed_start) {
2075 processed_start = helper->
StartMin(start_index);
2080 int end_index_of_max_violation = -1;
2081 double max_relative_violation = 1.01;
2082 IntegerValue span_of_max_violation(0);
2085 double energy_lp = 0.0;
2091 std::vector<int> residual_tasks(active_intervals.begin() + i1,
2092 active_intervals.end());
2094 residual_tasks.begin(), residual_tasks.end(),
2095 [&](
int a,
int b) { return helper->EndMax(a) < helper->EndMax(b); });
2100 for (
int i2 = 0; i2 < residual_tasks.size(); ++i2) {
2101 const int t = residual_tasks[i2];
2103 if (demand_is_fixed(t)) {
2107 energy_lp +=
ToDouble(demand_min(t)) *
2108 helper->
Sizes()[t].LpValue(lp_values);
2111 DCHECK(!demands.empty());
2114 DCHECK(!demands.empty());
2116 ToDouble(demand_min(t)) * helper->
Sizes()[t].LpValue(lp_values);
2130 const double relative_violation =
2131 energy_lp /
ToDouble((max_of_ends - min_of_starts) * capacity_max);
2132 if (relative_violation > max_relative_violation) {
2133 end_index_of_max_violation = i2;
2134 max_relative_violation = relative_violation;
2135 span_of_max_violation = max_of_ends - min_of_starts;
2139 if (end_index_of_max_violation == -1)
continue;
2142 bool cut_generated =
true;
2143 bool has_opt_cuts =
false;
2144 bool has_quadratic_cuts =
false;
2150 for (
int i2 = 0; i2 <= end_index_of_max_violation; ++i2) {
2151 const int t = residual_tasks[i2];
2153 if (demand_is_fixed(t)) {
2160 DCHECK(!demands.empty());
2163 DCHECK(!demands.empty());
2176 has_quadratic_cuts =
true;
2179 has_opt_cuts =
true;
2180 if (!helper->
SizeIsFixed(t) || !demand_is_fixed(t)) {
2181 has_quadratic_cuts =
true;
2184 helper->
SizeMin(t) * demand_min(t))) {
2185 cut_generated =
false;
2191 if (cut_generated) {
2192 std::string full_name = cut_name;
2193 if (has_opt_cuts) full_name.append(
"_opt");
2194 if (has_quadratic_cuts) full_name.append(
"_quad");
2196 manager->
AddCut(cut.
Build(), cut_name, lp_values);
2203 const std::vector<IntervalVariable>& intervals,
2204 const IntegerVariable
capacity,
const std::vector<IntegerVariable>& demands,
2210 model->TakeOwnership(helper);
2212 result.
vars = demands;
2222 const std::vector<IntervalVariable>& intervals,
2223 const IntegerVariable
capacity,
const std::vector<IntegerVariable>& demands,
2229 model->TakeOwnership(helper);
2231 result.
vars = demands;
2251 std::vector<Event> events;
2254 for (
int i = 0; i < helper->
NumTasks(); ++i) {
2263 e1.interval_index = i;
2265 e1.demand = demands[i];
2270 e2.positive =
false;
2271 events.push_back(e1);
2272 events.push_back(e2);
2278 std::sort(events.begin(), events.end(),
2279 [](
const Event i,
const Event j) {
2280 if (i.time == j.time) {
2281 if (i.positive == j.positive) {
2282 return i.interval_index < j.interval_index;
2286 return i.time < j.time;
2289 std::vector<Event> cut_events;
2290 bool added_positive_event =
false;
2291 for (
const Event& e : events) {
2293 added_positive_event =
true;
2294 cut_events.push_back(e);
2297 if (added_positive_event && cut_events.size() > 1) {
2299 bool cut_generated =
true;
2303 for (
const Event& cut_event : cut_events) {
2304 if (helper->
IsPresent(cut_event.interval_index)) {
2305 cut.
AddTerm(cut_event.demand, IntegerValue(1));
2309 integer_trail->LowerBound(cut_event.demand));
2310 if (!cut_generated)
break;
2313 if (cut_generated) {
2316 manager->
AddCut(cut.
Build(),
"Cumulative", lp_values);
2321 for (
int i = 0; i < cut_events.size(); ++i) {
2322 if (cut_events[i].interval_index == e.interval_index) {
2325 cut_events[new_size] = cut_events[i];
2328 cut_events.resize(new_size);
2329 added_positive_event =
false;
2336 const std::vector<IntervalVariable>& intervals,
Model*
model) {
2341 model->TakeOwnership(helper);
2346 "NoOverlapEnergy", helper,
2353 const std::vector<IntervalVariable>& intervals,
Model*
model) {
2358 model->TakeOwnership(helper);
2365 [trail, helper,
model](
2378 for (
int index1 = 0; index1 < helper->
NumTasks(); ++index1) {
2379 if (!helper->
IsPresent(index1))
continue;
2380 for (
int index2 = index1 + 1; index2 < helper->
NumTasks(); ++index2) {
2381 if (!helper->
IsPresent(index2))
continue;
2389 const bool interval_1_can_precede_2 =
2391 const bool interval_2_can_precede_1 =
2394 if (interval_1_can_precede_2 && !interval_2_can_precede_1) {
2398 cut.
AddTerm(helper->
Ends()[index1], IntegerValue(1));
2400 manager->
AddCut(cut.
Build(),
"NoOverlapPrecedence", lp_values);
2401 }
else if (interval_2_can_precede_1 && !interval_1_can_precede_2) {
2405 cut.
AddTerm(helper->
Ends()[index2], IntegerValue(1));
2407 manager->
AddCut(cut.
Build(),
"NoOverlapPrecedence", lp_values);
2417 const std::vector<IntervalVariable>& intervals,
Model*
model) {
2422 model->TakeOwnership(helper);
2437 IntegerValue size_min;
2441 std::vector<Event> events;
2443 for (
int index1 = 0; index1 < helper->
NumTasks(); ++index1) {
2444 if (!helper->
IsPresent(index1))
continue;
2445 const IntegerValue size_min = helper->
SizeMin(index1);
2448 events.push_back({end_expr, helper->
StartMin(index1), size_min,
2449 end_expr.
LpValue(lp_values), index1});
2471 std::sort(events.begin(), events.end(),
2472 [](
const Event& e1,
const Event& e2) {
2473 return e1.start_min < e2.start_min;
2475 for (
int start = 0; start + 1 < events.size(); ++start) {
2480 const IntegerValue sequence_start_min = events[start].start_min;
2481 std::vector<Event> residual_tasks(events.begin() + start,
2483 std::sort(residual_tasks.begin(), residual_tasks.end(),
2484 [sequence_start_min](
const Event& e1,
const Event& e2) {
2485 return ((e1.lp_end - sequence_start_min) / e1.size_min) <
2486 ((e2.lp_end - sequence_start_min) / e2.size_min);
2489 double best_efficacy = 0.01;
2490 IntegerValue best_min_contrib(0);
2492 IntegerValue sum_duration(0);
2493 IntegerValue sum_square_duration(0);
2494 double lp_contrib = 0;
2496 for (
int i = 0; i < residual_tasks.size(); ++i) {
2498 const IntegerValue duration = residual_tasks[i].size_min;
2499 sum_duration += duration;
2500 sum_square_duration += duration * duration;
2502 residual_tasks[i].lp_end * residual_tasks[i].size_min.value();
2506 const IntegerValue min_contrib =
2507 (sum_duration * sum_duration + sum_square_duration) / 2 +
2508 current_start_min * sum_duration;
2509 const double efficacy = (min_contrib.value() - lp_contrib) /
2510 std::sqrt(sum_square_duration.value());
2511 if (efficacy > best_efficacy) {
2512 best_efficacy = efficacy;
2514 best_min_contrib = min_contrib;
2517 if (best_end != -1) {
2520 for (
int i = 0; i <= best_end; ++i) {
2521 cut.
AddTerm(residual_tasks[i].end, residual_tasks[i].size_min);
2523 top_n_cuts.
AddCut(cut.
Build(),
"NoOverlapBalasArea", lp_values);
2532 const std::vector<IntegerVariable>& base_variables,
Model*
model) {
2535 std::vector<IntegerVariable> variables;
2536 std::vector<Literal> literals;
2537 absl::flat_hash_map<LiteralIndex, IntegerVariable> positive_map;
2538 absl::flat_hash_map<LiteralIndex, IntegerVariable> negative_map;
2541 for (
const IntegerVariable
var : base_variables) {
2542 if (integer_trail->LowerBound(
var) != IntegerValue(0))
continue;
2543 if (integer_trail->UpperBound(
var) != IntegerValue(1))
continue;
2544 const LiteralIndex literal_index = encoder->GetAssociatedLiteral(
2547 variables.push_back(
var);
2548 literals.push_back(
Literal(literal_index));
2549 positive_map[literal_index] =
var;
2554 result.
vars = variables;
2557 [variables, literals, implication_graph, positive_map, negative_map,
2560 std::vector<double> packed_values;
2561 for (
int i = 0; i < literals.size(); ++i) {
2562 packed_values.push_back(lp_values[variables[i]]);
2564 const std::vector<std::vector<Literal>> at_most_ones =
2565 implication_graph->GenerateAtMostOnesWithLargeWeight(literals,
2568 for (
const std::vector<Literal>& at_most_one : at_most_ones) {
2575 for (
const Literal l : at_most_one) {
2577 builder.
AddTerm(positive_map.at(l.Index()), IntegerValue(1));
2580 builder.
AddTerm(negative_map.at(l.Index()), IntegerValue(-1));
2585 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)
We call domain any subset of Int64 = [kint64min, kint64max].
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
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)
BestImpliedBoundInfo GetCachedImpliedBoundInfo(IntegerVariable var)
const IntegerVariable GetLiteralView(Literal lit) const
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)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
const Domain & InitialVariableDomain(IntegerVariable var) const
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddConstant(IntegerValue value)
void AddTerm(IntegerVariable var, IntegerValue coeff)
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
LiteralIndex NegatedIndex() const
Class that owns everything related to a particular optimization model.
IntegerValue EndMin(int t) const
bool IsPresent(int t) const
bool SizeIsFixed(int t) const
bool IsAbsent(int t) const
IntegerValue EndMax(int t) const
const std::vector< AffineExpression > & Starts() const
bool IsOptional(int t) const
IntegerValue StartMin(int t) const
Literal PresenceLiteral(int index) const
IntegerValue StartMax(int t) const
const std::vector< AffineExpression > & Sizes() const
IntegerValue SizeMin(int t) const
const std::vector< AffineExpression > & Ends() const
void AddCut(LinearConstraint ct, const std::string &name, const absl::StrongVector< IntegerVariable, double > &lp_solution)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
int CurrentDecisionLevel() const
SharedTimeLimit * time_limit
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool ContainsKey(const Collection &collection, const Key &key)
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 AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
LinearConstraint GetPreprocessedLinearConstraint(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &lp_values, const IntegerTrail &integer_trail)
IntType IntTypeAbs(IntType t)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(const std::vector< IntervalVariable > &intervals, 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)
CutGenerator CreateCumulativeCutGenerator(const std::vector< IntervalVariable > &intervals, const IntegerVariable capacity, const std::vector< IntegerVariable > &demands, Model *model)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::function< void(const absl::StrongVector< IntegerVariable, double > &, LinearConstraintManager *)> GenerateCumulativeCut(const std::string &cut_name, SchedulingConstraintHelper *helper, const std::vector< IntegerVariable > &demands, AffineExpression capacity, Model *model)
void RemoveZeroTerms(LinearConstraint *constraint)
IntegerValue GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue max_t)
double GetKnapsackUpperBound(std::vector< KnapsackItem > items, const double capacity)
CutGenerator CreateOverlappingCumulativeCutGenerator(const std::vector< IntervalVariable > &intervals, const IntegerVariable capacity, const std::vector< IntegerVariable > &demands, Model *model)
CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, Model *model)
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)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
IntegerVariable PositiveVariable(IntegerVariable i)
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< IntegerVariable > &vars, 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 CreateNoOverlapBalasCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
void MakeAllVariablesPositive(LinearConstraint *constraint)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
CutGenerator CreateNoOverlapEnergyCutGenerator(const std::vector< IntervalVariable > &intervals, 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)
void AddIntegerVariableFromIntervals(SchedulingConstraintHelper *helper, Model *model, std::vector< IntegerVariable > *vars)
bool ConstraintIsTriviallyTrue(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue >> *terms, LinearConstraint *constraint)
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 CreatePositiveMultiplicationCutGenerator(IntegerVariable z, IntegerVariable x, IntegerVariable y, Model *model)
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 SumOfKMinValueInDomain(const Domain &domain, int k)
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 SumOfKMaxValueInDomain(const Domain &domain, int k)
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< void(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)