29 #include "absl/strings/str_format.h"
47 return absl::StrFormat(
"[%g, %g]", lb, ub);
51 double trunc(
double d) {
return d > 0 ? floor(d) : ceil(d); }
61 in_mip_context_(false),
62 infinite_time_limit_(
TimeLimit::Infinite()),
63 time_limit_(infinite_time_limit_.get()) {}
70 #define RUN_PREPROCESSOR(name) \
71 RunAndPushIfRelevant(std::unique_ptr<Preprocessor>(new name(¶meters_)), \
72 #name, time_limit_, lp)
91 const int kMaxNumPasses = 20;
92 for (
int i = 0; i < kMaxNumPasses; ++i) {
93 const int old_stack_size = preprocessors_.size();
106 if (preprocessors_.size() == old_stack_size) {
108 SOLVER_LOG(logger_,
"Reached fixed point after presolve pass #", i);
122 const int old_stack_size = preprocessors_.size();
128 if (old_stack_size != preprocessors_.size()) {
142 return !preprocessors_.empty();
145 #undef RUN_PREPROCESSOR
147 void MainLpPreprocessor::RunAndPushIfRelevant(
154 const double start_time =
time_limit->GetElapsedTime();
165 const EntryIndex new_num_entries = lp->
num_entries();
166 const double preprocess_time =
time_limit->GetElapsedTime() - start_time;
169 "%-45s: %d(%d) rows, %d(%d) columns, %d(%d) entries. (%fs)",
176 static_cast<int64_t
>(new_num_entries.value()),
177 static_cast<int64_t
>(new_num_entries.value() -
178 initial_num_entries_.value()),
197 p->RecoverSolution(solution);
203 while (!preprocessors_.empty()) {
204 preprocessors_.back()->RecoverSolution(solution);
205 preprocessors_.pop_back();
214 const int index = saved_columns_.size();
215 CHECK(saved_columns_index_.insert({col, index}).second);
216 saved_columns_.push_back(
column);
221 const int index = saved_columns_.size();
222 const bool inserted = saved_columns_index_.insert({
col,
index}).second;
223 if (inserted) saved_columns_.push_back(
column);
227 const auto it = saved_columns_index_.find(
col);
228 CHECK(it != saved_columns_index_.end());
229 return saved_columns_[it->second];
233 const auto it = saved_columns_index_.find(
col);
234 return it == saved_columns_index_.end() ? empty_column_
235 : saved_columns_[it->second];
239 is_column_deleted_.
clear();
240 stored_value_.
clear();
250 if (
col >= is_column_deleted_.
size()) {
251 is_column_deleted_.
resize(
col + 1,
false);
255 is_column_deleted_[
col] =
true;
256 stored_value_[
col] = fixed_value;
264 ColIndex old_index(0);
265 for (ColIndex
col(0);
col < is_column_deleted_.
size(); ++
col) {
266 if (is_column_deleted_[
col]) {
279 for (; old_index < num_cols; ++old_index) {
295 if (
row >= is_row_deleted_.
size()) {
298 is_row_deleted_[
row] =
true;
302 if (
row >= is_row_deleted_.
size())
return;
303 is_row_deleted_[
row] =
false;
307 return is_row_deleted_;
313 RowIndex old_index(0);
314 const RowIndex
end = is_row_deleted_.
size();
316 if (is_row_deleted_[
row]) {
330 for (; old_index < num_rows; ++old_index) {
381 Fractional ComputeMaxVariableBoundsMagnitude(
const LinearProgram& lp) {
383 const ColIndex num_cols = lp.num_variables();
384 for (ColIndex
col(0);
col < num_cols; ++
col) {
386 max_bounds_magnitude,
387 std::max(MagnitudeOrZeroIfInfinite(lp.variable_lower_bounds()[
col]),
388 MagnitudeOrZeroIfInfinite(lp.variable_upper_bounds()[
col])));
390 return max_bounds_magnitude;
398 column_deletion_helper_.
Clear();
400 for (ColIndex
col(0);
col < num_cols; ++
col) {
407 if (objective_coefficient == 0) {
421 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, empty column " <<
col
422 <<
" has a minimization cost of " << objective_coefficient
436 return !column_deletion_helper_.
IsEmpty();
454 void SubtractColumnMultipleFromConstraintBound(ColIndex
col,
460 const RowIndex
row = e.row();
474 struct ColumnWithRepresentativeAndScaledCost {
475 ColumnWithRepresentativeAndScaledCost(ColIndex _col, ColIndex _representative,
482 bool operator<(
const ColumnWithRepresentativeAndScaledCost& other)
const {
485 return col < other.col;
506 int num_proportionality_classes = 0;
507 std::vector<ColIndex> proportional_columns;
513 ++num_proportionality_classes;
516 proportional_columns.push_back(
col);
519 if (proportional_columns.empty())
return false;
520 VLOG(1) <<
"The problem contains " << proportional_columns.size()
521 <<
" columns which belong to " << num_proportionality_classes
522 <<
" proportionality classes.";
526 column_factors_.
assign(num_cols, 0.0);
527 for (
const ColIndex
col : proportional_columns) {
529 column_factors_[
col] =
column.GetFirstCoefficient();
541 for (
const ColIndex
col : proportional_columns) {
545 const bool is_rc_positive_or_zero =
547 const bool is_rc_negative_or_zero =
549 bool is_slope_upper_bounded = is_rc_positive_or_zero;
550 bool is_slope_lower_bounded = is_rc_negative_or_zero;
551 if (column_factors_[
col] < 0.0) {
552 std::swap(is_slope_lower_bounded, is_slope_upper_bounded);
556 column_factors_[
col];
557 if (is_slope_lower_bounded) {
561 if (is_slope_upper_bounded) {
568 for (
const ColIndex
col : proportional_columns) {
576 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, no feasible dual values"
577 <<
" can satisfy the constraints of the proportional columns"
579 <<
" the associated quantity must be in ["
589 for (
const ColIndex
col : proportional_columns) {
593 column_factors_[
col];
596 bool variable_can_be_fixed =
false;
604 variable_can_be_fixed =
true;
609 variable_can_be_fixed =
true;
613 if (variable_can_be_fixed) {
618 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED.";
631 std::vector<ColumnWithRepresentativeAndScaledCost> sorted_columns;
632 for (
const ColIndex
col : proportional_columns) {
637 sorted_columns.
push_back(ColumnWithRepresentativeAndScaledCost(
642 std::sort(sorted_columns.begin(), sorted_columns.end());
651 for (
int i = 0; i < sorted_columns.size();) {
652 const ColIndex target_col = sorted_columns[i].col;
653 const ColIndex target_representative = sorted_columns[i].representative;
654 const Fractional target_scaled_cost = sorted_columns[i].scaled_cost;
661 for (++i; i < sorted_columns.size(); ++i) {
662 if (sorted_columns[i].
representative != target_representative)
break;
663 if (std::abs(sorted_columns[i].
scaled_cost - target_scaled_cost) >=
668 const ColIndex
col = sorted_columns[i].col;
673 merged_columns_[
col] = target_col;
678 column_factors_[
col] / column_factors_[target_col];
692 if (bound_factor < 0.0) {
698 SubtractColumnMultipleFromConstraintBound(
col, target_value, lp);
707 if (num_merged > 0) {
708 merged_columns_[target_col] = target_col;
709 const Fractional target_value = MinInMagnitudeOrZeroIfInfinite(
710 lower_bounds_[target_col], upper_bounds_[target_col]);
714 SubtractColumnMultipleFromConstraintBound(target_col, target_value, lp);
721 return !column_deletion_helper_.
IsEmpty();
732 const ColIndex num_cols = merged_columns_.
size();
735 DenseRow distance_to_bound(num_cols, 0.0);
736 DenseRow wanted_value(num_cols, 0.0);
740 for (ColIndex
col(0);
col < num_cols; ++
col) {
741 if (merged_columns_[
col] ==
col) {
745 if (distance_to_upper_bound < distance_to_lower_bound) {
746 distance_to_bound[
col] = distance_to_upper_bound;
747 is_distance_to_upper_bound[
col] =
true;
749 distance_to_bound[
col] = distance_to_lower_bound;
750 is_distance_to_upper_bound[
col] =
false;
752 is_representative_basic[
col] =
759 lower_bounds_[
col], upper_bounds_[
col]);
766 for (ColIndex
col(0);
col < num_cols; ++
col) {
778 const bool to_upper_bound =
779 (bound_factor > 0.0) == is_distance_to_upper_bound[
representative];
780 if (
width <= scaled_distance) {
782 to_upper_bound ? lower_bounds_[
col] : upper_bounds_[
col];
785 lower_bounds_[
col], upper_bounds_[
col]);
789 to_upper_bound ? upper_bounds_[
col] - scaled_distance
790 : lower_bounds_[
col] + scaled_distance;
813 const bool use_this_variable =
814 (error * bound_factor > 0.0) ? (upper_bounds_[
col] ==
kInfinity)
816 if (use_this_variable) {
849 row_factors_.
assign(num_rows, 0.0);
850 for (RowIndex
row(0);
row < num_rows; ++
row) {
852 if (!row_transpose.
IsEmpty()) {
871 transpose,
parameters_.preprocessor_zero_tolerance());
873 int num_proportional_rows = 0;
874 for (RowIndex
row(0);
row < num_rows; ++
row) {
877 mapping[representative_row_as_col] = representative_row_as_col;
878 is_a_representative[
ColToRowIndex(representative_row_as_col)] =
true;
879 ++num_proportional_rows;
885 for (RowIndex
row(0);
row < num_rows; ++
row) {
891 const RowIndex representative_row =
ColToRowIndex(mapping[row_as_col]);
894 row_factors_[representative_row] / row_factors_[
row];
904 lower_bound_sources_[representative_row] =
row;
908 upper_bound_sources_[representative_row] =
row;
916 for (RowIndex
row(0);
row < num_rows; ++
row) {
917 if (!is_a_representative[
row])
continue;
918 const RowIndex lower_source = lower_bound_sources_[
row];
919 const RowIndex upper_source = upper_bound_sources_[
row];
924 if (lower_source == upper_source) {
928 row_deletion_helper_.
UnmarkRow(lower_source);
941 row_deletion_helper_.
UnmarkRow(lower_source);
946 row_deletion_helper_.
UnmarkRow(upper_source);
954 RowIndex new_representative = lower_source;
955 RowIndex other = upper_source;
956 if (std::abs(row_factors_[new_representative]) <
957 std::abs(row_factors_[other])) {
963 row_factors_[new_representative] / row_factors_[other];
970 lower_bound_sources_[new_representative] = new_representative;
971 upper_bound_sources_[new_representative] = new_representative;
974 lower_bound_sources_[new_representative] = other;
978 if (new_ub < lp->constraint_upper_bounds()[new_representative]) {
979 upper_bound_sources_[new_representative] = other;
983 const RowIndex new_lower_source =
984 lower_bound_sources_[new_representative];
985 if (new_lower_source == upper_bound_sources_[new_representative]) {
986 row_deletion_helper_.
UnmarkRow(new_lower_source);
987 lower_bound_sources_[new_representative] =
kInvalidRow;
988 upper_bound_sources_[new_representative] =
kInvalidRow;
997 if (new_lb > new_ub) {
998 if (lower_bound_sources_[new_representative] == new_representative) {
1004 row_deletion_helper_.
UnmarkRow(new_representative);
1011 return !row_deletion_helper_.
IsEmpty();
1024 for (RowIndex
row(0);
row < num_rows; ++
row) {
1025 const RowIndex lower_source = lower_bound_sources_[
row];
1026 const RowIndex upper_source = upper_bound_sources_[
row];
1028 DCHECK_NE(lower_source, upper_source);
1029 DCHECK(lower_source ==
row || upper_source ==
row);
1041 const Fractional corrected_dual_value = lp_is_maximization_problem_
1044 if (corrected_dual_value != 0.0) {
1054 DCHECK_EQ(0.0, solution->
dual_values[lower_source]);
1055 const Fractional factor = row_factors_[
row] / row_factors_[lower_source];
1064 DCHECK_EQ(0.0, solution->
dual_values[upper_source]);
1065 const Fractional factor = row_factors_[
row] / row_factors_[upper_source];
1092 for (ColIndex
col(0);
col < num_cols; ++
col) {
1100 SubtractColumnMultipleFromConstraintBound(
col, fixed_value, lp);
1107 return !column_deletion_helper_.
IsEmpty();
1131 for (ColIndex
col(0);
col < num_cols; ++
col) {
1135 const RowIndex
row = e.row();
1138 implied_lower_bounds[
row] +=
lower * coeff;
1139 implied_upper_bounds[
row] +=
upper * coeff;
1141 implied_lower_bounds[
row] +=
upper * coeff;
1142 implied_upper_bounds[
row] +=
lower * coeff;
1150 int num_implied_free_constraints = 0;
1151 int num_forcing_constraints = 0;
1152 is_forcing_up_.
assign(num_rows,
false);
1154 for (RowIndex
row(0);
row < num_rows; ++
row) {
1155 if (row_degree[
row] == 0)
continue;
1161 implied_upper_bounds[
row]) ||
1164 VLOG(1) <<
"implied bound " << implied_lower_bounds[
row] <<
" "
1165 << implied_upper_bounds[
row];
1175 is_forcing_down[
row] =
true;
1176 ++num_forcing_constraints;
1180 implied_lower_bounds[
row])) {
1181 is_forcing_up_[
row] =
true;
1182 ++num_forcing_constraints;
1193 implied_lower_bounds[
row]) &&
1197 ++num_implied_free_constraints;
1201 if (num_implied_free_constraints > 0) {
1202 VLOG(1) << num_implied_free_constraints <<
" implied free constraints.";
1205 if (num_forcing_constraints > 0) {
1206 VLOG(1) << num_forcing_constraints <<
" forcing constraints.";
1208 costs_.
resize(num_cols, 0.0);
1209 for (ColIndex
col(0);
col < num_cols; ++
col) {
1213 bool is_forced =
false;
1216 if (is_forcing_down[e.row()]) {
1229 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1230 << std::fixed << std::setprecision(10) <<
lower <<
", "
1231 <<
upper <<
"]. coeff:" << e.coefficient();
1238 if (is_forcing_up_[e.row()]) {
1247 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1248 << std::fixed << std::setprecision(10) <<
lower <<
", "
1249 <<
upper <<
"]. coeff:" << e.coefficient();
1268 for (RowIndex
row(0);
row < num_rows; ++
row) {
1275 if (is_forcing_down[
row] || is_forcing_up_[
row]) {
1283 return !column_deletion_helper_.
IsEmpty();
1293 struct DeletionEntry {
1298 std::vector<DeletionEntry> entries;
1302 for (ColIndex
col(0);
col < size; ++
col) {
1308 const RowIndex
row = e.row();
1311 last_coefficient = e.coefficient();
1315 entries.push_back({last_row,
col, last_coefficient});
1320 std::sort(entries.begin(), entries.end(),
1321 [](
const DeletionEntry&
a,
const DeletionEntry&
b) {
1322 if (a.row == b.row) return a.col < b.col;
1323 return a.row < b.row;
1336 for (
int i = 0; i < entries.size();) {
1337 const RowIndex
row = entries[i].row;
1343 for (; i < entries.size(); ++i) {
1344 if (entries[i].
row !=
row)
break;
1345 const ColIndex
col = entries[i].col;
1349 const Fractional reduced_cost = costs_[
col] - scalar_product;
1351 if (is_forcing_up_[
row] == !lp_is_maximization_problem_) {
1352 if (
bound < new_dual_value) {
1353 new_dual_value =
bound;
1354 new_basic_column =
col;
1357 if (
bound > new_dual_value) {
1358 new_dual_value =
bound;
1359 new_basic_column =
col;
1378 struct ColWithDegree {
1381 ColWithDegree(ColIndex c, EntryIndex n) :
col(c),
num_entries(n) {}
1382 bool operator<(
const ColWithDegree& other)
const {
1384 return col < other.col;
1394 if (!
parameters_.use_implied_free_preprocessor())
return false;
1402 const int size = num_rows.value();
1411 for (ColIndex
col(0);
col < num_cols; ++
col) {
1417 if (e.coefficient() < 0.0)
std::swap(entry_lb, entry_ub);
1418 lb_sums[e.row()].Add(entry_lb);
1419 ub_sums[e.row()].Add(entry_ub);
1429 for (RowIndex
row(0);
row < num_rows; ++
row) {
1438 variable_offsets_.
assign(num_cols, 0.0);
1455 std::vector<ColWithDegree> col_by_degree;
1456 for (ColIndex
col(0);
col < num_cols; ++
col) {
1457 col_by_degree.push_back(
1460 std::sort(col_by_degree.begin(), col_by_degree.end());
1463 int num_already_free_variables = 0;
1464 int num_implied_free_variables = 0;
1465 int num_fixed_variables = 0;
1466 for (ColWithDegree col_with_degree : col_by_degree) {
1467 const ColIndex
col = col_with_degree.col;
1473 ++num_already_free_variables;
1484 if (used_rows[e.row()])
continue;
1490 if (coeff < 0.0)
std::swap(entry_lb, entry_ub);
1501 coeff > 0.0 ? -ub_sums[e.row()].SumWithoutUb(entry_ub) / coeff
1502 : -lb_sums[e.row()].SumWithoutLb(entry_lb) / coeff;
1504 coeff > 0.0 ? -lb_sums[e.row()].SumWithoutLb(entry_lb) / coeff
1505 : -ub_sums[e.row()].SumWithoutUb(entry_ub) / coeff;
1507 overall_implied_lb =
std::max(overall_implied_lb, implied_lb);
1508 overall_implied_ub =
std::min(overall_implied_ub, implied_ub);
1515 overall_implied_ub)) {
1523 overall_implied_lb) ||
1529 ++num_fixed_variables;
1532 overall_implied_lb)) {
1538 ++num_fixed_variables;
1545 overall_implied_lb) &&
1548 ++num_implied_free_variables;
1551 used_rows[e.row()] =
true;
1575 if (offset != 0.0) {
1576 variable_offsets_[
col] = offset;
1577 SubtractColumnMultipleFromConstraintBound(
col, offset, lp);
1579 postsolve_status_of_free_variables_[
col] =
1583 VLOG(1) << num_already_free_variables <<
" free variables in the problem.";
1584 VLOG(1) << num_implied_free_variables <<
" implied free columns.";
1585 VLOG(1) << num_fixed_variables <<
" variables can be fixed.";
1587 return num_implied_free_variables > 0;
1594 for (ColIndex
col(0);
col < num_cols; ++
col) {
1597 DCHECK_EQ(0.0, variable_offsets_[
col]);
1602 postsolve_status_of_free_variables_[
col];
1624 for (ColIndex doubleton_col(0); doubleton_col < num_cols; ++doubleton_col) {
1633 r.col = doubleton_col;
1637 if (row_deletion_helper_.
IsRowMarked(e.row()))
break;
1638 r.row[
index] = e.row();
1639 r.coeff[
index] = e.coefficient();
1640 DCHECK_NE(0.0, e.coefficient());
1643 if (
index != NUM_ROWS)
continue;
1649 DCHECK_EQ(r.coeff[MODIFIED],
1655 if (std::abs(r.coeff[DELETED]) < std::abs(r.coeff[MODIFIED])) {
1656 std::swap(r.coeff[DELETED], r.coeff[MODIFIED]);
1657 std::swap(r.row[DELETED], r.row[MODIFIED]);
1664 r.deleted_row_as_column.Swap(
1673 new_variable_lb /= r.coeff[DELETED];
1674 new_variable_ub /= r.coeff[DELETED];
1675 if (r.coeff[DELETED] < 0.0)
std::swap(new_variable_lb, new_variable_ub);
1681 r.deleted_row_as_column.AddMultipleToSparseVectorAndIgnoreCommonIndex(
1682 -r.coeff[MODIFIED] / r.coeff[DELETED],
ColToRowIndex(r.col),
1688 if (r.objective_coefficient != 0.0) {
1691 if (
col == r.col)
continue;
1694 e.coefficient() * r.objective_coefficient / r.coeff[DELETED];
1700 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
1708 restore_stack_.push_back(r);
1711 if (!row_deletion_helper_.
IsEmpty()) {
1724 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
1756 if (
col == r.col)
continue;
1757 new_variable_value -= (e.coefficient() / r.coeff[DELETED]) *
1770 r.objective_coefficient -
1771 r.coeff[MODIFIED] * solution->
dual_values[r.row[MODIFIED]];
1774 current_reduced_cost / r.coeff[DELETED];
1776 DCHECK_EQ(solution->
dual_values[r.row[DELETED]], 0.0);
1809 const RowIndex
row = e.row();
1816 const bool is_constraint_upper_bound_relevant =
1817 e.coefficient() > 0.0 ? !is_unbounded_up : is_unbounded_up;
1818 activity_sign_correction_[
row] =
1819 is_constraint_upper_bound_relevant ? 1.0 : -1.0;
1820 rhs_[
row] = is_constraint_upper_bound_relevant
1829 is_unbounded_[
col] =
true;
1830 Fractional initial_feasible_value = MinInMagnitudeOrZeroIfInfinite(
1833 col, initial_feasible_value,
1834 ComputeVariableStatus(initial_feasible_value,
1857 for (RowIndex
row(0);
row < num_rows; ++
row) {
1859 dual_ub_[
row] = 0.0;
1862 dual_lb_[
row] = 0.0;
1867 may_have_participated_lb_.
assign(num_cols,
false);
1868 may_have_participated_ub_.
assign(num_cols,
false);
1871 std::deque<ColIndex> columns_to_process;
1873 std::vector<RowIndex> changed_rows;
1874 for (ColIndex
col(0);
col < num_cols; ++
col) {
1875 columns_to_process.push_back(
col);
1881 const int limit = 5 * num_cols.value();
1882 for (
int count = 0; !columns_to_process.empty() && count < limit; ++count) {
1883 const ColIndex
col = columns_to_process.front();
1884 columns_to_process.pop_front();
1885 in_columns_to_process[
col] =
false;
1897 rc_lb.
Add(col_cost);
1898 rc_ub.
Add(col_cost);
1900 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
1903 rc_lb.
Add(-coeff * dual_ub_[e.row()]);
1904 rc_ub.
Add(-coeff * dual_lb_[e.row()]);
1906 rc_lb.
Add(-coeff * dual_lb_[e.row()]);
1907 rc_ub.
Add(-coeff * dual_ub_[e.row()]);
1915 bool can_be_removed =
false;
1917 bool rc_is_away_from_zero;
1918 if (rc_ub.
Sum() <= low_tolerance) {
1919 can_be_removed =
true;
1925 rc_is_away_from_zero = rc_ub.
Sum() <= -high_tolerance;
1926 can_be_removed = !may_have_participated_ub_[
col];
1928 if (rc_lb.
Sum() >= -low_tolerance) {
1932 can_be_removed =
true;
1938 rc_is_away_from_zero = rc_lb.
Sum() >= high_tolerance;
1939 can_be_removed = !may_have_participated_lb_[
col];
1943 if (can_be_removed) {
1955 if (rc_is_away_from_zero) {
1956 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, variable " <<
col
1958 <<
" and its reduced cost is in [" << rc_lb.
Sum() <<
", "
1959 << rc_ub.
Sum() <<
"]";
1972 if (col_cost != 0.0)
continue;
1979 if (IsConstraintBlockingVariable(
1980 *lp, sign_correction * e.coefficient(), e.row())) {
1998 DCHECK(!can_be_removed);
2005 changed_rows.clear();
2007 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
2009 const RowIndex
row = e.row();
2014 if (candidate < dual_ub_[
row]) {
2015 dual_ub_[
row] = candidate;
2016 may_have_participated_lb_[
col] =
true;
2022 if (candidate > dual_lb_[
row]) {
2023 dual_lb_[
row] = candidate;
2024 may_have_participated_lb_[
col] =
true;
2033 if (candidate > dual_lb_[
row]) {
2034 dual_lb_[
row] = candidate;
2035 may_have_participated_ub_[
col] =
true;
2041 if (candidate < dual_ub_[
row]) {
2042 dual_ub_[
row] = candidate;
2043 may_have_participated_ub_[
col] =
true;
2050 if (!changed_rows.empty()) {
2052 for (
const RowIndex
row : changed_rows) {
2056 if (!in_columns_to_process[
col]) {
2057 columns_to_process.push_back(
col);
2058 in_columns_to_process[
col] =
true;
2080 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
2090 struct DeletionEntry {
2095 std::vector<DeletionEntry> entries;
2100 for (RowIndex
row(0);
row < num_rows; ++
row) {
2108 if (is_unbounded_[
col]) {
2110 last_coefficient = e.coefficient();
2114 entries.push_back({
row, last_col, last_coefficient});
2119 std::sort(entries.begin(), entries.end(),
2120 [](
const DeletionEntry&
a,
const DeletionEntry&
b) {
2121 if (a.col == b.col) return a.row < b.row;
2122 return a.col < b.col;
2126 for (
int i = 0; i < entries.size();) {
2127 const ColIndex
col = entries[i].col;
2128 CHECK(is_unbounded_[
col]);
2132 for (; i < entries.size(); ++i) {
2133 if (entries[i].
col !=
col)
break;
2134 const RowIndex
row = entries[i].row;
2153 if (activity * activity_sign_correction_[
row] < 0.0) {
2155 if (std::abs(
bound) > std::abs(primal_value_shift)) {
2156 primal_value_shift =
bound;
2165 activity_sign_correction_[row_at_bound] == 1.0
2180 for (RowIndex
row(0);
row < num_rows; ++
row) {
2188 return !row_deletion_helper_.
IsEmpty();
2210 for (ColIndex
col(0);
col < num_cols; ++
col) {
2217 for (RowIndex
row(0);
row < num_rows; ++
row) {
2218 if (degree[
row] == 0) {
2225 VLOG(1) <<
"Problem PRIMAL_INFEASIBLE, constraint " <<
row
2226 <<
" is empty and its range ["
2236 return !row_deletion_helper_.
IsEmpty();
2253 is_maximization_(lp.IsMaximizationProblem()),
2255 cost_(lp.objective_coefficients()[e.
col]),
2258 constraint_lower_bound_(lp.constraint_lower_bounds()[e.
row]),
2259 constraint_upper_bound_(lp.constraint_upper_bounds()[e.
row]),
2260 constraint_status_(
status) {}
2268 SingletonRowUndo(saved_column, solution);
2271 ZeroCostSingletonColumnUndo(
parameters, saved_row, solution);
2274 SingletonColumnInEqualityUndo(
parameters, saved_row, solution);
2277 MakeConstraintAnEqualityUndo(solution);
2282 void SingletonPreprocessor::DeleteSingletonRow(MatrixEntry e,
2288 if (e.coeff < 0.0) {
2289 std::swap(implied_lower_bound, implied_upper_bound);
2296 std::abs(
parameters_.preprocessor_zero_tolerance() / e.coeff);
2298 implied_lower_bound - potential_error > old_lower_bound
2299 ? implied_lower_bound
2302 implied_upper_bound + potential_error < old_upper_bound
2303 ? implied_upper_bound
2308 VLOG(1) <<
"Problem ProblemStatus::PRIMAL_INFEASIBLE, singleton "
2309 "row causes the bound of the variable "
2310 << e.col <<
" to go to infinity.";
2315 if (new_upper_bound < new_lower_bound) {
2318 VLOG(1) <<
"Problem ProblemStatus::PRIMAL_INFEASIBLE, singleton "
2319 "row causes the bound of the variable "
2320 << e.col <<
" to be infeasible by "
2321 << new_lower_bound - new_upper_bound;
2328 new_upper_bound = new_lower_bound;
2331 new_lower_bound = new_upper_bound;
2341 new_upper_bound = new_lower_bound;
2352 void SingletonUndo::SingletonRowUndo(
const SparseColumn& saved_column,
2353 ProblemSolution* solution)
const {
2354 DCHECK_EQ(0, solution->dual_values[e_.row]);
2362 Fractional implied_lower_bound = constraint_lower_bound_ / e_.coeff;
2363 Fractional implied_upper_bound = constraint_upper_bound_ / e_.coeff;
2364 if (e_.coeff < 0.0) {
2365 std::swap(implied_lower_bound, implied_upper_bound);
2367 const bool lower_bound_changed = implied_lower_bound > variable_lower_bound_;
2368 const bool upper_bound_changed = implied_upper_bound < variable_upper_bound_;
2370 if (!lower_bound_changed && !upper_bound_changed)
return;
2378 const Fractional reduced_cost_for_minimization =
2379 is_maximization_ ? -reduced_cost : reduced_cost;
2382 DCHECK(lower_bound_changed || upper_bound_changed);
2383 if (reduced_cost_for_minimization >= 0.0 && !lower_bound_changed) {
2387 if (reduced_cost_for_minimization <= 0.0 && !upper_bound_changed) {
2405 solution->dual_values[e_.row] = reduced_cost / e_.coeff;
2408 (!lower_bound_changed || !upper_bound_changed)) {
2409 new_constraint_status = lower_bound_changed
2413 if (e_.coeff < 0.0) {
2421 solution->constraint_statuses[e_.row] = new_constraint_status;
2424 void SingletonPreprocessor::UpdateConstraintBoundsWithVariableBounds(
2425 MatrixEntry e, LinearProgram* lp) {
2426 Fractional lower_delta = -e.coeff * lp->variable_upper_bounds()[e.col];
2427 Fractional upper_delta = -e.coeff * lp->variable_lower_bounds()[e.col];
2428 if (e.coeff < 0.0) {
2431 lp->SetConstraintBounds(e.row,
2432 lp->constraint_lower_bounds()[e.row] + lower_delta,
2433 lp->constraint_upper_bounds()[e.row] + upper_delta);
2436 bool SingletonPreprocessor::IntegerSingletonColumnIsRemovable(
2437 const MatrixEntry& matrix_entry,
const LinearProgram& lp)
const {
2439 DCHECK(lp.IsVariableInteger(matrix_entry.col));
2440 const SparseMatrix& transpose = lp.GetTransposeSparseMatrix();
2452 coefficient_ratio,
parameters_.solution_feasibility_tolerance())) {
2457 lp.constraint_lower_bounds()[matrix_entry.row];
2459 const Fractional lower_bound_ratio = constraint_lb / matrix_entry.coeff;
2461 lower_bound_ratio,
parameters_.solution_feasibility_tolerance())) {
2466 lp.constraint_upper_bounds()[matrix_entry.row];
2468 const Fractional upper_bound_ratio = constraint_ub / matrix_entry.coeff;
2470 upper_bound_ratio,
parameters_.solution_feasibility_tolerance())) {
2477 void SingletonPreprocessor::DeleteZeroCostSingletonColumn(
2478 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2482 const SparseColumn& row_as_col = transpose.column(transpose_col);
2484 UpdateConstraintBoundsWithVariableBounds(e, lp);
2489 void SingletonUndo::ZeroCostSingletonColumnUndo(
2490 const GlopParameters&
parameters,
const SparseColumn& saved_row,
2491 ProblemSolution* solution)
const {
2496 if (variable_upper_bound_ == variable_lower_bound_) {
2497 solution->primal_values[e_.col] = variable_lower_bound_;
2504 const Fractional corrected_dual = is_maximization_
2505 ? -solution->dual_values[e_.row]
2506 : solution->dual_values[e_.row];
2507 if (corrected_dual > 0) {
2508 DCHECK(
IsFinite(variable_lower_bound_));
2509 solution->primal_values[e_.col] = variable_lower_bound_;
2512 DCHECK(
IsFinite(variable_upper_bound_));
2513 solution->primal_values[e_.col] = variable_upper_bound_;
2521 DCHECK(
IsFinite(variable_lower_bound_));
2522 solution->primal_values[e_.col] = variable_lower_bound_;
2525 DCHECK(
IsFinite(variable_upper_bound_));
2526 solution->primal_values[e_.col] = variable_upper_bound_;
2529 if (constraint_upper_bound_ == constraint_lower_bound_) {
2544 const auto is_smaller_with_tolerance = [tolerance](
Fractional a,
2548 if (variable_lower_bound_ != -
kInfinity) {
2550 activity + e_.coeff * variable_lower_bound_;
2551 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_lb) &&
2552 is_smaller_with_tolerance(activity_at_lb, constraint_upper_bound_)) {
2553 solution->primal_values[e_.col] = variable_lower_bound_;
2558 if (variable_upper_bound_ !=
kInfinity) {
2560 activity + e_.coeff * variable_upper_bound_;
2561 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_ub) &&
2562 is_smaller_with_tolerance(activity_at_ub, constraint_upper_bound_)) {
2563 solution->primal_values[e_.col] = variable_upper_bound_;
2572 if (constraint_lower_bound_ == -
kInfinity &&
2574 solution->primal_values[e_.col] = 0.0;
2582 if (constraint_lower_bound_ == constraint_upper_bound_) {
2583 solution->primal_values[e_.col] =
2584 (constraint_lower_bound_ - activity) / e_.coeff;
2589 bool set_constraint_to_lower_bound;
2590 if (constraint_lower_bound_ == -
kInfinity) {
2591 set_constraint_to_lower_bound =
false;
2592 }
else if (constraint_upper_bound_ ==
kInfinity) {
2593 set_constraint_to_lower_bound =
true;
2597 const Fractional to_lb = (constraint_lower_bound_ - activity) / e_.coeff;
2598 const Fractional to_ub = (constraint_upper_bound_ - activity) / e_.coeff;
2599 set_constraint_to_lower_bound =
2600 std::max(variable_lower_bound_ - to_lb, to_lb - variable_upper_bound_) <
2601 std::max(variable_lower_bound_ - to_ub, to_ub - variable_upper_bound_);
2604 if (set_constraint_to_lower_bound) {
2605 solution->primal_values[e_.col] =
2606 (constraint_lower_bound_ - activity) / e_.coeff;
2609 solution->primal_values[e_.col] =
2610 (constraint_upper_bound_ - activity) / e_.coeff;
2615 void SingletonPreprocessor::DeleteSingletonColumnInEquality(
2616 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2619 const SparseColumn& row_as_column = transpose.column(transpose_col);
2620 undo_stack_.push_back(
2630 const Fractional rhs = lp->constraint_upper_bounds()[e.row];
2633 lp->SetObjectiveOffset(lp->objective_offset() + rhs * multiplier);
2638 lp->objective_coefficients()[
col] - e.coefficient() * multiplier;
2645 if (std::abs(new_cost) <
parameters_.preprocessor_zero_tolerance()) {
2648 lp->SetObjectiveCoefficient(
col, new_cost);
2653 UpdateConstraintBoundsWithVariableBounds(e, lp);
2657 void SingletonUndo::SingletonColumnInEqualityUndo(
2658 const GlopParameters&
parameters,
const SparseColumn& saved_row,
2659 ProblemSolution* solution)
const {
2661 ZeroCostSingletonColumnUndo(
parameters, saved_row, solution);
2665 solution->dual_values[e_.row] += cost_ / e_.coeff;
2672 void SingletonUndo::MakeConstraintAnEqualityUndo(
2673 ProblemSolution* solution)
const {
2675 solution->constraint_statuses[e_.row] = constraint_status_;
2679 bool SingletonPreprocessor::MakeConstraintAnEqualityIfPossible(
2680 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2683 const Fractional cst_lower_bound = lp->constraint_lower_bounds()[e.row];
2684 const Fractional cst_upper_bound = lp->constraint_upper_bounds()[e.row];
2685 if (cst_lower_bound == cst_upper_bound)
return true;
2694 const DenseRow& variable_ubs = lp->variable_upper_bounds();
2695 const DenseRow& variable_lbs = lp->variable_lower_bounds();
2696 if (e.row >= row_sum_is_cached_.
size() || !row_sum_is_cached_[e.row]) {
2697 if (e.row >= row_sum_is_cached_.
size()) {
2698 const int new_size = e.row.value() + 1;
2699 row_sum_is_cached_.
resize(new_size);
2700 row_lb_sum_.resize(new_size);
2701 row_ub_sum_.resize(new_size);
2703 row_sum_is_cached_[e.row] =
true;
2704 row_lb_sum_[e.row].Add(cst_lower_bound);
2705 row_ub_sum_[e.row].Add(cst_upper_bound);
2716 if (column_deletion_helper_.
IsColumnMarked(row_as_col))
continue;
2717 if (entry.coefficient() > 0.0) {
2718 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2719 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2721 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2722 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2734 c > 0.0 ? row_lb_sum_[e.row].SumWithoutLb(-c * variable_ubs[e.col]) / c
2735 : row_ub_sum_[e.row].SumWithoutUb(-c * variable_ubs[e.col]) / c;
2737 c > 0.0 ? row_ub_sum_[e.row].SumWithoutUb(-c * variable_lbs[e.col]) / c
2738 : row_lb_sum_[e.row].SumWithoutLb(-c * variable_lbs[e.col]) / c;
2744 lp->GetObjectiveCoefficientForMinimizationVersion(e.col);
2745 DCHECK_NE(
cost, 0.0);
2751 ub, lp->variable_upper_bounds()[e.col])) {
2757 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2764 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2769 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2771 << e.col <<
" has a cost (for minimization) of " <<
cost
2772 <<
" and is unbounded towards kInfinity.";
2790 lp->SetVariableBounds(e.col, lp->variable_lower_bounds()[e.col],
kInfinity);
2793 lp->variable_lower_bounds()[e.col], lb)) {
2799 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2806 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2812 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2814 << e.col <<
" has a cost (for minimization) of " <<
cost
2815 <<
" and is unbounded towards -kInfinity.";
2820 lp->SetVariableBounds(e.col, -
kInfinity,
2821 lp->variable_upper_bounds()[e.col]);
2824 if (lp->constraint_lower_bounds()[e.row] ==
2825 lp->constraint_upper_bounds()[e.row]) {
2826 undo_stack_.push_back(SingletonUndo(
2840 ColIndex num_cols(matrix.
num_cols());
2841 RowIndex num_rows(matrix.
num_rows());
2843 std::vector<ColIndex> column_to_process;
2844 for (ColIndex
col(0);
col < num_cols; ++
col) {
2846 if (column_degree[
col] == 1) {
2847 column_to_process.push_back(
col);
2853 std::vector<RowIndex> row_to_process;
2854 for (RowIndex
row(0);
row < num_rows; ++
row) {
2856 if (row_degree[
row] == 1) {
2857 row_to_process.push_back(
row);
2863 (!column_to_process.empty() || !row_to_process.empty())) {
2865 const ColIndex
col = column_to_process.back();
2866 column_to_process.pop_back();
2867 if (column_degree[
col] <= 0)
continue;
2868 const MatrixEntry e = GetSingletonColumnMatrixEntry(
col, matrix);
2870 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2877 DeleteZeroCostSingletonColumn(transpose, e, lp);
2881 if (std::abs(e.coeff) <
parameters_.preprocessor_zero_tolerance()) {
2884 if (MakeConstraintAnEqualityIfPossible(transpose, e, lp)) {
2885 DeleteSingletonColumnInEquality(transpose, e, lp);
2890 --row_degree[e.row];
2891 if (row_degree[e.row] == 1) {
2896 const RowIndex
row = row_to_process.back();
2897 row_to_process.pop_back();
2898 if (row_degree[
row] <= 0)
continue;
2899 const MatrixEntry e = GetSingletonRowMatrixEntry(
row, transpose);
2905 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2909 DeleteSingletonRow(e, lp);
2910 --column_degree[e.col];
2911 if (column_degree[e.col] == 1) {
2920 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
2938 for (
int i = undo_stack_.size() - 1; i >= 0; --i) {
2943 undo_stack_[i].Undo(
parameters_, saved_col, saved_row, solution);
2947 MatrixEntry SingletonPreprocessor::GetSingletonColumnMatrixEntry(
2951 DCHECK_NE(0.0, e.coefficient());
2952 return MatrixEntry(e.row(),
col, e.coefficient());
2957 LOG(DFATAL) <<
"No unmarked entry in a column that is supposed to have one.";
2959 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2962 MatrixEntry SingletonPreprocessor::GetSingletonRowMatrixEntry(
2963 RowIndex
row,
const SparseMatrix& transpose) {
2967 DCHECK_NE(0.0, e.coefficient());
2968 return MatrixEntry(
row,
col, e.coefficient());
2973 LOG(DFATAL) <<
"No unmarked entry in a row that is supposed to have one.";
2975 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2986 if (num_cols == 0)
return false;
2992 Fractional num_non_zero_objective_coefficients = 0.0;
2993 for (ColIndex
col(0);
col < num_cols; ++
col) {
2995 row_degree[e.row()] += 1.0;
2998 num_non_zero_objective_coefficients += 1.0;
3010 const EntryIndex initial_num_entries = lp->
num_entries();
3011 int num_zeroed_objective_coefficients = 0;
3012 for (ColIndex
col(0);
col < num_cols; ++
col) {
3021 if (max_magnitude ==
kInfinity || max_magnitude == 0)
continue;
3022 const Fractional threshold = allowed_impact / max_magnitude;
3024 threshold, row_degree);
3027 num_non_zero_objective_coefficients *
3031 ++num_zeroed_objective_coefficients;
3038 <<
" near-zero entries.";
3040 if (num_zeroed_objective_coefficients > 0) {
3041 VLOG(1) <<
"Removed " << num_zeroed_objective_coefficients
3042 <<
" near-zero objective coefficients.";
3060 if (num_cols == 0)
return false;
3062 changed_columns_.clear();
3063 int num_singletons = 0;
3064 for (ColIndex
col(0);
col < num_cols; ++
col) {
3076 changed_columns_.push_back(
col);
3079 VLOG(1) <<
"Changed the sign of " << changed_columns_.size() <<
" columns.";
3080 VLOG(1) << num_singletons <<
" singleton columns left.";
3081 return !changed_columns_.empty();
3088 for (
int i = 0; i < changed_columns_.size(); ++i) {
3089 const ColIndex
col = changed_columns_[i];
3126 std::vector<std::pair<int64_t, RowIndex>> sorted_rows;
3128 for (RowIndex
row(0);
row < num_rows; ++
row) {
3141 sorted_rows.push_back({score,
row});
3143 std::sort(sorted_rows.begin(), sorted_rows.end());
3149 for (
const auto p : sorted_rows) {
3150 const RowIndex
row = p.second;
3160 int entry_index = 0;
3164 r.col[entry_index] =
col;
3165 r.coeff[entry_index] = e.coefficient();
3166 DCHECK_NE(0.0, r.coeff[entry_index]);
3173 if (entry_index < 2)
continue;
3179 for (
int col_choice = 0; col_choice < NUM_DOUBLETON_COLS; ++col_choice) {
3180 const ColIndex
col = r.col[col_choice];
3188 if (r.lb[DELETED] == r.ub[DELETED] || r.lb[MODIFIED] == r.ub[MODIFIED]) {
3205 const Fractional carry_over_offset = r.rhs / r.coeff[MODIFIED];
3207 -r.coeff[DELETED] / r.coeff[MODIFIED];
3209 carry_over_factor == 0.0) {
3217 r.lb[DELETED] * carry_over_factor + carry_over_offset;
3219 r.ub[DELETED] * carry_over_factor + carry_over_offset;
3220 if (carry_over_factor < 0) {
3221 std::swap(carried_over_lb, carried_over_ub);
3223 if (carried_over_lb <= lb) {
3228 lb = carried_over_lb;
3233 carry_over_factor > 0 ? r.lb[DELETED] : r.ub[DELETED]);
3235 if (carried_over_ub >= ub) {
3240 ub = carried_over_ub;
3245 carry_over_factor > 0 ? r.ub[DELETED] : r.lb[DELETED]);
3254 restore_stack_.push_back(r);
3261 DCHECK_NE(r.coeff[DELETED], 0.0);
3263 -r.coeff[MODIFIED] / r.coeff[DELETED];
3264 const Fractional constant_offset_factor = r.rhs / r.coeff[DELETED];
3266 if (!
IsFinite(substitution_factor) || substitution_factor == 0.0 ||
3267 !
IsFinite(constant_offset_factor)) {
3275 for (
const int col_choice : {DELETED, MODIFIED}) {
3276 const ColIndex
col = r.col[col_choice];
3282 substitution_factor, r.row,
parameters_.drop_tolerance(),
3290 r.objective_coefficient[MODIFIED] +
3291 substitution_factor * r.objective_coefficient[DELETED];
3292 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
3302 SubtractColumnMultipleFromConstraintBound(r.col[DELETED],
3303 constant_offset_factor, lp);
3319 return !column_deletion_helper_.
IsEmpty();
3332 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
3335 LOG(DFATAL) <<
"FIXED variable produced by DoubletonPreprocessor!";
3342 ABSL_FALLTHROUGH_INTENDED;
3347 new_basic_columns[r.col[DELETED]] =
true;
3350 ABSL_FALLTHROUGH_INTENDED;
3358 ? r.bound_backtracking_at_lower_bound
3359 : r.bound_backtracking_at_upper_bound;
3360 const ColIndex bounded_var = r.col[bound_backtracking.
col_choice];
3361 const ColIndex basic_var =
3362 r.col[OtherColChoice(bound_backtracking.
col_choice)];
3366 new_basic_columns[basic_var] =
true;
3377 solution->
primal_values[r.col[MODIFIED]] * r.coeff[MODIFIED]) /
3406 for (
int i = 0; i < restore_stack_.size(); ++i) {
3407 const RestoreInfo& r = restore_stack_[i];
3408 col_to_index[r.col[MODIFIED]].
insert(i);
3409 col_to_index[r.col[DELETED]].
insert(i);
3411 std::vector<ColIndex> singleton_col;
3412 for (ColIndex
col(0);
col < num_cols; ++
col) {
3413 if (!new_basic_columns[
col])
continue;
3414 if (col_to_index[
col].size() == 1) singleton_col.push_back(
col);
3416 while (!singleton_col.empty()) {
3417 const ColIndex
col = singleton_col.back();
3418 singleton_col.pop_back();
3419 if (!new_basic_columns[
col])
continue;
3420 if (col_to_index[
col].empty())
continue;
3421 CHECK_EQ(col_to_index[
col].size(), 1);
3423 const RestoreInfo& r = restore_stack_[
index];
3425 const ColChoice col_choice = r.col[MODIFIED] ==
col ? MODIFIED : DELETED;
3433 saved_objective_[r.col[col_choice]] -
3435 solution->
dual_values[r.row] = current_reduced_cost / r.coeff[col_choice];
3440 if (col_to_index[r.col[DELETED]].
size() == 1) {
3441 singleton_col.push_back(r.col[DELETED]);
3443 if (col_to_index[r.col[MODIFIED]].
size() == 1) {
3444 singleton_col.push_back(r.col[MODIFIED]);
3450 saved_row_upper_bounds_, solution);
3457 DCHECK_EQ(row_lower_bounds.
size(), num_rows);
3458 DCHECK_EQ(row_upper_bounds.
size(), num_rows);
3459 for (RowIndex
row(0);
row < num_rows; ++
row) {
3463 if (row_lower_bounds[
row] == row_upper_bounds[
row])
continue;
3475 void DoubletonEqualityRowPreprocessor::
3476 SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r) {
3478 swap(r->col[DELETED], r->col[MODIFIED]);
3479 swap(r->coeff[DELETED], r->coeff[MODIFIED]);
3480 swap(r->lb[DELETED], r->lb[MODIFIED]);
3481 swap(r->ub[DELETED], r->ub[MODIFIED]);
3482 swap(r->objective_coefficient[DELETED], r->objective_coefficient[MODIFIED]);
3492 if (
parameters_.solve_dual_problem() == GlopParameters::NEVER_DO) {
3519 if (
parameters_.solve_dual_problem() == GlopParameters::LET_SOLVER_DECIDE) {
3520 if (1.0 * primal_num_rows_.value() <
3521 parameters_.dualizer_threshold() * primal_num_cols_.value()) {
3531 variable_lower_bounds_.
assign(num_cols, 0.0);
3532 variable_upper_bounds_.
assign(num_cols, 0.0);
3533 for (ColIndex
col(0);
col < num_cols; ++
col) {
3538 variable_lower_bounds_[
col] =
lower;
3539 variable_upper_bounds_[
col] =
upper;
3543 SubtractColumnMultipleFromConstraintBound(
col,
value, lp);
3551 dual_status_correspondence_.
clear();
3552 for (RowIndex
row(0);
row < primal_num_rows_; ++
row) {
3562 LOG(DFATAL) <<
"There should be no free constraint in this lp.";
3565 slack_or_surplus_mapping_.
clear();
3566 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3576 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3609 DenseRow new_primal_values(primal_num_cols_, 0.0);
3613 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3631 new_variable_statuses[
col] = ComputeVariableStatus(shift,
lower,
upper);
3640 const ColIndex
end = dual_status_correspondence_.
size();
3642 DCHECK_EQ(
end - begin, slack_or_surplus_mapping_.
size());
3645 const ColIndex
col = slack_or_surplus_mapping_[
index - begin];
3653 new_primal_values[
col] = variable_upper_bounds_[
col];
3656 new_primal_values[
col] = variable_lower_bounds_[
col];
3664 DenseColumn new_dual_values(primal_num_rows_, 0.0);
3671 Fractional sign = primal_is_maximization_problem_ ? -1 : 1;
3672 for (RowIndex
row(0);
row < primal_num_rows_; ++
row) {
3690 new_constraint_statuses[
row] =
3697 new_dual_values[
row] +=
3703 DCHECK(new_dual_values[
row] == 0 ||
3743 bool all_variable_domains_contain_zero =
true;
3745 variable_initial_lbs_.
assign(num_cols, 0.0);
3746 variable_initial_ubs_.
assign(num_cols, 0.0);
3747 for (ColIndex
col(0);
col < num_cols; ++
col) {
3750 if (0.0 < variable_initial_lbs_[
col] || 0.0 > variable_initial_ubs_[
col]) {
3751 all_variable_domains_contain_zero =
false;
3754 VLOG(1) <<
"Maximum variable bounds magnitude (before shift): "
3755 << ComputeMaxVariableBoundsMagnitude(*lp);
3758 if (all_variable_domains_contain_zero)
return false;
3762 int num_bound_shifts = 0;
3766 offsets_.
assign(num_cols, 0.0);
3767 for (ColIndex
col(0);
col < num_cols; ++
col) {
3768 if (0.0 < variable_initial_lbs_[
col] || 0.0 > variable_initial_ubs_[
col]) {
3769 Fractional offset = MinInMagnitudeOrZeroIfInfinite(
3770 variable_initial_lbs_[
col], variable_initial_ubs_[
col]);
3778 offset = trunc(offset);
3780 DCHECK_NE(offset, 0.0);
3782 offsets_[
col] = offset;
3784 variable_initial_ubs_[
col] - offset);
3787 row_offsets[e.row()].Add(e.coefficient() * offset);
3793 VLOG(1) <<
"Maximum variable bounds magnitude (after " << num_bound_shifts
3794 <<
" shifts): " << ComputeMaxVariableBoundsMagnitude(*lp);
3797 for (RowIndex
row(0);
row < num_rows; ++
row) {
3798 if (!std::isfinite(row_offsets[
row].
Value())) {
3801 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3811 if (!std::isfinite(objective_offset.
Value())) {
3812 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3813 "for the objective.";
3826 for (ColIndex
col(0);
col < num_cols; ++
col) {
3832 ABSL_FALLTHROUGH_INTENDED;
3860 variable_lower_bounds_.
assign(num_cols, 0.0);
3861 variable_upper_bounds_.
assign(num_cols, 0.0);
3862 for (ColIndex
col(0);
col < num_cols; ++
col) {
3894 for (ColIndex
col(0);
col < num_cols; ++
col) {
3897 ABSL_FALLTHROUGH_INTENDED;
3905 ABSL_FALLTHROUGH_INTENDED;
3956 for (RowIndex
row(0);
row < num_rows; ++
row) {
3963 switch (variable_status) {
iterator erase(const_iterator pos)
iterator insert(const_iterator pos, const value_type &x)
void resize(size_type new_size)
void push_back(const value_type &x)
void swap(StrongVector &x)
void Add(const FpNumber &value)
void SetLogToStdOut(bool enable)
void EnableLogging(bool enable)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool IsColumnMarked(ColIndex col) const
void MarkColumnForDeletionWithState(ColIndex col, Fractional value, VariableStatus status)
void MarkColumnForDeletion(ColIndex col)
const DenseBooleanRow & GetMarkedColumns() const
void RestoreDeletedColumns(ProblemSolution *solution) const
const DenseRow & GetStoredValue() const
const SparseColumn & SavedOrEmptyColumn(ColIndex col) const
void SaveColumnIfNotAlreadyDone(ColIndex col, const SparseColumn &column)
void SaveColumn(ColIndex col, const SparseColumn &column)
const SparseColumn & SavedColumn(ColIndex col) const
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
ProblemStatus ChangeStatusToDualStatus(ProblemStatus status) const
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
SparseMatrix * GetMutableTransposeSparseMatrix()
void SetObjectiveScalingFactor(Fractional objective_scaling_factor)
DenseColumn * mutable_constraint_upper_bounds()
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
const SparseMatrix & GetTransposeSparseMatrix() const
void SetObjectiveOffset(Fractional objective_offset)
ColIndex GetFirstSlackVariable() const
const SparseMatrix & GetSparseMatrix() const
const DenseRow & variable_lower_bounds() const
const DenseColumn & constraint_lower_bounds() const
Fractional ScaleObjective(GlopParameters::CostScalingAlgorithm method)
const DenseRow & objective_coefficients() const
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
void Swap(LinearProgram *linear_program)
Fractional objective_offset() const
SparseColumn * GetMutableSparseColumn(ColIndex col)
void UseTransposeMatrixAsReference()
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
const DenseColumn & constraint_upper_bounds() const
bool IsVariableInteger(ColIndex col) const
void SetObjectiveCoefficient(ColIndex col, Fractional value)
void DeleteRows(const DenseBooleanColumn &rows_to_delete)
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
bool IsMaximizationProblem() const
const DenseRow & variable_upper_bounds() const
ColIndex num_variables() const
void PopulateFromDual(const LinearProgram &dual, RowToColMapping *duplicated_rows)
Fractional objective_scaling_factor() const
void SetMaximizationProblem(bool maximize)
const SparseColumn & GetSparseColumn(ColIndex col) const
EntryIndex num_entries() const
DenseColumn * mutable_constraint_lower_bounds()
RowIndex num_constraints() const
void RecoverSolution(ProblemSolution *solution) const override
bool Run(LinearProgram *lp) final
void DestructiveRecoverSolution(ProblemSolution *solution)
ProblemStatus status() const
bool IsSmallerWithinPreprocessorZeroTolerance(Fractional a, Fractional b) const
Preprocessor(const GlopParameters *parameters)
const GlopParameters & parameters_
bool IsSmallerWithinFeasibilityTolerance(Fractional a, Fractional b) const
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void MarkRowForDeletion(RowIndex row)
void UnmarkRow(RowIndex row)
void RestoreDeletedRows(ProblemSolution *solution) const
const DenseBooleanColumn & GetMarkedRows() const
bool IsRowMarked(RowIndex row) const
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void Undo(const GlopParameters ¶meters, const SparseColumn &saved_column, const SparseColumn &saved_row, ProblemSolution *solution) const
SingletonUndo(OperationType type, const LinearProgram &lp, MatrixEntry e, ConstraintStatus status)
@ ZERO_COST_SINGLETON_COLUMN
@ MAKE_CONSTRAINT_AN_EQUALITY
@ SINGLETON_COLUMN_IN_EQUALITY
SparseColumn * mutable_column(ColIndex col)
ColIndex num_cols() const
RowIndex num_rows() const
const SparseColumn & column(ColIndex col) const
void ScaleColumnVector(bool up, DenseColumn *column_vector) const
void ScaleRowVector(bool up, DenseRow *row_vector) const
Fractional LookUpCoefficient(Index index) const
void RemoveNearZeroEntriesWithWeights(Fractional threshold, const DenseVector &weights)
void AddMultipleToSparseVectorAndDeleteCommonIndex(Fractional multiplier, Index removed_common_index, Fractional drop_tolerance, SparseVector *accumulator_vector) const
void MultiplyByConstant(Fractional factor)
typename Iterator::Entry Entry
Fractional GetFirstCoefficient() const
EntryIndex num_entries() const
void resize(IntType size)
void assign(IntType size, const T &v)
Fractional SumWithoutUb(Fractional c) const
Fractional SumWithoutLb(Fractional c) const
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void RemoveZeroCostUnconstrainedVariable(ColIndex col, Fractional target_bound, LinearProgram *lp)
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
ModelSharedTimeLimit * time_limit
ReverseView< Container > reversed_view(const Container &c)
constexpr ColIndex kInvalidCol(-1)
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
StrictITIVector< ColIndex, Fractional > DenseRow
std::string GetProblemStatusString(ProblemStatus problem_status)
void FixConstraintWithFixedStatuses(const DenseColumn &row_lower_bounds, const DenseColumn &row_upper_bounds, ProblemSolution *solution)
constexpr double kInfinity
@ INFEASIBLE_OR_UNBOUNDED
ColIndex RowToColIndex(RowIndex row)
bool IsFinite(Fractional value)
constexpr RowIndex kInvalidRow(-1)
RowIndex ColToRowIndex(ColIndex col)
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
ColMapping FindProportionalColumns(const SparseMatrix &matrix, Fractional tolerance)
void Scale(LinearProgram *lp, SparseMatrixScaler *scaler)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
bool IsSmallerWithinTolerance(FloatType x, FloatType y, FloatType tolerance)
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
#define RUN_PREPROCESSOR(name)
glop::MainLpPreprocessor preprocessor
#define RETURN_IF_NULL(x)
#define RETURN_VALUE_IF_NULL(x, v)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
std::optional< int64_t > end
#define SCOPED_INSTRUCTION_COUNT(time_limit)
VariableStatusRow variable_statuses
ConstraintStatusColumn constraint_statuses
VectorXd variable_lower_bounds
VectorXd variable_upper_bounds
#define SOLVER_LOG(logger,...)
#define VLOG(verboselevel)