18 #include "absl/strings/str_format.h"
34 return absl::StrFormat(
"[%g, %g]", lb, ub);
38 double trunc(
double d) {
return d > 0 ? floor(d) : ceil(d); }
48 in_mip_context_(false),
49 infinite_time_limit_(
TimeLimit::Infinite()),
50 time_limit_(infinite_time_limit_.get()) {}
57 #define RUN_PREPROCESSOR(name) \
58 RunAndPushIfRelevant(std::unique_ptr<Preprocessor>(new name(¶meters_)), \
59 #name, time_limit_, lp)
71 const int kMaxNumPasses = 20;
72 for (
int i = 0; i < kMaxNumPasses; ++i) {
73 const int old_stack_size = preprocessors_.size();
86 if (preprocessors_.size() == old_stack_size) {
88 VLOG(1) <<
"Reached fixed point after presolve pass #" << i;
102 const int old_stack_size = preprocessors_.size();
108 if (old_stack_size != preprocessors_.size()) {
124 return !preprocessors_.empty();
127 #undef RUN_PREPROCESSOR
129 void MainLpPreprocessor::RunAndPushIfRelevant(
130 std::unique_ptr<Preprocessor> preprocessor,
const std::string&
name,
136 const double start_time =
time_limit->GetElapsedTime();
146 if (preprocessor->Run(lp)) {
147 const EntryIndex new_num_entries = lp->
num_entries();
148 const double preprocess_time =
time_limit->GetElapsedTime() - start_time;
149 VLOG(1) << absl::StrFormat(
150 "%s(%fs): %d(%d) rows, %d(%d) columns, %d(%d) entries.",
name,
156 static_cast<int64>(new_num_entries.value()),
157 static_cast<int64>(new_num_entries.value() -
158 initial_num_entries_.value()));
159 status_ = preprocessor->status();
160 preprocessors_.push_back(std::move(preprocessor));
165 status_ = preprocessor->status();
167 VLOG(1) <<
name <<
" detected that the problem is "
175 while (!preprocessors_.empty()) {
176 preprocessors_.back()->RecoverSolution(solution);
177 preprocessors_.pop_back();
186 is_column_deleted_.
clear();
187 stored_value_.
clear();
197 if (
col >= is_column_deleted_.
size()) {
198 is_column_deleted_.
resize(
col + 1,
false);
202 is_column_deleted_[
col] =
true;
203 stored_value_[
col] = fixed_value;
204 stored_status_[
col] = status;
211 ColIndex old_index(0);
212 for (ColIndex
col(0);
col < is_column_deleted_.
size(); ++
col) {
213 if (is_column_deleted_[
col]) {
226 for (; old_index < num_cols; ++old_index) {
242 if (
row >= is_row_deleted_.
size()) {
245 is_row_deleted_[
row] =
true;
249 if (
row >= is_row_deleted_.
size())
return;
250 is_row_deleted_[
row] =
false;
254 return is_row_deleted_;
260 RowIndex old_index(0);
261 const RowIndex end = is_row_deleted_.
size();
262 for (RowIndex
row(0);
row < end; ++
row) {
263 if (is_row_deleted_[
row]) {
277 for (; old_index < num_rows; ++old_index) {
296 if (lower_bound == upper_bound) {
297 DCHECK_EQ(
value, lower_bound);
301 if (
value == lower_bound) {
305 if (
value == upper_bound) {
328 Fractional ComputeMaxVariableBoundsMagnitude(
const LinearProgram& lp) {
330 const ColIndex num_cols = lp.num_variables();
331 for (ColIndex
col(0);
col < num_cols; ++
col) {
333 max_bounds_magnitude,
334 std::max(MagnitudeOrZeroIfInfinite(lp.variable_lower_bounds()[
col]),
335 MagnitudeOrZeroIfInfinite(lp.variable_upper_bounds()[
col])));
337 return max_bounds_magnitude;
345 column_deletion_helper_.
Clear();
347 for (ColIndex
col(0);
col < num_cols; ++
col) {
354 if (objective_coefficient == 0) {
366 value = objective_coefficient > 0 ? lower_bound : upper_bound;
368 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, empty column " <<
col
369 <<
" has a minimization cost of " << objective_coefficient
371 <<
" [" << lower_bound <<
"," << upper_bound <<
"]";
372 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
379 col,
value, ComputeVariableStatus(
value, lower_bound, upper_bound));
383 return !column_deletion_helper_.
IsEmpty();
401 void SubtractColumnMultipleFromConstraintBound(ColIndex
col,
405 const RowIndex
row = e.row();
419 struct ColumnWithRepresentativeAndScaledCost {
420 ColumnWithRepresentativeAndScaledCost(ColIndex _col, ColIndex _representative,
427 bool operator<(
const ColumnWithRepresentativeAndScaledCost& other)
const {
430 return col < other.col;
451 int num_proportionality_classes = 0;
452 std::vector<ColIndex> proportional_columns;
458 ++num_proportionality_classes;
461 proportional_columns.push_back(
col);
464 if (proportional_columns.empty())
return false;
465 VLOG(1) <<
"The problem contains " << proportional_columns.size()
466 <<
" columns which belong to " << num_proportionality_classes
467 <<
" proportionality classes.";
471 column_factors_.
assign(num_cols, 0.0);
472 for (
const ColIndex
col : proportional_columns) {
486 for (
const ColIndex
col : proportional_columns) {
490 const bool is_rc_positive_or_zero =
492 const bool is_rc_negative_or_zero =
494 bool is_slope_upper_bounded = is_rc_positive_or_zero;
495 bool is_slope_lower_bounded = is_rc_negative_or_zero;
496 if (column_factors_[
col] < 0.0) {
497 std::swap(is_slope_lower_bounded, is_slope_upper_bounded);
501 column_factors_[
col];
502 if (is_slope_lower_bounded) {
506 if (is_slope_upper_bounded) {
513 for (
const ColIndex
col : proportional_columns) {
521 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, no feasible dual values"
522 <<
" can satisfy the constraints of the proportional columns"
524 <<
" the associated quantity must be in ["
527 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
534 for (
const ColIndex
col : proportional_columns) {
538 column_factors_[
col];
541 bool variable_can_be_fixed =
false;
549 variable_can_be_fixed =
true;
550 target_bound = (column_factors_[
col] >= 0.0) ? upper_bound : lower_bound;
554 variable_can_be_fixed =
true;
555 target_bound = (column_factors_[
col] >= 0.0) ? lower_bound : upper_bound;
558 if (variable_can_be_fixed) {
563 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED.";
564 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
570 ComputeVariableStatus(
target_bound, lower_bound, upper_bound));
576 std::vector<ColumnWithRepresentativeAndScaledCost> sorted_columns;
577 for (
const ColIndex
col : proportional_columns) {
582 sorted_columns.push_back(ColumnWithRepresentativeAndScaledCost(
587 std::sort(sorted_columns.begin(), sorted_columns.end());
596 for (
int i = 0; i < sorted_columns.size();) {
597 const ColIndex target_col = sorted_columns[i].col;
598 const ColIndex target_representative = sorted_columns[i].representative;
599 const Fractional target_scaled_cost = sorted_columns[i].scaled_cost;
606 for (++i; i < sorted_columns.size(); ++i) {
607 if (sorted_columns[i].
representative != target_representative)
break;
608 if (std::abs(sorted_columns[i].
scaled_cost - target_scaled_cost) >=
613 const ColIndex
col = sorted_columns[i].col;
616 lower_bounds_[
col] = lower_bound;
617 upper_bounds_[
col] = upper_bound;
618 merged_columns_[
col] = target_col;
623 column_factors_[
col] / column_factors_[target_col];
634 MinInMagnitudeOrZeroIfInfinite(lower_bound, upper_bound);
635 Fractional lower_diff = (lower_bound - target_value) * bound_factor;
636 Fractional upper_diff = (upper_bound - target_value) * bound_factor;
637 if (bound_factor < 0.0) {
638 std::swap(lower_diff, upper_diff);
643 SubtractColumnMultipleFromConstraintBound(
col, target_value, lp);
646 ComputeVariableStatus(target_value, lower_bound, upper_bound));
652 if (num_merged > 0) {
653 merged_columns_[target_col] = target_col;
654 const Fractional target_value = MinInMagnitudeOrZeroIfInfinite(
655 lower_bounds_[target_col], upper_bounds_[target_col]);
659 SubtractColumnMultipleFromConstraintBound(target_col, target_value, lp);
666 return !column_deletion_helper_.
IsEmpty();
677 const ColIndex num_cols = merged_columns_.
size();
680 DenseRow distance_to_bound(num_cols, 0.0);
681 DenseRow wanted_value(num_cols, 0.0);
685 for (ColIndex
col(0);
col < num_cols; ++
col) {
686 if (merged_columns_[
col] ==
col) {
690 if (distance_to_upper_bound < distance_to_lower_bound) {
691 distance_to_bound[
col] = distance_to_upper_bound;
692 is_distance_to_upper_bound[
col] =
true;
694 distance_to_bound[
col] = distance_to_lower_bound;
695 is_distance_to_upper_bound[
col] =
false;
697 is_representative_basic[
col] =
704 lower_bounds_[
col], upper_bounds_[
col]);
711 for (ColIndex
col(0);
col < num_cols; ++
col) {
723 const bool to_upper_bound =
724 (bound_factor > 0.0) == is_distance_to_upper_bound[
representative];
725 if (width <= scaled_distance) {
727 to_upper_bound ? lower_bounds_[
col] : upper_bounds_[
col];
730 lower_bounds_[
col], upper_bounds_[
col]);
731 distance_to_bound[
representative] -= width * std::abs(bound_factor);
734 to_upper_bound ? upper_bounds_[
col] - scaled_distance
735 : lower_bounds_[
col] + scaled_distance;
758 const bool use_this_variable =
759 (error * bound_factor > 0.0) ? (upper_bounds_[
col] ==
kInfinity)
761 if (use_this_variable) {
770 solution->
status != ProblemStatus::PRIMAL_FEASIBLE);
794 row_factors_.
assign(num_rows, 0.0);
795 for (RowIndex
row(0);
row < num_rows; ++
row) {
797 if (!row_transpose.
IsEmpty()) {
816 transpose,
parameters_.preprocessor_zero_tolerance());
818 int num_proportional_rows = 0;
819 for (RowIndex
row(0);
row < num_rows; ++
row) {
822 mapping[representative_row_as_col] = representative_row_as_col;
823 is_a_representative[
ColToRowIndex(representative_row_as_col)] =
true;
824 ++num_proportional_rows;
830 for (RowIndex
row(0);
row < num_rows; ++
row) {
836 const RowIndex representative_row =
ColToRowIndex(mapping[row_as_col]);
839 row_factors_[representative_row] / row_factors_[
row];
843 std::swap(implied_lb, implied_ub);
849 lower_bound_sources_[representative_row] =
row;
853 upper_bound_sources_[representative_row] =
row;
861 for (RowIndex
row(0);
row < num_rows; ++
row) {
862 if (!is_a_representative[
row])
continue;
863 const RowIndex lower_source = lower_bound_sources_[
row];
864 const RowIndex upper_source = upper_bound_sources_[
row];
869 if (lower_source == upper_source) {
873 row_deletion_helper_.
UnmarkRow(lower_source);
879 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
886 row_deletion_helper_.
UnmarkRow(lower_source);
891 row_deletion_helper_.
UnmarkRow(upper_source);
899 RowIndex new_representative = lower_source;
900 RowIndex other = upper_source;
901 if (std::abs(row_factors_[new_representative]) <
902 std::abs(row_factors_[other])) {
903 std::swap(new_representative, other);
908 row_factors_[new_representative] / row_factors_[other];
912 std::swap(new_lb, new_ub);
915 lower_bound_sources_[new_representative] = new_representative;
916 upper_bound_sources_[new_representative] = new_representative;
919 lower_bound_sources_[new_representative] = other;
923 if (new_ub < lp->constraint_upper_bounds()[new_representative]) {
924 upper_bound_sources_[new_representative] = other;
928 const RowIndex new_lower_source =
929 lower_bound_sources_[new_representative];
930 if (new_lower_source == upper_bound_sources_[new_representative]) {
931 row_deletion_helper_.
UnmarkRow(new_lower_source);
932 lower_bound_sources_[new_representative] =
kInvalidRow;
933 upper_bound_sources_[new_representative] =
kInvalidRow;
942 if (new_lb > new_ub) {
943 if (lower_bound_sources_[new_representative] == new_representative) {
949 row_deletion_helper_.
UnmarkRow(new_representative);
956 return !row_deletion_helper_.
IsEmpty();
969 for (RowIndex
row(0);
row < num_rows; ++
row) {
970 const RowIndex lower_source = lower_bound_sources_[
row];
971 const RowIndex upper_source = upper_bound_sources_[
row];
973 DCHECK_NE(lower_source, upper_source);
974 DCHECK(lower_source ==
row || upper_source ==
row);
986 const Fractional corrected_dual_value = lp_is_maximization_problem_
989 if (corrected_dual_value != 0.0) {
999 DCHECK_EQ(0.0, solution->
dual_values[lower_source]);
1000 const Fractional factor = row_factors_[
row] / row_factors_[lower_source];
1009 DCHECK_EQ(0.0, solution->
dual_values[upper_source]);
1010 const Fractional factor = row_factors_[
row] / row_factors_[upper_source];
1037 for (ColIndex
col(0);
col < num_cols; ++
col) {
1040 if (lower_bound == upper_bound) {
1045 SubtractColumnMultipleFromConstraintBound(
col, fixed_value, lp);
1052 return !column_deletion_helper_.
IsEmpty();
1076 for (ColIndex
col(0);
col < num_cols; ++
col) {
1080 const RowIndex
row = e.row();
1083 implied_lower_bounds[
row] += lower * coeff;
1084 implied_upper_bounds[
row] += upper * coeff;
1086 implied_lower_bounds[
row] += upper * coeff;
1087 implied_upper_bounds[
row] += lower * coeff;
1095 int num_implied_free_constraints = 0;
1096 int num_forcing_constraints = 0;
1097 is_forcing_up_.
assign(num_rows,
false);
1099 for (RowIndex
row(0);
row < num_rows; ++
row) {
1100 if (row_degree[
row] == 0)
continue;
1106 implied_upper_bounds[
row]) ||
1109 VLOG(1) <<
"implied bound " << implied_lower_bounds[
row] <<
" "
1110 << implied_upper_bounds[
row];
1111 VLOG(1) <<
"constraint bound " << lower <<
" " << upper;
1112 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
1120 is_forcing_down[
row] =
true;
1121 ++num_forcing_constraints;
1125 implied_lower_bounds[
row])) {
1126 is_forcing_up_[
row] =
true;
1127 ++num_forcing_constraints;
1138 implied_lower_bounds[
row]) &&
1142 ++num_implied_free_constraints;
1146 if (num_implied_free_constraints > 0) {
1147 VLOG(1) << num_implied_free_constraints <<
" implied free constraints.";
1150 if (num_forcing_constraints > 0) {
1151 VLOG(1) << num_forcing_constraints <<
" forcing constraints.";
1154 costs_.
resize(num_cols, 0.0);
1155 for (ColIndex
col(0);
col < num_cols; ++
col) {
1159 bool is_forced =
false;
1162 if (is_forcing_down[e.row()]) {
1163 const Fractional candidate = e.coefficient() < 0.0 ? lower : upper;
1172 target_bound = std::abs(lower) < std::abs(upper) ? lower : upper;
1175 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1176 << std::fixed << std::setprecision(10) << lower <<
", "
1177 << upper <<
"]. coeff:" << e.coefficient();
1178 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
1184 if (is_forcing_up_[e.row()]) {
1185 const Fractional candidate = e.coefficient() < 0.0 ? upper : lower;
1190 target_bound = std::abs(lower) < std::abs(upper) ? lower : upper;
1193 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1194 << std::fixed << std::setprecision(10) << lower <<
", "
1195 << upper <<
"]. coeff:" << e.coefficient();
1196 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
1214 for (RowIndex
row(0);
row < num_rows; ++
row) {
1221 if (is_forcing_down[
row] || is_forcing_up_[
row]) {
1229 return !column_deletion_helper_.
IsEmpty();
1240 const ColIndex num_cols = deleted_columns_.
num_cols();
1242 for (ColIndex
col(0);
col < num_cols; ++
col) {
1245 const RowIndex
row = e.row();
1247 last_deleted_row[
col] =
row;
1265 for (RowIndex
row(0);
row < num_rows; ++
row) {
1271 if (last_deleted_row[
col] !=
row)
continue;
1274 const Fractional reduced_cost = costs_[
col] - scalar_product;
1276 if (is_forcing_up_[
row] == !lp_is_maximization_problem_) {
1277 if (
bound < new_dual_value) {
1278 new_dual_value =
bound;
1279 new_basic_column =
col;
1282 if (
bound > new_dual_value) {
1283 new_dual_value =
bound;
1284 new_basic_column =
col;
1304 struct ColWithDegree {
1307 ColWithDegree(ColIndex c, EntryIndex n) :
col(c),
num_entries(n) {}
1308 bool operator<(
const ColWithDegree& other)
const {
1310 return col < other.col;
1327 const int size = num_rows.value();
1334 for (ColIndex
col(0);
col < num_cols; ++
col) {
1338 Fractional entry_lb = e.coefficient() * lower_bound;
1339 Fractional entry_ub = e.coefficient() * upper_bound;
1340 if (e.coefficient() < 0.0) std::swap(entry_lb, entry_ub);
1341 lb_sums[e.row()].Add(entry_lb);
1342 ub_sums[e.row()].Add(entry_ub);
1352 for (RowIndex
row(0);
row < num_rows; ++
row) {
1361 variable_offsets_.
assign(num_cols, 0.0);
1378 std::vector<ColWithDegree> col_by_degree;
1379 for (ColIndex
col(0);
col < num_cols; ++
col) {
1380 col_by_degree.push_back(
1383 std::sort(col_by_degree.begin(), col_by_degree.end());
1386 int num_already_free_variables = 0;
1387 int num_implied_free_variables = 0;
1388 int num_fixed_variables = 0;
1389 for (ColWithDegree col_with_degree : col_by_degree) {
1390 const ColIndex
col = col_with_degree.col;
1396 ++num_already_free_variables;
1399 if (lower_bound == upper_bound)
continue;
1407 if (used_rows[e.row()])
continue;
1413 if (coeff < 0.0) std::swap(entry_lb, entry_ub);
1419 Fractional implied_lb = -ub_sums[e.row()].SumWithout(entry_ub) / coeff;
1420 Fractional implied_ub = -lb_sums[e.row()].SumWithout(entry_lb) / coeff;
1421 if (coeff < 0.0) std::swap(implied_lb, implied_ub);
1422 overall_implied_lb =
std::max(overall_implied_lb, implied_lb);
1423 overall_implied_ub =
std::min(overall_implied_ub, implied_ub);
1430 overall_implied_ub)) {
1431 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
1438 overall_implied_lb) ||
1444 ++num_fixed_variables;
1447 overall_implied_lb)) {
1453 ++num_fixed_variables;
1460 overall_implied_lb) &&
1463 ++num_implied_free_variables;
1466 used_rows[e.row()] =
true;
1487 DCHECK_NE(lower_bound, upper_bound);
1489 MinInMagnitudeOrZeroIfInfinite(lower_bound, upper_bound);
1490 if (offset != 0.0) {
1491 variable_offsets_[
col] = offset;
1492 SubtractColumnMultipleFromConstraintBound(
col, offset, lp);
1494 postsolve_status_of_free_variables_[
col] =
1495 ComputeVariableStatus(offset, lower_bound, upper_bound);
1498 VLOG(1) << num_already_free_variables <<
" free variables in the problem.";
1499 VLOG(1) << num_implied_free_variables <<
" implied free columns.";
1500 VLOG(1) << num_fixed_variables <<
" variables can be fixed.";
1501 return num_implied_free_variables > 0;
1508 for (ColIndex
col(0);
col < num_cols; ++
col) {
1511 DCHECK_EQ(0.0, variable_offsets_[
col]);
1516 postsolve_status_of_free_variables_[
col];
1538 for (ColIndex doubleton_col(0); doubleton_col < num_cols; ++doubleton_col) {
1547 r.col = doubleton_col;
1551 if (row_deletion_helper_.
IsRowMarked(e.row()))
break;
1552 r.row[
index] = e.row();
1553 r.coeff[
index] = e.coefficient();
1554 DCHECK_NE(0.0, e.coefficient());
1557 if (
index != NUM_ROWS)
continue;
1563 DCHECK_EQ(r.coeff[MODIFIED],
1569 if (std::abs(r.coeff[DELETED]) < std::abs(r.coeff[MODIFIED])) {
1570 std::swap(r.coeff[DELETED], r.coeff[MODIFIED]);
1571 std::swap(r.row[DELETED], r.row[MODIFIED]);
1578 r.deleted_row_as_column.Swap(
1587 new_variable_lb /= r.coeff[DELETED];
1588 new_variable_ub /= r.coeff[DELETED];
1589 if (r.coeff[DELETED] < 0.0) std::swap(new_variable_lb, new_variable_ub);
1595 r.deleted_row_as_column.AddMultipleToSparseVectorAndIgnoreCommonIndex(
1596 -r.coeff[MODIFIED] / r.coeff[DELETED],
ColToRowIndex(r.col),
1602 if (r.objective_coefficient != 0.0) {
1605 if (
col == r.col)
continue;
1608 e.coefficient() * r.objective_coefficient / r.coeff[DELETED];
1614 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
1622 restore_stack_.push_back(r);
1625 if (!row_deletion_helper_.
IsEmpty()) {
1638 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
1670 if (
col == r.col)
continue;
1671 new_variable_value -= (e.coefficient() / r.coeff[DELETED]) *
1684 r.objective_coefficient -
1685 r.coeff[MODIFIED] * solution->
dual_values[r.row[MODIFIED]];
1688 current_reduced_cost / r.coeff[DELETED];
1690 DCHECK_EQ(solution->
dual_values[r.row[DELETED]], 0.0);
1715 if (deleted_rows_as_column_.
IsEmpty()) {
1728 const RowIndex
row = e.row();
1736 const bool is_constraint_upper_bound_relevant =
1737 e.coefficient() > 0.0 ? !is_unbounded_up : is_unbounded_up;
1738 activity_sign_correction_[
row] =
1739 is_constraint_upper_bound_relevant ? 1.0 : -1.0;
1740 rhs_[
row] = is_constraint_upper_bound_relevant
1748 is_unbounded_[
col] =
true;
1749 Fractional initial_feasible_value = MinInMagnitudeOrZeroIfInfinite(
1753 col, initial_feasible_value,
1754 ComputeVariableStatus(initial_feasible_value,
1777 for (RowIndex
row(0);
row < num_rows; ++
row) {
1779 dual_ub_[
row] = 0.0;
1782 dual_lb_[
row] = 0.0;
1787 may_have_participated_lb_.
assign(num_cols,
false);
1788 may_have_participated_ub_.
assign(num_cols,
false);
1791 std::deque<ColIndex> columns_to_process;
1793 std::vector<RowIndex> changed_rows;
1794 for (ColIndex
col(0);
col < num_cols; ++
col) {
1795 columns_to_process.push_back(
col);
1801 const int limit = 5 * num_cols.value();
1802 for (
int count = 0; !columns_to_process.empty() && count < limit; ++count) {
1803 const ColIndex
col = columns_to_process.front();
1804 columns_to_process.pop_front();
1805 in_columns_to_process[
col] =
false;
1817 rc_lb.
Add(col_cost);
1818 rc_ub.
Add(col_cost);
1820 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
1823 rc_lb.
Add(-coeff * dual_ub_[e.row()]);
1824 rc_ub.
Add(-coeff * dual_lb_[e.row()]);
1826 rc_lb.
Add(-coeff * dual_lb_[e.row()]);
1827 rc_ub.
Add(-coeff * dual_ub_[e.row()]);
1835 bool can_be_removed =
false;
1837 bool rc_is_away_from_zero;
1838 if (rc_ub.
Sum() <= low_tolerance) {
1839 can_be_removed =
true;
1841 rc_is_away_from_zero = rc_ub.
Sum() <= -high_tolerance;
1842 can_be_removed = !may_have_participated_ub_[
col];
1844 if (rc_lb.
Sum() >= -low_tolerance) {
1848 can_be_removed =
true;
1850 rc_is_away_from_zero = rc_lb.
Sum() >= high_tolerance;
1851 can_be_removed = !may_have_participated_lb_[
col];
1855 if (can_be_removed) {
1867 if (rc_is_away_from_zero) {
1868 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, variable " <<
col
1870 <<
" and its reduced cost is in [" << rc_lb.
Sum() <<
", "
1871 << rc_ub.
Sum() <<
"]";
1872 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
1883 if (col_cost != 0.0)
continue;
1888 if (IsConstraintBlockingVariable(*lp, e.coefficient(), e.row())) {
1906 DCHECK(!can_be_removed);
1913 changed_rows.clear();
1915 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
1917 const RowIndex
row = e.row();
1921 if (candidate < dual_ub_[
row]) {
1922 dual_ub_[
row] = candidate;
1923 may_have_participated_lb_[
col] =
true;
1924 changed_rows.push_back(
row);
1928 if (candidate > dual_lb_[
row]) {
1929 dual_lb_[
row] = candidate;
1930 may_have_participated_lb_[
col] =
true;
1931 changed_rows.push_back(
row);
1938 if (candidate > dual_lb_[
row]) {
1939 dual_lb_[
row] = candidate;
1940 may_have_participated_ub_[
col] =
true;
1941 changed_rows.push_back(
row);
1945 if (candidate < dual_ub_[
row]) {
1946 dual_ub_[
row] = candidate;
1947 may_have_participated_ub_[
col] =
true;
1948 changed_rows.push_back(
row);
1954 if (!changed_rows.empty()) {
1956 for (
const RowIndex
row : changed_rows) {
1960 if (!in_columns_to_process[
col]) {
1961 columns_to_process.push_back(
col);
1962 in_columns_to_process[
col] =
true;
1974 for (ColIndex
col(0);
col < end; ++
col) {
1984 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
1997 for (RowIndex
row(0);
row < num_rows; ++
row) {
2002 if (is_unbounded_[
col]) {
2003 last_deleted_column[
row] =
col;
2010 const ColIndex num_cols = is_unbounded_.
size();
2011 for (ColIndex
col(0);
col < num_cols; ++
col) {
2012 if (!is_unbounded_[
col])
continue;
2016 const RowIndex
row = e.row();
2032 if (activity * activity_sign_correction_[
row] < 0.0) {
2034 if (std::abs(
bound) > std::abs(primal_value_shift)) {
2035 primal_value_shift =
bound;
2044 activity_sign_correction_[row_at_bound] == 1.0
2059 for (RowIndex
row(0);
row < num_rows; ++
row) {
2067 return !row_deletion_helper_.
IsEmpty();
2089 for (ColIndex
col(0);
col < num_cols; ++
col) {
2096 for (RowIndex
row(0);
row < num_rows; ++
row) {
2097 if (degree[
row] == 0) {
2104 VLOG(1) <<
"Problem PRIMAL_INFEASIBLE, constraint " <<
row
2105 <<
" is empty and its range ["
2108 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
2115 return !row_deletion_helper_.
IsEmpty();
2132 is_maximization_(lp.IsMaximizationProblem()),
2134 cost_(lp.objective_coefficients()[e.
col]),
2135 variable_lower_bound_(lp.variable_lower_bounds()[e.
col]),
2136 variable_upper_bound_(lp.variable_upper_bounds()[e.
col]),
2137 constraint_lower_bound_(lp.constraint_lower_bounds()[e.
row]),
2138 constraint_upper_bound_(lp.constraint_upper_bounds()[e.
row]),
2139 constraint_status_(status) {}
2147 SingletonRowUndo(deleted_columns, solution);
2150 ZeroCostSingletonColumnUndo(
parameters, deleted_rows, solution);
2153 SingletonColumnInEqualityUndo(
parameters, deleted_rows, solution);
2156 MakeConstraintAnEqualityUndo(solution);
2161 void SingletonPreprocessor::DeleteSingletonRow(MatrixEntry e,
2167 if (e.coeff < 0.0) {
2168 std::swap(implied_lower_bound, implied_upper_bound);
2175 std::abs(
parameters_.preprocessor_zero_tolerance() / e.coeff);
2177 implied_lower_bound - potential_error > old_lower_bound
2178 ? implied_lower_bound
2181 implied_upper_bound + potential_error < old_upper_bound
2182 ? implied_upper_bound
2185 if (new_upper_bound < new_lower_bound) {
2188 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2189 "row causes the bound of the variable "
2190 << e.col <<
" to be infeasible by "
2191 << new_lower_bound - new_upper_bound;
2192 status_ = ProblemStatus::PRIMAL_INFEASIBLE;
2197 new_upper_bound = new_lower_bound;
2200 new_lower_bound = new_upper_bound;
2202 DCHECK_EQ(new_lower_bound, new_upper_bound);
2216 void SingletonUndo::SingletonRowUndo(
const SparseMatrix& deleted_columns,
2217 ProblemSolution* solution)
const {
2218 DCHECK_EQ(0, solution->dual_values[e_.row]);
2223 const VariableStatus status = solution->variable_statuses[e_.col];
2227 Fractional implied_lower_bound = constraint_lower_bound_ / e_.coeff;
2228 Fractional implied_upper_bound = constraint_upper_bound_ / e_.coeff;
2229 if (e_.coeff < 0.0) {
2230 std::swap(implied_lower_bound, implied_upper_bound);
2232 const bool lower_bound_changed = implied_lower_bound > variable_lower_bound_;
2233 const bool upper_bound_changed = implied_upper_bound < variable_upper_bound_;
2235 if (!lower_bound_changed && !upper_bound_changed)
return;
2243 ScalarProduct(solution->dual_values, deleted_columns.column(e_.col));
2244 const Fractional reduced_cost_for_minimization =
2245 is_maximization_ ? -reduced_cost : reduced_cost;
2248 DCHECK(lower_bound_changed || upper_bound_changed);
2249 if (reduced_cost_for_minimization >= 0.0 && !lower_bound_changed) {
2253 if (reduced_cost_for_minimization <= 0.0 && !upper_bound_changed) {
2264 solution->dual_values[e_.row] = reduced_cost / e_.coeff;
2267 (!lower_bound_changed || !upper_bound_changed)) {
2268 new_constraint_status = lower_bound_changed
2272 if (e_.coeff < 0.0) {
2280 solution->constraint_statuses[e_.row] = new_constraint_status;
2283 void SingletonPreprocessor::UpdateConstraintBoundsWithVariableBounds(
2284 MatrixEntry e, LinearProgram* lp) {
2285 Fractional lower_delta = -e.coeff * lp->variable_upper_bounds()[e.col];
2286 Fractional upper_delta = -e.coeff * lp->variable_lower_bounds()[e.col];
2287 if (e.coeff < 0.0) {
2288 std::swap(lower_delta, upper_delta);
2290 lp->SetConstraintBounds(e.row,
2291 lp->constraint_lower_bounds()[e.row] + lower_delta,
2292 lp->constraint_upper_bounds()[e.row] + upper_delta);
2295 bool SingletonPreprocessor::IntegerSingletonColumnIsRemovable(
2296 const MatrixEntry& matrix_entry,
const LinearProgram& lp)
const {
2298 DCHECK(lp.IsVariableInteger(matrix_entry.col));
2299 const SparseMatrix& transpose = lp.GetTransposeSparseMatrix();
2311 coefficient_ratio,
parameters_.solution_feasibility_tolerance())) {
2316 lp.constraint_lower_bounds()[matrix_entry.row];
2318 const Fractional lower_bound_ratio = constraint_lb / matrix_entry.coeff;
2320 lower_bound_ratio,
parameters_.solution_feasibility_tolerance())) {
2325 lp.constraint_upper_bounds()[matrix_entry.row];
2327 const Fractional upper_bound_ratio = constraint_ub / matrix_entry.coeff;
2329 upper_bound_ratio,
parameters_.solution_feasibility_tolerance())) {
2336 void SingletonPreprocessor::DeleteZeroCostSingletonColumn(
2337 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2339 const SparseColumn& column = transpose.column(transpose_col);
2346 UpdateConstraintBoundsWithVariableBounds(e, lp);
2351 void SingletonUndo::ZeroCostSingletonColumnUndo(
2352 const GlopParameters&
parameters,
const SparseMatrix& deleted_rows,
2353 ProblemSolution* solution)
const {
2356 if (variable_upper_bound_ == variable_lower_bound_) {
2357 solution->primal_values[e_.col] = variable_lower_bound_;
2368 DCHECK(
IsFinite(variable_lower_bound_));
2369 solution->primal_values[e_.col] = variable_lower_bound_;
2372 DCHECK(
IsFinite(variable_upper_bound_));
2373 solution->primal_values[e_.col] = variable_upper_bound_;
2376 if (constraint_upper_bound_ == constraint_lower_bound_) {
2386 ScalarProduct(solution->primal_values, deleted_rows.column(row_as_col));
2393 const auto is_smaller_with_tolerance = [tolerance](
Fractional a,
2397 if (variable_lower_bound_ != -
kInfinity) {
2399 activity + e_.coeff * variable_lower_bound_;
2400 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_lb) &&
2401 is_smaller_with_tolerance(activity_at_lb, constraint_upper_bound_)) {
2402 solution->primal_values[e_.col] = variable_lower_bound_;
2407 if (variable_upper_bound_ !=
kInfinity) {
2409 activity + e_.coeff * variable_upper_bound_;
2410 if (is_smaller_with_tolerance(constraint_lower_bound_, actibity_at_ub) &&
2411 is_smaller_with_tolerance(actibity_at_ub, constraint_upper_bound_)) {
2412 solution->primal_values[e_.col] = variable_upper_bound_;
2421 if (constraint_lower_bound_ == -
kInfinity &&
2423 solution->primal_values[e_.col] = 0.0;
2431 if (constraint_lower_bound_ == constraint_upper_bound_) {
2432 solution->primal_values[e_.col] =
2433 (constraint_lower_bound_ - activity) / e_.coeff;
2438 bool set_constraint_to_lower_bound;
2439 if (constraint_lower_bound_ == -
kInfinity) {
2440 set_constraint_to_lower_bound =
false;
2441 }
else if (constraint_upper_bound_ ==
kInfinity) {
2442 set_constraint_to_lower_bound =
true;
2446 const Fractional to_lb = (constraint_lower_bound_ - activity) / e_.coeff;
2447 const Fractional to_ub = (constraint_upper_bound_ - activity) / e_.coeff;
2448 set_constraint_to_lower_bound =
2449 std::max(variable_lower_bound_ - to_lb, to_lb - variable_upper_bound_) <
2450 std::max(variable_lower_bound_ - to_ub, to_ub - variable_upper_bound_);
2453 if (set_constraint_to_lower_bound) {
2454 solution->primal_values[e_.col] =
2455 (constraint_lower_bound_ - activity) / e_.coeff;
2458 solution->primal_values[e_.col] =
2459 (constraint_upper_bound_ - activity) / e_.coeff;
2464 void SingletonPreprocessor::DeleteSingletonColumnInEquality(
2465 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2468 const SparseColumn& row_as_column = transpose.column(transpose_col);
2469 undo_stack_.push_back(
2480 const Fractional rhs = lp->constraint_upper_bounds()[e.row];
2483 lp->SetObjectiveOffset(lp->objective_offset() + rhs * multiplier);
2488 lp->objective_coefficients()[
col] - e.coefficient() * multiplier;
2495 if (std::abs(new_cost) <
parameters_.preprocessor_zero_tolerance()) {
2498 lp->SetObjectiveCoefficient(
col, new_cost);
2503 UpdateConstraintBoundsWithVariableBounds(e, lp);
2507 void SingletonUndo::SingletonColumnInEqualityUndo(
2508 const GlopParameters&
parameters,
const SparseMatrix& deleted_rows,
2509 ProblemSolution* solution)
const {
2511 ZeroCostSingletonColumnUndo(
parameters, deleted_rows, solution);
2515 solution->dual_values[e_.row] += cost_ / e_.coeff;
2522 void SingletonUndo::MakeConstraintAnEqualityUndo(
2523 ProblemSolution* solution)
const {
2525 solution->constraint_statuses[e_.row] = constraint_status_;
2529 bool SingletonPreprocessor::MakeConstraintAnEqualityIfPossible(
2530 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2533 const Fractional cst_lower_bound = lp->constraint_lower_bounds()[e.row];
2534 const Fractional cst_upper_bound = lp->constraint_upper_bounds()[e.row];
2535 if (cst_lower_bound == cst_upper_bound)
return true;
2541 const DenseRow& variable_ubs = lp->variable_upper_bounds();
2542 const DenseRow& variable_lbs = lp->variable_lower_bounds();
2543 if (e.row >= row_sum_is_cached_.
size() || !row_sum_is_cached_[e.row]) {
2544 if (e.row >= row_sum_is_cached_.
size()) {
2545 const int new_size = e.row.value() + 1;
2546 row_sum_is_cached_.
resize(new_size);
2547 row_lb_sum_.resize(new_size);
2548 row_ub_sum_.resize(new_size);
2550 row_sum_is_cached_[e.row] =
true;
2551 row_lb_sum_[e.row].Add(cst_lower_bound);
2552 row_ub_sum_[e.row].Add(cst_upper_bound);
2563 if (column_deletion_helper_.
IsColumnMarked(row_as_col))
continue;
2564 if (entry.coefficient() > 0.0) {
2565 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2566 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2568 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2569 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2581 c > 0.0 ? row_lb_sum_[e.row].SumWithout(-c * variable_ubs[e.col]) / c
2582 : row_ub_sum_[e.row].SumWithout(-c * variable_ubs[e.col]) / c;
2584 c > 0.0 ? row_ub_sum_[e.row].SumWithout(-c * variable_lbs[e.col]) / c
2585 : row_lb_sum_[e.row].SumWithout(-c * variable_lbs[e.col]) / c;
2591 lp->GetObjectiveCoefficientForMinimizationVersion(e.col);
2592 DCHECK_NE(
cost, 0.0);
2598 ub, lp->variable_upper_bounds()[e.col])) {
2601 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
2604 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2608 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
2611 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2615 if (
status_ == ProblemStatus::INFEASIBLE_OR_UNBOUNDED) {
2617 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2619 << e.col <<
" has a cost (for minimization) of " <<
cost
2620 <<
" and is unbounded towards kInfinity.";
2637 lp->SetVariableBounds(e.col, lp->variable_lower_bounds()[e.col],
kInfinity);
2640 lp->variable_lower_bounds()[e.col], lb)) {
2643 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
2646 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2650 status_ = ProblemStatus::INFEASIBLE_OR_UNBOUNDED;
2653 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2657 if (
status_ == ProblemStatus::INFEASIBLE_OR_UNBOUNDED) {
2659 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2661 << e.col <<
" has a cost (for minimization) of " <<
cost
2662 <<
" and is unbounded towards -kInfinity.";
2667 lp->SetVariableBounds(e.col, -
kInfinity,
2668 lp->variable_upper_bounds()[e.col]);
2671 if (lp->constraint_lower_bounds()[e.row] ==
2672 lp->constraint_upper_bounds()[e.row]) {
2673 undo_stack_.push_back(SingletonUndo(
2687 ColIndex num_cols(matrix.
num_cols());
2688 RowIndex num_rows(matrix.
num_rows());
2693 std::vector<ColIndex> column_to_process;
2694 for (ColIndex
col(0);
col < num_cols; ++
col) {
2696 if (column_degree[
col] == 1) {
2697 column_to_process.push_back(
col);
2703 std::vector<RowIndex> row_to_process;
2704 for (RowIndex
row(0);
row < num_rows; ++
row) {
2706 if (row_degree[
row] == 1) {
2707 row_to_process.push_back(
row);
2713 (!column_to_process.empty() || !row_to_process.empty())) {
2715 const ColIndex
col = column_to_process.back();
2716 column_to_process.pop_back();
2717 if (column_degree[
col] <= 0)
continue;
2718 const MatrixEntry e = GetSingletonColumnMatrixEntry(
col, matrix);
2720 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2727 DeleteZeroCostSingletonColumn(transpose, e, lp);
2728 }
else if (MakeConstraintAnEqualityIfPossible(transpose, e, lp)) {
2729 DeleteSingletonColumnInEquality(transpose, e, lp);
2733 --row_degree[e.row];
2734 if (row_degree[e.row] == 1) {
2735 row_to_process.push_back(e.row);
2739 const RowIndex
row = row_to_process.back();
2740 row_to_process.pop_back();
2741 if (row_degree[
row] <= 0)
continue;
2742 const MatrixEntry e = GetSingletonRowMatrixEntry(
row, transpose);
2744 DeleteSingletonRow(e, lp);
2745 --column_degree[e.col];
2746 if (column_degree[e.col] == 1) {
2747 column_to_process.push_back(e.col);
2755 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
2773 for (
int i = undo_stack_.size() - 1; i >= 0; --i) {
2774 undo_stack_[i].Undo(
parameters_, deleted_columns_, deleted_rows_, solution);
2778 MatrixEntry SingletonPreprocessor::GetSingletonColumnMatrixEntry(
2782 DCHECK_NE(0.0, e.coefficient());
2783 return MatrixEntry(e.row(),
col, e.coefficient());
2787 LOG(DFATAL) <<
"No unmarked entry in a column that is supposed to have one.";
2789 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2792 MatrixEntry SingletonPreprocessor::GetSingletonRowMatrixEntry(
2793 RowIndex
row,
const SparseMatrix& transpose) {
2797 DCHECK_NE(0.0, e.coefficient());
2798 return MatrixEntry(
row,
col, e.coefficient());
2802 LOG(DFATAL) <<
"No unmarked entry in a row that is supposed to have one.";
2804 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2815 if (num_cols == 0)
return false;
2821 Fractional num_non_zero_objective_coefficients = 0.0;
2822 for (ColIndex
col(0);
col < num_cols; ++
col) {
2824 row_degree[e.row()] += 1.0;
2827 num_non_zero_objective_coefficients += 1.0;
2839 const EntryIndex initial_num_entries = lp->
num_entries();
2840 int num_zeroed_objective_coefficients = 0;
2841 for (ColIndex
col(0);
col < num_cols; ++
col) {
2849 std::max(std::abs(lower_bound), std::abs(upper_bound));
2850 if (max_magnitude ==
kInfinity || max_magnitude == 0)
continue;
2851 const Fractional threshold = allowed_impact / max_magnitude;
2853 threshold, row_degree);
2856 num_non_zero_objective_coefficients *
2860 ++num_zeroed_objective_coefficients;
2866 VLOG(1) <<
"Removed " << initial_num_entries -
num_entries
2867 <<
" near-zero entries.";
2869 if (num_zeroed_objective_coefficients > 0) {
2870 VLOG(1) <<
"Removed " << num_zeroed_objective_coefficients
2871 <<
" near-zero objective coefficients.";
2889 if (num_cols == 0)
return false;
2891 changed_columns_.clear();
2892 int num_singletons = 0;
2893 for (ColIndex
col(0);
col < num_cols; ++
col) {
2905 changed_columns_.push_back(
col);
2908 VLOG(1) <<
"Changed the sign of " << changed_columns_.size() <<
" columns.";
2909 VLOG(1) << num_singletons <<
" singleton columns left.";
2910 return !changed_columns_.empty();
2917 for (
int i = 0; i < changed_columns_.size(); ++i) {
2918 const ColIndex
col = changed_columns_[i];
2952 for (RowIndex
row(0);
row < num_rows; ++
row) {
2967 int entry_index = 0;
2971 r.col[entry_index] =
col;
2972 r.coeff[entry_index] = e.coefficient();
2973 DCHECK_NE(0.0, r.coeff[entry_index]);
2980 if (entry_index < 2)
continue;
2986 for (
int col_choice = 0; col_choice < NUM_DOUBLETON_COLS; ++col_choice) {
2987 const ColIndex
col = r.col[col_choice];
2996 if (r.lb[DELETED] == r.ub[DELETED] || r.lb[MODIFIED] == r.ub[MODIFIED]) {
3013 const Fractional carry_over_offset = r.rhs / r.coeff[MODIFIED];
3015 -r.coeff[DELETED] / r.coeff[MODIFIED];
3017 carry_over_factor == 0.0) {
3025 r.lb[DELETED] * carry_over_factor + carry_over_offset;
3027 r.ub[DELETED] * carry_over_factor + carry_over_offset;
3028 if (carry_over_factor < 0) {
3029 std::swap(carried_over_lb, carried_over_ub);
3031 if (carried_over_lb <= lb) {
3036 lb = carried_over_lb;
3041 carry_over_factor > 0 ? r.lb[DELETED] : r.ub[DELETED]);
3043 if (carried_over_ub >= ub) {
3048 ub = carried_over_ub;
3053 carry_over_factor > 0 ? r.ub[DELETED] : r.lb[DELETED]);
3062 restore_stack_.push_back(r);
3069 DCHECK_NE(r.coeff[DELETED], 0.0);
3071 -r.coeff[MODIFIED] / r.coeff[DELETED];
3072 const Fractional constant_offset_factor = r.rhs / r.coeff[DELETED];
3074 if (!
IsFinite(substitution_factor) || substitution_factor == 0.0 ||
3075 !
IsFinite(constant_offset_factor)) {
3079 r.column[DELETED].AddMultipleToSparseVectorAndDeleteCommonIndex(
3088 r.objective_coefficient[MODIFIED] +
3089 substitution_factor * r.objective_coefficient[DELETED];
3090 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
3100 SubtractColumnMultipleFromConstraintBound(r.col[DELETED],
3101 constant_offset_factor, lp);
3110 return !column_deletion_helper_.
IsEmpty();
3119 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
3122 LOG(DFATAL) <<
"FIXED variable produced by DoubletonPreprocessor!";
3129 ABSL_FALLTHROUGH_INTENDED;
3136 ABSL_FALLTHROUGH_INTENDED;
3144 ? r.bound_backtracking_at_lower_bound
3145 : r.bound_backtracking_at_upper_bound;
3146 const ColIndex bounded_var = r.col[bound_backtracking.
col_choice];
3147 const ColIndex basic_var =
3148 r.col[OtherColChoice(bound_backtracking.
col_choice)];
3163 solution->
primal_values[r.col[MODIFIED]] * r.coeff[MODIFIED]) /
3176 const ColChoice col_choice =
3183 r.objective_coefficient[col_choice] -
3185 solution->
dual_values[r.row] = current_reduced_cost / r.coeff[col_choice];
3190 saved_row_upper_bounds_, solution);
3197 DCHECK_EQ(row_lower_bounds.
size(), num_rows);
3198 DCHECK_EQ(row_upper_bounds.
size(), num_rows);
3199 for (RowIndex
row(0);
row < num_rows; ++
row) {
3203 if (row_lower_bounds[
row] == row_upper_bounds[
row])
continue;
3215 void DoubletonEqualityRowPreprocessor::
3216 SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r) {
3218 swap(r->col[DELETED], r->col[MODIFIED]);
3219 swap(r->coeff[DELETED], r->coeff[MODIFIED]);
3220 swap(r->lb[DELETED], r->lb[MODIFIED]);
3221 swap(r->ub[DELETED], r->ub[MODIFIED]);
3222 swap(r->column[DELETED], r->column[MODIFIED]);
3223 swap(r->objective_coefficient[DELETED], r->objective_coefficient[MODIFIED]);
3233 if (
parameters_.solve_dual_problem() == GlopParameters::NEVER_DO) {
3260 if (
parameters_.solve_dual_problem() == GlopParameters::LET_SOLVER_DECIDE) {
3261 if (1.0 * primal_num_rows_.value() <
3262 parameters_.dualizer_threshold() * primal_num_cols_.value()) {
3272 variable_lower_bounds_.
assign(num_cols, 0.0);
3273 variable_upper_bounds_.
assign(num_cols, 0.0);
3274 for (ColIndex
col(0);
col < num_cols; ++
col) {
3279 variable_lower_bounds_[
col] = lower;
3280 variable_upper_bounds_[
col] = upper;
3281 const Fractional value = MinInMagnitudeOrZeroIfInfinite(lower, upper);
3284 SubtractColumnMultipleFromConstraintBound(
col,
value, lp);
3292 dual_status_correspondence_.
clear();
3293 for (RowIndex
row(0);
row < primal_num_rows_; ++
row) {
3296 if (lower_bound == upper_bound) {
3303 LOG(DFATAL) <<
"There should be no free constraint in this lp.";
3306 slack_or_surplus_mapping_.
clear();
3307 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3317 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3350 DenseRow new_primal_values(primal_num_cols_, 0.0);
3354 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3361 const Fractional shift = MinInMagnitudeOrZeroIfInfinite(lower, upper);
3372 new_variable_statuses[
col] = ComputeVariableStatus(shift, lower, upper);
3381 const ColIndex end = dual_status_correspondence_.
size();
3383 DCHECK_EQ(end - begin, slack_or_surplus_mapping_.
size());
3386 const ColIndex
col = slack_or_surplus_mapping_[
index - begin];
3394 new_primal_values[
col] = variable_upper_bounds_[
col];
3397 new_primal_values[
col] = variable_lower_bounds_[
col];
3405 DenseColumn new_dual_values(primal_num_rows_, 0.0);
3412 Fractional sign = primal_is_maximization_problem_ ? -1 : 1;
3413 for (RowIndex
row(0);
row < primal_num_rows_; ++
row) {
3431 new_constraint_statuses[
row] =
3438 new_dual_values[
row] +=
3444 DCHECK(new_dual_values[
row] == 0 ||
3458 case ProblemStatus::PRIMAL_INFEASIBLE:
3459 return ProblemStatus::DUAL_INFEASIBLE;
3460 case ProblemStatus::DUAL_INFEASIBLE:
3461 return ProblemStatus::PRIMAL_INFEASIBLE;
3462 case ProblemStatus::PRIMAL_UNBOUNDED:
3463 return ProblemStatus::DUAL_UNBOUNDED;
3464 case ProblemStatus::DUAL_UNBOUNDED:
3465 return ProblemStatus::PRIMAL_UNBOUNDED;
3466 case ProblemStatus::PRIMAL_FEASIBLE:
3467 return ProblemStatus::DUAL_FEASIBLE;
3468 case ProblemStatus::DUAL_FEASIBLE:
3469 return ProblemStatus::PRIMAL_FEASIBLE;
3484 bool all_variable_domains_contain_zero =
true;
3486 variable_initial_lbs_.
assign(num_cols, 0.0);
3487 variable_initial_ubs_.
assign(num_cols, 0.0);
3488 for (ColIndex
col(0);
col < num_cols; ++
col) {
3491 if (0.0 < variable_initial_lbs_[
col] || 0.0 > variable_initial_ubs_[
col]) {
3492 all_variable_domains_contain_zero =
false;
3495 VLOG(1) <<
"Maximum variable bounds magnitude (before shift): "
3496 << ComputeMaxVariableBoundsMagnitude(*lp);
3499 if (all_variable_domains_contain_zero)
return false;
3503 int num_bound_shifts = 0;
3507 offsets_.
assign(num_cols, 0.0);
3508 for (ColIndex
col(0);
col < num_cols; ++
col) {
3509 if (0.0 < variable_initial_lbs_[
col] || 0.0 > variable_initial_ubs_[
col]) {
3510 Fractional offset = MinInMagnitudeOrZeroIfInfinite(
3511 variable_initial_lbs_[
col], variable_initial_ubs_[
col]);
3519 offset = trunc(offset);
3521 DCHECK_NE(offset, 0.0);
3523 offsets_[
col] = offset;
3525 variable_initial_ubs_[
col] - offset);
3528 row_offsets[e.row()].Add(e.coefficient() * offset);
3534 VLOG(1) <<
"Maximum variable bounds magnitude (after " << num_bound_shifts
3535 <<
" shifts): " << ComputeMaxVariableBoundsMagnitude(*lp);
3538 for (RowIndex
row(0);
row < num_rows; ++
row) {
3552 for (ColIndex
col(0);
col < num_cols; ++
col) {
3558 ABSL_FALLTHROUGH_INTENDED;
3586 variable_lower_bounds_.
assign(num_cols, 0.0);
3587 variable_upper_bounds_.
assign(num_cols, 0.0);
3588 for (ColIndex
col(0);
col < num_cols; ++
col) {
3620 for (ColIndex
col(0);
col < num_cols; ++
col) {
3623 ABSL_FALLTHROUGH_INTENDED;
3631 ABSL_FALLTHROUGH_INTENDED;
3682 for (RowIndex
row(0);
row < num_rows; ++
row) {
3689 switch (variable_status) {