30#include "absl/strings/str_format.h"
31#include "absl/strings/string_view.h"
49 return absl::StrFormat(
"[%g, %g]", lb, ub);
53double trunc(
double d) {
return d > 0 ?
floor(d) : ceil(d); }
63 in_mip_context_(false),
64 infinite_time_limit_(
TimeLimit::Infinite()),
65 time_limit_(infinite_time_limit_.get()) {}
72#define RUN_PREPROCESSOR(name) \
73 RunAndPushIfRelevant(std::unique_ptr<Preprocessor>(new name(¶meters_)), \
74 #name, time_limit_, lp)
85 initial_num_rows_ = lp->num_constraints();
86 initial_num_cols_ = lp->num_variables();
87 initial_num_entries_ = lp->num_entries();
93 const int kMaxNumPasses = 20;
94 for (
int i = 0; i < kMaxNumPasses; ++i) {
95 const int old_stack_size = preprocessors_.size();
108 if (preprocessors_.size() == old_stack_size) {
110 SOLVER_LOG(logger_,
"Reached fixed point after presolve pass #", i);
124 const int old_stack_size = preprocessors_.size();
130 if (old_stack_size != preprocessors_.size()) {
144 return !preprocessors_.empty();
147#undef RUN_PREPROCESSOR
149void MainLpPreprocessor::RunAndPushIfRelevant(
156 const double start_time =
time_limit->GetElapsedTime();
161 if (lp->num_variables() == 0 && lp->num_constraints() == 0) {
167 const EntryIndex new_num_entries = lp->num_entries();
168 const double preprocess_time =
time_limit->GetElapsedTime() - start_time;
171 "%-45s: %d(%d) rows, %d(%d) columns, %d(%d) entries. (%fs)",
172 name, lp->num_constraints().value(),
173 (lp->num_constraints() - initial_num_rows_).value(),
174 lp->num_variables().value(),
175 (lp->num_variables() - initial_num_cols_).value(),
178 static_cast<int64_t
>(new_num_entries.value()),
179 static_cast<int64_t
>(new_num_entries.value() -
180 initial_num_entries_.value()),
205 while (!preprocessors_.empty()) {
206 preprocessors_.back()->RecoverSolution(
solution);
207 preprocessors_.pop_back();
216 const int index = saved_columns_.size();
217 CHECK(saved_columns_index_.insert({col, index}).second);
218 saved_columns_.push_back(
column);
223 const int index = saved_columns_.size();
224 const bool inserted = saved_columns_index_.insert({
col,
index}).second;
225 if (inserted) saved_columns_.push_back(
column);
229 const auto it = saved_columns_index_.find(
col);
230 CHECK(it != saved_columns_index_.end());
231 return saved_columns_[it->second];
235 const auto it = saved_columns_index_.find(
col);
236 return it == saved_columns_index_.end() ? empty_column_
237 : saved_columns_[it->second];
241 is_column_deleted_.clear();
242 stored_value_.clear();
252 if (
col >= is_column_deleted_.
size()) {
253 is_column_deleted_.
resize(
col + 1,
false);
257 is_column_deleted_[
col] =
true;
258 stored_value_[
col] = fixed_value;
266 ColIndex old_index(0);
267 for (ColIndex
col(0);
col < is_column_deleted_.
size(); ++
col) {
268 if (is_column_deleted_[
col]) {
269 new_primal_values.push_back(stored_value_[
col]);
270 new_variable_statuses.push_back(stored_status_[
col]);
272 new_primal_values.push_back(
solution->primal_values[old_index]);
273 new_variable_statuses.push_back(
solution->variable_statuses[old_index]);
279 const ColIndex num_cols =
solution->primal_values.size();
280 DCHECK_EQ(num_cols,
solution->variable_statuses.size());
281 for (; old_index < num_cols; ++old_index) {
282 new_primal_values.push_back(
solution->primal_values[old_index]);
283 new_variable_statuses.push_back(
solution->variable_statuses[old_index]);
285 new_primal_values.swap(
solution->primal_values);
286 new_variable_statuses.swap(
solution->variable_statuses);
297 if (
row >= is_row_deleted_.
size()) {
300 is_row_deleted_[
row] =
true;
304 if (
row >= is_row_deleted_.
size())
return;
305 is_row_deleted_[
row] =
false;
309 return is_row_deleted_;
315 RowIndex old_index(0);
318 if (is_row_deleted_[
row]) {
324 solution->constraint_statuses[old_index]);
330 const RowIndex num_rows =
solution->dual_values.size();
331 DCHECK_EQ(num_rows,
solution->constraint_statuses.size());
332 for (; old_index < num_rows; ++old_index) {
337 new_constraint_statuses.
swap(
solution->constraint_statuses);
383Fractional ComputeMaxVariableBoundsMagnitude(
const LinearProgram& lp) {
385 const ColIndex num_cols = lp.num_variables();
386 for (ColIndex
col(0);
col < num_cols; ++
col) {
387 max_bounds_magnitude = std::max(
388 max_bounds_magnitude,
389 std::max(MagnitudeOrZeroIfInfinite(lp.variable_lower_bounds()[
col]),
390 MagnitudeOrZeroIfInfinite(lp.variable_upper_bounds()[
col])));
392 return max_bounds_magnitude;
400 column_deletion_helper_.
Clear();
401 const ColIndex num_cols = lp->num_variables();
402 for (ColIndex
col(0);
col < num_cols; ++
col) {
403 if (lp->GetSparseColumn(
col).IsEmpty()) {
407 lp->GetObjectiveCoefficientForMinimizationVersion(
col);
409 if (objective_coefficient == 0) {
423 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, empty column " <<
col
424 <<
" has a minimization cost of " << objective_coefficient
430 lp->SetObjectiveOffset(lp->objective_offset() +
431 value * lp->objective_coefficients()[
col]);
438 return !column_deletion_helper_.
IsEmpty();
456void SubtractColumnMultipleFromConstraintBound(ColIndex
col,
462 const RowIndex
row = e.row();
476struct ColumnWithRepresentativeAndScaledCost {
477 ColumnWithRepresentativeAndScaledCost(ColIndex _col, ColIndex _representative,
484 bool operator<(
const ColumnWithRepresentativeAndScaledCost& other)
const {
487 return col < other.col;
501 lp->GetSparseMatrix(),
parameters_.preprocessor_zero_tolerance());
508 int num_proportionality_classes = 0;
509 std::vector<ColIndex> proportional_columns;
510 for (ColIndex
col(0);
col < mapping.size(); ++
col) {
515 ++num_proportionality_classes;
518 proportional_columns.push_back(
col);
521 if (proportional_columns.empty())
return false;
522 VLOG(1) <<
"The problem contains " << proportional_columns.size()
523 <<
" columns which belong to " << num_proportionality_classes
524 <<
" proportionality classes.";
527 const ColIndex num_cols = lp->num_variables();
528 column_factors_.
assign(num_cols, 0.0);
529 for (
const ColIndex
col : proportional_columns) {
531 column_factors_[
col] =
column.GetFirstCoefficient();
541 DenseRow slope_lower_bound(num_cols, -kInfinity);
542 DenseRow slope_upper_bound(num_cols, +kInfinity);
543 for (
const ColIndex
col : proportional_columns) {
547 const bool is_rc_positive_or_zero =
548 (lp->variable_upper_bounds()[
col] == kInfinity);
549 const bool is_rc_negative_or_zero =
550 (lp->variable_lower_bounds()[
col] == -kInfinity);
551 bool is_slope_upper_bounded = is_rc_positive_or_zero;
552 bool is_slope_lower_bounded = is_rc_negative_or_zero;
553 if (column_factors_[
col] < 0.0) {
554 std::swap(is_slope_lower_bounded, is_slope_upper_bounded);
557 lp->GetObjectiveCoefficientForMinimizationVersion(
col) /
558 column_factors_[
col];
559 if (is_slope_lower_bounded) {
563 if (is_slope_upper_bounded) {
570 for (
const ColIndex
col : proportional_columns) {
578 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, no feasible dual values"
579 <<
" can satisfy the constraints of the proportional columns"
581 <<
" the associated quantity must be in ["
591 for (
const ColIndex
col : proportional_columns) {
594 lp->GetObjectiveCoefficientForMinimizationVersion(
col) /
595 column_factors_[
col];
598 bool variable_can_be_fixed =
false;
606 variable_can_be_fixed =
true;
611 variable_can_be_fixed =
true;
615 if (variable_can_be_fixed) {
620 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED.";
633 std::vector<ColumnWithRepresentativeAndScaledCost> sorted_columns;
634 for (
const ColIndex
col : proportional_columns) {
639 sorted_columns.push_back(ColumnWithRepresentativeAndScaledCost(
641 lp->objective_coefficients()[
col] / column_factors_[
col]));
644 std::sort(sorted_columns.begin(), sorted_columns.end());
648 lower_bounds_.
assign(num_cols, -kInfinity);
649 upper_bounds_.
assign(num_cols, kInfinity);
650 new_lower_bounds_.
assign(num_cols, -kInfinity);
651 new_upper_bounds_.
assign(num_cols, kInfinity);
653 for (
int i = 0;
i < sorted_columns.size();) {
654 const ColIndex target_col = sorted_columns[
i].col;
655 const ColIndex target_representative = sorted_columns[
i].representative;
656 const Fractional target_scaled_cost = sorted_columns[
i].scaled_cost;
659 lower_bounds_[target_col] = lp->variable_lower_bounds()[target_col];
660 upper_bounds_[target_col] = lp->variable_upper_bounds()[target_col];
663 for (++i;
i < sorted_columns.size(); ++
i) {
664 if (sorted_columns[i].
representative != target_representative)
break;
665 if (std::abs(sorted_columns[i].
scaled_cost - target_scaled_cost) >=
670 const ColIndex
col = sorted_columns[
i].col;
675 merged_columns_[
col] = target_col;
680 column_factors_[
col] / column_factors_[target_col];
694 if (bound_factor < 0.0) {
695 std::swap(lower_diff, upper_diff);
697 lp->SetVariableBounds(
698 target_col, lp->variable_lower_bounds()[target_col] + lower_diff,
699 lp->variable_upper_bounds()[target_col] + upper_diff);
700 SubtractColumnMultipleFromConstraintBound(
col, target_value, lp);
709 if (num_merged > 0) {
710 merged_columns_[target_col] = target_col;
711 const Fractional target_value = MinInMagnitudeOrZeroIfInfinite(
712 lower_bounds_[target_col], upper_bounds_[target_col]);
713 lp->SetVariableBounds(
714 target_col, lp->variable_lower_bounds()[target_col] - target_value,
715 lp->variable_upper_bounds()[target_col] - target_value);
716 SubtractColumnMultipleFromConstraintBound(target_col, target_value, lp);
717 new_lower_bounds_[target_col] = lp->variable_lower_bounds()[target_col];
718 new_upper_bounds_[target_col] = lp->variable_upper_bounds()[target_col];
723 return !column_deletion_helper_.
IsEmpty();
734 const ColIndex num_cols = merged_columns_.
size();
737 DenseRow distance_to_bound(num_cols, 0.0);
742 for (ColIndex
col(0);
col < num_cols; ++
col) {
743 if (merged_columns_[
col] ==
col) {
747 if (distance_to_upper_bound < distance_to_lower_bound) {
748 distance_to_bound[
col] = distance_to_upper_bound;
749 is_distance_to_upper_bound[
col] =
true;
751 distance_to_bound[
col] = distance_to_lower_bound;
752 is_distance_to_upper_bound[
col] =
false;
754 is_representative_basic[
col] =
760 solution->primal_values[
col] = MinInMagnitudeOrZeroIfInfinite(
761 lower_bounds_[
col], upper_bounds_[
col]);
762 solution->variable_statuses[
col] = ComputeVariableStatus(
768 for (ColIndex
col(0);
col < num_cols; ++
col) {
780 const bool to_upper_bound =
781 (bound_factor > 0.0) == is_distance_to_upper_bound[
representative];
782 if (
width <= scaled_distance) {
784 to_upper_bound ? lower_bounds_[
col] : upper_bounds_[
col];
786 ComputeVariableStatus(
solution->primal_values[
col],
787 lower_bounds_[
col], upper_bounds_[
col]);
791 to_upper_bound ? upper_bounds_[
col] - scaled_distance
792 : lower_bounds_[
col] + scaled_distance;
796 : ComputeVariableStatus(
solution->primal_values[
col],
815 const bool use_this_variable =
816 (error * bound_factor > 0.0) ? (upper_bounds_[
col] == kInfinity)
818 if (use_this_variable) {
820 solution->primal_values[
col] += error / bound_factor;
844 const RowIndex num_rows = lp->num_constraints();
845 const SparseMatrix& transpose = lp->GetTransposeSparseMatrix();
851 row_factors_.
assign(num_rows, 0.0);
852 for (RowIndex
row(0);
row < num_rows; ++
row) {
854 if (!row_transpose.IsEmpty()) {
855 row_factors_[
row] = row_transpose.GetFirstCoefficient();
873 transpose,
parameters_.preprocessor_zero_tolerance());
875 int num_proportional_rows = 0;
876 for (RowIndex
row(0);
row < num_rows; ++
row) {
879 mapping[representative_row_as_col] = representative_row_as_col;
880 is_a_representative[
ColToRowIndex(representative_row_as_col)] =
true;
881 ++num_proportional_rows;
887 for (RowIndex
row(0);
row < num_rows; ++
row) {
893 const RowIndex representative_row =
ColToRowIndex(mapping[row_as_col]);
896 row_factors_[representative_row] / row_factors_[
row];
897 Fractional implied_lb = factor * lp->constraint_lower_bounds()[
row];
898 Fractional implied_ub = factor * lp->constraint_upper_bounds()[
row];
900 std::swap(implied_lb, implied_ub);
906 lower_bound_sources_[representative_row] =
row;
910 upper_bound_sources_[representative_row] =
row;
918 for (RowIndex
row(0);
row < num_rows; ++
row) {
919 if (!is_a_representative[
row])
continue;
920 const RowIndex lower_source = lower_bound_sources_[
row];
921 const RowIndex upper_source = upper_bound_sources_[
row];
926 if (lower_source == upper_source) {
930 row_deletion_helper_.
UnmarkRow(lower_source);
941 if (lp->constraint_lower_bounds()[lower_source] ==
942 lp->constraint_upper_bounds()[lower_source]) {
943 row_deletion_helper_.
UnmarkRow(lower_source);
946 if (lp->constraint_lower_bounds()[upper_source] ==
947 lp->constraint_upper_bounds()[upper_source]) {
948 row_deletion_helper_.
UnmarkRow(upper_source);
956 RowIndex new_representative = lower_source;
957 RowIndex other = upper_source;
958 if (std::abs(row_factors_[new_representative]) <
959 std::abs(row_factors_[other])) {
960 std::swap(new_representative, other);
965 row_factors_[new_representative] / row_factors_[other];
966 Fractional new_lb = factor * lp->constraint_lower_bounds()[other];
967 Fractional new_ub = factor * lp->constraint_upper_bounds()[other];
969 std::swap(new_lb, new_ub);
972 lower_bound_sources_[new_representative] = new_representative;
973 upper_bound_sources_[new_representative] = new_representative;
975 if (new_lb > lp->constraint_lower_bounds()[new_representative]) {
976 lower_bound_sources_[new_representative] = other;
978 new_lb = lp->constraint_lower_bounds()[new_representative];
980 if (new_ub < lp->constraint_upper_bounds()[new_representative]) {
981 upper_bound_sources_[new_representative] = other;
983 new_ub = lp->constraint_upper_bounds()[new_representative];
985 const RowIndex new_lower_source =
986 lower_bound_sources_[new_representative];
987 if (new_lower_source == upper_bound_sources_[new_representative]) {
988 row_deletion_helper_.
UnmarkRow(new_lower_source);
989 lower_bound_sources_[new_representative] =
kInvalidRow;
990 upper_bound_sources_[new_representative] =
kInvalidRow;
999 if (new_lb > new_ub) {
1000 if (lower_bound_sources_[new_representative] == new_representative) {
1001 new_ub = lp->constraint_lower_bounds()[new_representative];
1003 new_lb = lp->constraint_upper_bounds()[new_representative];
1006 row_deletion_helper_.
UnmarkRow(new_representative);
1007 lp->SetConstraintBounds(new_representative, new_lb, new_ub);
1011 lp_is_maximization_problem_ = lp->IsMaximizationProblem();
1013 return !row_deletion_helper_.
IsEmpty();
1025 const RowIndex num_rows =
solution->dual_values.size();
1026 for (RowIndex
row(0);
row < num_rows; ++
row) {
1027 const RowIndex lower_source = lower_bound_sources_[
row];
1028 const RowIndex upper_source = upper_bound_sources_[
row];
1030 DCHECK_NE(lower_source, upper_source);
1031 DCHECK(lower_source ==
row || upper_source ==
row);
1043 const Fractional corrected_dual_value = lp_is_maximization_problem_
1046 if (corrected_dual_value != 0.0) {
1056 DCHECK_EQ(0.0,
solution->dual_values[lower_source]);
1057 const Fractional factor = row_factors_[
row] / row_factors_[lower_source];
1061 solution->constraint_statuses[lower_source] =
1066 DCHECK_EQ(0.0,
solution->dual_values[upper_source]);
1067 const Fractional factor = row_factors_[
row] / row_factors_[upper_source];
1071 solution->constraint_statuses[upper_source] =
1093 const ColIndex num_cols = lp->num_variables();
1094 for (ColIndex
col(0);
col < num_cols; ++
col) {
1102 SubtractColumnMultipleFromConstraintBound(
col, fixed_value, lp);
1109 return !column_deletion_helper_.
IsEmpty();
1126 const RowIndex num_rows = lp->num_constraints();
1131 const ColIndex num_cols = lp->num_variables();
1133 for (ColIndex
col(0);
col < num_cols; ++
col) {
1136 for (
const SparseColumn::Entry e : lp->GetSparseColumn(
col)) {
1137 const RowIndex
row = e.row();
1140 implied_lower_bounds[
row] +=
lower * coeff;
1143 implied_lower_bounds[
row] +=
upper * coeff;
1144 implied_upper_bounds[
row] +=
lower * coeff;
1152 int num_implied_free_constraints = 0;
1153 int num_forcing_constraints = 0;
1154 is_forcing_up_.
assign(num_rows,
false);
1156 for (RowIndex
row(0);
row < num_rows; ++
row) {
1157 if (row_degree[
row] == 0)
continue;
1163 implied_upper_bounds[
row]) ||
1166 VLOG(1) <<
"implied bound " << implied_lower_bounds[
row] <<
" "
1167 << implied_upper_bounds[
row];
1168 VLOG(1) <<
"constraint bound " <<
lower <<
" " <<
upper;
1177 is_forcing_down[
row] =
true;
1178 ++num_forcing_constraints;
1182 implied_lower_bounds[
row])) {
1183 is_forcing_up_[
row] =
true;
1184 ++num_forcing_constraints;
1195 implied_lower_bounds[
row]) &&
1198 lp->SetConstraintBounds(
row, -kInfinity, kInfinity);
1199 ++num_implied_free_constraints;
1203 if (num_implied_free_constraints > 0) {
1204 VLOG(1) << num_implied_free_constraints <<
" implied free constraints.";
1207 if (num_forcing_constraints > 0) {
1208 VLOG(1) << num_forcing_constraints <<
" forcing constraints.";
1209 lp_is_maximization_problem_ = lp->IsMaximizationProblem();
1210 costs_.
resize(num_cols, 0.0);
1211 for (ColIndex
col(0);
col < num_cols; ++
col) {
1215 bool is_forced =
false;
1217 for (
const SparseColumn::Entry e :
column) {
1218 if (is_forcing_down[e.row()]) {
1231 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1232 << std::fixed << std::setprecision(10) <<
lower <<
", "
1233 <<
upper <<
"]. coeff:" << e.coefficient();
1240 if (is_forcing_up_[e.row()]) {
1249 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1250 << std::fixed << std::setprecision(10) <<
lower <<
", "
1251 <<
upper <<
"]. coeff:" << e.coefficient();
1267 costs_[
col] = lp->objective_coefficients()[
col];
1270 for (RowIndex
row(0);
row < num_rows; ++
row) {
1277 if (is_forcing_down[
row] || is_forcing_up_[
row]) {
1285 return !column_deletion_helper_.
IsEmpty();
1295 struct DeletionEntry {
1300 std::vector<DeletionEntry> entries;
1309 for (
const SparseColumn::Entry e : columns_saver_.
SavedColumn(
col)) {
1310 const RowIndex
row = e.row();
1313 last_coefficient = e.coefficient();
1317 entries.push_back({last_row,
col, last_coefficient});
1322 std::sort(entries.begin(), entries.end(),
1323 [](
const DeletionEntry&
a,
const DeletionEntry&
b) {
1324 if (a.row == b.row) return a.col < b.col;
1325 return a.row < b.row;
1338 for (
int i = 0;
i < entries.size();) {
1339 const RowIndex
row = entries[
i].row;
1345 for (;
i < entries.size(); ++
i) {
1346 if (entries[i].
row !=
row)
break;
1347 const ColIndex
col = entries[
i].col;
1351 const Fractional reduced_cost = costs_[
col] - scalar_product;
1353 if (is_forcing_up_[
row] == !lp_is_maximization_problem_) {
1354 if (
bound < new_dual_value) {
1355 new_dual_value =
bound;
1356 new_basic_column =
col;
1359 if (
bound > new_dual_value) {
1360 new_dual_value =
bound;
1361 new_basic_column =
col;
1382 if (!
parameters_.use_implied_free_preprocessor())
return false;
1383 const RowIndex num_rows = lp->num_constraints();
1384 const ColIndex num_cols = lp->num_variables();
1390 const int size = num_rows.value();
1402 for (
const SparseColumn::Entry e : lp->GetSparseColumn(
col)) {
1405 if (e.coefficient() < 0.0) std::swap(entry_lb, entry_ub);
1406 lb_sums[e.row()].Add(entry_lb);
1407 ub_sums[e.row()].Add(entry_ub);
1417 for (RowIndex
row(0);
row < num_rows; ++
row) {
1418 lb_sums[
row].Add(-lp->constraint_upper_bounds()[
row]);
1419 ub_sums[
row].Add(-lp->constraint_lower_bounds()[
row]);
1426 variable_offsets_.
assign(num_cols, 0.0);
1443 std::vector<std::pair<EntryIndex, ColIndex>> col_by_degree;
1444 col_by_degree.reserve(num_cols.value());
1445 for (ColIndex
col(0);
col < num_cols; ++
col) {
1446 col_by_degree.push_back({lp->GetSparseColumn(
col).num_entries(),
col});
1448 std::sort(col_by_degree.begin(), col_by_degree.end());
1451 int num_already_free_variables = 0;
1452 int num_implied_free_variables = 0;
1453 int num_fixed_variables = 0;
1454 for (
const auto [_,
col] : col_by_degree) {
1459 ++num_already_free_variables;
1467 for (
const SparseColumn::Entry e : lp->GetSparseColumn(
col)) {
1470 if (used_rows[e.row()])
continue;
1476 if (coeff < 0.0) std::swap(entry_lb, entry_ub);
1487 coeff > 0.0 ? -ub_sums[e.row()].SumWithoutUb(entry_ub) / coeff
1488 : -lb_sums[e.row()].SumWithoutLb(entry_lb) / coeff;
1490 coeff > 0.0 ? -lb_sums[e.row()].SumWithoutLb(entry_lb) / coeff
1491 : -ub_sums[e.row()].SumWithoutUb(entry_ub) / coeff;
1493 overall_implied_lb = std::max(overall_implied_lb, implied_lb);
1494 overall_implied_ub = std::min(overall_implied_ub, implied_ub);
1501 overall_implied_ub)) {
1509 overall_implied_lb) ||
1515 ++num_fixed_variables;
1518 overall_implied_lb)) {
1524 ++num_fixed_variables;
1531 overall_implied_lb) &&
1534 ++num_implied_free_variables;
1535 lp->SetVariableBounds(
col, -kInfinity, kInfinity);
1536 for (
const SparseColumn::Entry e : lp->GetSparseColumn(
col)) {
1537 used_rows[e.row()] =
true;
1561 if (offset != 0.0) {
1562 variable_offsets_[
col] = offset;
1563 SubtractColumnMultipleFromConstraintBound(
col, offset, lp);
1565 postsolve_status_of_free_variables_[
col] =
1569 VLOG(1) << num_already_free_variables <<
" free variables in the problem.";
1570 VLOG(1) << num_implied_free_variables <<
" implied free columns.";
1571 VLOG(1) << num_fixed_variables <<
" variables can be fixed.";
1573 return num_implied_free_variables > 0;
1579 const ColIndex num_cols =
solution->variable_statuses.size();
1580 for (ColIndex
col(0);
col < num_cols; ++
col) {
1583 DCHECK_EQ(0.0, variable_offsets_[
col]);
1588 postsolve_status_of_free_variables_[
col];
1610 for (ColIndex doubleton_col(0); doubleton_col < num_cols; ++doubleton_col) {
1619 r.col = doubleton_col;
1622 for (
const SparseColumn::Entry e : original_matrix.
column(r.col)) {
1623 if (row_deletion_helper_.
IsRowMarked(e.row()))
break;
1624 r.row[
index] = e.row();
1625 r.coeff[
index] = e.coefficient();
1626 DCHECK_NE(0.0, e.coefficient());
1629 if (
index != NUM_ROWS)
continue;
1635 DCHECK_EQ(r.coeff[MODIFIED],
1641 if (std::abs(r.coeff[DELETED]) < std::abs(r.coeff[MODIFIED])) {
1642 std::swap(r.coeff[DELETED], r.coeff[MODIFIED]);
1643 std::swap(r.row[DELETED], r.row[MODIFIED]);
1650 r.deleted_row_as_column.Swap(
1659 new_variable_lb /= r.coeff[DELETED];
1660 new_variable_ub /= r.coeff[DELETED];
1661 if (r.coeff[DELETED] < 0.0) std::swap(new_variable_lb, new_variable_ub);
1667 r.deleted_row_as_column.AddMultipleToSparseVectorAndIgnoreCommonIndex(
1668 -r.coeff[MODIFIED] / r.coeff[DELETED],
ColToRowIndex(r.col),
1674 if (r.objective_coefficient != 0.0) {
1675 for (
const SparseColumn::Entry e : r.deleted_row_as_column) {
1677 if (
col == r.col)
continue;
1680 e.coefficient() * r.objective_coefficient / r.coeff[DELETED];
1686 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
1694 restore_stack_.push_back(r);
1697 if (!row_deletion_helper_.
IsEmpty()) {
1710 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
1712 switch (
solution->variable_statuses[r.col]) {
1714 solution->constraint_statuses[r.row[DELETED]] =
1718 solution->constraint_statuses[r.row[DELETED]] =
1723 solution->constraint_statuses[r.row[DELETED]] =
1732 DCHECK_EQ(
solution->constraint_statuses[r.row[DELETED]],
1740 for (
const SparseColumn::Entry e : r.deleted_row_as_column) {
1742 if (
col == r.col)
continue;
1743 new_variable_value -= (e.coefficient() / r.coeff[DELETED]) *
1746 solution->primal_values[r.col] = new_variable_value;
1756 r.objective_coefficient -
1757 r.coeff[MODIFIED] *
solution->dual_values[r.row[MODIFIED]];
1759 solution->dual_values[r.row[DELETED]] =
1760 current_reduced_cost / r.coeff[DELETED];
1762 DCHECK_EQ(
solution->dual_values[r.row[DELETED]], 0.0);
1776bool IsConstraintBlockingVariable(
const LinearProgram& lp,
Fractional direction,
1778 return direction > 0.0 ? lp.constraint_upper_bounds()[
row] !=
kInfinity
1786 DCHECK_EQ(0.0, lp->objective_coefficients()[
col]);
1788 rhs_.
resize(lp->num_constraints(), 0.0);
1789 activity_sign_correction_.
resize(lp->num_constraints(), 1.0);
1790 is_unbounded_.
resize(lp->num_variables(),
false);
1794 for (
const SparseColumn::Entry e :
column) {
1795 const RowIndex
row = e.row();
1802 const bool is_constraint_upper_bound_relevant =
1803 e.coefficient() > 0.0 ? !is_unbounded_up : is_unbounded_up;
1804 activity_sign_correction_[
row] =
1805 is_constraint_upper_bound_relevant ? 1.0 : -1.0;
1806 rhs_[
row] = is_constraint_upper_bound_relevant
1807 ? lp->constraint_upper_bounds()[
row]
1808 : lp->constraint_lower_bounds()[
row];
1815 is_unbounded_[
col] =
true;
1816 Fractional initial_feasible_value = MinInMagnitudeOrZeroIfInfinite(
1817 lp->variable_lower_bounds()[
col], lp->variable_upper_bounds()[
col]);
1819 col, initial_feasible_value,
1820 ComputeVariableStatus(initial_feasible_value,
1821 lp->variable_lower_bounds()[
col],
1822 lp->variable_upper_bounds()[
col]));
1840 const RowIndex num_rows = lp->num_constraints();
1841 dual_lb_.
assign(num_rows, -kInfinity);
1842 dual_ub_.
assign(num_rows, kInfinity);
1843 for (RowIndex
row(0);
row < num_rows; ++
row) {
1844 if (lp->constraint_lower_bounds()[
row] == -kInfinity) {
1845 dual_ub_[
row] = 0.0;
1847 if (lp->constraint_upper_bounds()[
row] == kInfinity) {
1848 dual_lb_[
row] = 0.0;
1852 const ColIndex num_cols = lp->num_variables();
1853 may_have_participated_lb_.
assign(num_cols,
false);
1854 may_have_participated_ub_.
assign(num_cols,
false);
1857 std::deque<ColIndex> columns_to_process;
1859 std::vector<RowIndex> changed_rows;
1860 for (ColIndex
col(0);
col < num_cols; ++
col) {
1861 columns_to_process.push_back(
col);
1867 const int limit = 5 * num_cols.value();
1868 for (
int count = 0; !columns_to_process.empty() && count < limit; ++count) {
1869 const ColIndex
col = columns_to_process.front();
1870 columns_to_process.pop_front();
1871 in_columns_to_process[
col] =
false;
1876 lp->GetObjectiveCoefficientForMinimizationVersion(
col);
1883 rc_lb.Add(col_cost);
1884 rc_ub.Add(col_cost);
1885 for (
const SparseColumn::Entry e :
column) {
1886 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
1889 rc_lb.Add(-coeff * dual_ub_[e.row()]);
1890 rc_ub.Add(-coeff * dual_lb_[e.row()]);
1892 rc_lb.Add(-coeff * dual_lb_[e.row()]);
1893 rc_ub.Add(-coeff * dual_ub_[e.row()]);
1901 bool can_be_removed =
false;
1903 bool rc_is_away_from_zero;
1904 if (rc_ub.Sum() <= low_tolerance) {
1905 can_be_removed =
true;
1911 rc_is_away_from_zero = rc_ub.Sum() <= -high_tolerance;
1912 can_be_removed = !may_have_participated_ub_[
col];
1914 if (rc_lb.Sum() >= -low_tolerance) {
1918 can_be_removed =
true;
1924 rc_is_away_from_zero = rc_lb.Sum() >= high_tolerance;
1925 can_be_removed = !may_have_participated_lb_[
col];
1929 if (can_be_removed) {
1941 if (rc_is_away_from_zero) {
1942 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, variable " <<
col
1944 <<
" and its reduced cost is in [" << rc_lb.Sum() <<
", "
1945 << rc_ub.Sum() <<
"]";
1958 if (col_cost != 0.0)
continue;
1962 for (
const SparseColumn::Entry e :
column) {
1965 if (IsConstraintBlockingVariable(
1966 *lp, sign_correction * e.coefficient(), e.row())) {
1984 DCHECK(!can_be_removed);
1985 if (col_lb != -kInfinity && col_ub != kInfinity)
continue;
1991 changed_rows.clear();
1992 for (
const SparseColumn::Entry e :
column) {
1993 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
1995 const RowIndex
row = e.row();
1996 if (col_ub == kInfinity) {
1999 rc_ub.SumWithoutUb(-c * dual_lb_[
row]) /
c;
2000 if (candidate < dual_ub_[
row]) {
2001 dual_ub_[
row] = candidate;
2002 may_have_participated_lb_[
col] =
true;
2003 changed_rows.push_back(
row);
2007 rc_ub.SumWithoutUb(-c * dual_ub_[
row]) /
c;
2008 if (candidate > dual_lb_[
row]) {
2009 dual_lb_[
row] = candidate;
2010 may_have_participated_lb_[
col] =
true;
2011 changed_rows.push_back(
row);
2015 if (col_lb == -kInfinity) {
2018 rc_lb.SumWithoutLb(-c * dual_ub_[
row]) /
c;
2019 if (candidate > dual_lb_[
row]) {
2020 dual_lb_[
row] = candidate;
2021 may_have_participated_ub_[
col] =
true;
2022 changed_rows.push_back(
row);
2026 rc_lb.SumWithoutLb(-c * dual_lb_[
row]) /
c;
2027 if (candidate < dual_ub_[
row]) {
2028 dual_ub_[
row] = candidate;
2029 may_have_participated_ub_[
col] =
true;
2030 changed_rows.push_back(
row);
2036 if (!changed_rows.empty()) {
2037 const SparseMatrix& transpose = lp->GetTransposeSparseMatrix();
2038 for (
const RowIndex
row : changed_rows) {
2039 for (
const SparseColumn::Entry entry :
2042 if (!in_columns_to_process[
col]) {
2043 columns_to_process.push_back(
col);
2044 in_columns_to_process[
col] =
true;
2066 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
2076 struct DeletionEntry {
2081 std::vector<DeletionEntry> entries;
2084 const RowIndex num_rows =
solution->dual_values.size();
2086 for (RowIndex
row(0);
row < num_rows; ++
row) {
2091 for (
const SparseColumn::Entry e :
2094 if (is_unbounded_[
col]) {
2096 last_coefficient = e.coefficient();
2100 entries.push_back({
row, last_col, last_coefficient});
2105 std::sort(entries.begin(), entries.end(),
2106 [](
const DeletionEntry&
a,
const DeletionEntry&
b) {
2107 if (a.col == b.col) return a.row < b.row;
2108 return a.col < b.col;
2112 for (
int i = 0;
i < entries.size();) {
2113 const ColIndex
col = entries[
i].col;
2114 CHECK(is_unbounded_[
col]);
2118 for (;
i < entries.size(); ++
i) {
2119 if (entries[i].
col !=
col)
break;
2120 const RowIndex
row = entries[
i].row;
2139 if (activity * activity_sign_correction_[
row] < 0.0) {
2141 if (std::abs(
bound) > std::abs(primal_value_shift)) {
2142 primal_value_shift =
bound;
2147 solution->primal_values[
col] += primal_value_shift;
2150 solution->constraint_statuses[row_at_bound] =
2151 activity_sign_correction_[row_at_bound] == 1.0
2165 const RowIndex num_rows = lp->num_constraints();
2166 for (RowIndex
row(0);
row < num_rows; ++
row) {
2174 return !row_deletion_helper_.
IsEmpty();
2196 for (ColIndex
col(0);
col < num_cols; ++
col) {
2204 if (degree[
row] == 0) {
2211 VLOG(1) <<
"Problem PRIMAL_INFEASIBLE, constraint " <<
row
2212 <<
" is empty and its range ["
2222 return !row_deletion_helper_.
IsEmpty();
2239 is_maximization_(lp.IsMaximizationProblem()),
2241 cost_(lp.objective_coefficients()[e.
col]),
2242 variable_lower_bound_(lp.variable_lower_bounds()[e.
col]),
2243 variable_upper_bound_(lp.variable_upper_bounds()[e.
col]),
2244 constraint_lower_bound_(lp.constraint_lower_bounds()[e.
row]),
2245 constraint_upper_bound_(lp.constraint_upper_bounds()[e.
row]),
2246 constraint_status_(
status) {}
2254 SingletonRowUndo(saved_column,
solution);
2263 MakeConstraintAnEqualityUndo(
solution);
2268void SingletonPreprocessor::DeleteSingletonRow(MatrixEntry e,
2274 if (e.coeff < 0.0) {
2275 std::swap(implied_lower_bound, implied_upper_bound);
2282 std::abs(
parameters_.preprocessor_zero_tolerance() / e.coeff);
2284 implied_lower_bound - potential_error > old_lower_bound
2285 ? implied_lower_bound
2288 implied_upper_bound + potential_error < old_upper_bound
2289 ? implied_upper_bound
2293 if (new_upper_bound == -kInfinity || new_lower_bound == kInfinity) {
2294 VLOG(1) <<
"Problem ProblemStatus::PRIMAL_INFEASIBLE, singleton "
2295 "row causes the bound of the variable "
2296 << e.col <<
" to go to infinity.";
2301 if (new_upper_bound < new_lower_bound) {
2304 VLOG(1) <<
"Problem ProblemStatus::PRIMAL_INFEASIBLE, singleton "
2305 "row causes the bound of the variable "
2306 << e.col <<
" to be infeasible by "
2307 << new_lower_bound - new_upper_bound;
2314 new_upper_bound = new_lower_bound;
2317 new_lower_bound = new_upper_bound;
2327 new_upper_bound = new_lower_bound;
2338void SingletonUndo::SingletonRowUndo(
const SparseColumn& saved_column,
2340 DCHECK_EQ(0,
solution->dual_values[e_.row]);
2348 Fractional implied_lower_bound = constraint_lower_bound_ / e_.coeff;
2349 Fractional implied_upper_bound = constraint_upper_bound_ / e_.coeff;
2350 if (e_.coeff < 0.0) {
2351 std::swap(implied_lower_bound, implied_upper_bound);
2353 const bool lower_bound_changed = implied_lower_bound > variable_lower_bound_;
2354 const bool upper_bound_changed = implied_upper_bound < variable_upper_bound_;
2356 if (!lower_bound_changed && !upper_bound_changed)
return;
2364 const Fractional reduced_cost_for_minimization =
2365 is_maximization_ ? -reduced_cost : reduced_cost;
2368 DCHECK(lower_bound_changed || upper_bound_changed);
2369 if (reduced_cost_for_minimization >= 0.0 && !lower_bound_changed) {
2373 if (reduced_cost_for_minimization <= 0.0 && !upper_bound_changed) {
2391 solution->dual_values[e_.row] = reduced_cost / e_.coeff;
2394 (!lower_bound_changed || !upper_bound_changed)) {
2395 new_constraint_status = lower_bound_changed
2399 if (e_.coeff < 0.0) {
2407 solution->constraint_statuses[e_.row] = new_constraint_status;
2410void SingletonPreprocessor::UpdateConstraintBoundsWithVariableBounds(
2411 MatrixEntry e, LinearProgram* lp) {
2412 Fractional lower_delta = -e.coeff * lp->variable_upper_bounds()[e.col];
2413 Fractional upper_delta = -e.coeff * lp->variable_lower_bounds()[e.col];
2414 if (e.coeff < 0.0) {
2415 std::swap(lower_delta, upper_delta);
2417 lp->SetConstraintBounds(e.row,
2418 lp->constraint_lower_bounds()[e.row] + lower_delta,
2419 lp->constraint_upper_bounds()[e.row] + upper_delta);
2422bool SingletonPreprocessor::IntegerSingletonColumnIsRemovable(
2423 const MatrixEntry& matrix_entry,
const LinearProgram& lp)
const {
2425 DCHECK(lp.IsVariableInteger(matrix_entry.col));
2426 const SparseMatrix& transpose = lp.GetTransposeSparseMatrix();
2427 for (
const SparseColumn::Entry entry :
2438 coefficient_ratio,
parameters_.solution_feasibility_tolerance())) {
2443 lp.constraint_lower_bounds()[matrix_entry.row];
2445 const Fractional lower_bound_ratio = constraint_lb / matrix_entry.coeff;
2447 lower_bound_ratio,
parameters_.solution_feasibility_tolerance())) {
2452 lp.constraint_upper_bounds()[matrix_entry.row];
2454 const Fractional upper_bound_ratio = constraint_ub / matrix_entry.coeff;
2456 upper_bound_ratio,
parameters_.solution_feasibility_tolerance())) {
2463void SingletonPreprocessor::DeleteZeroCostSingletonColumn(
2464 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2468 const SparseColumn& row_as_col = transpose.column(transpose_col);
2470 UpdateConstraintBoundsWithVariableBounds(e, lp);
2475void SingletonUndo::ZeroCostSingletonColumnUndo(
2482 if (variable_upper_bound_ == variable_lower_bound_) {
2483 solution->primal_values[e_.col] = variable_lower_bound_;
2490 const Fractional corrected_dual = is_maximization_
2493 if (corrected_dual > 0) {
2494 DCHECK(
IsFinite(variable_lower_bound_));
2495 solution->primal_values[e_.col] = variable_lower_bound_;
2498 DCHECK(
IsFinite(variable_upper_bound_));
2499 solution->primal_values[e_.col] = variable_upper_bound_;
2507 DCHECK(
IsFinite(variable_lower_bound_));
2508 solution->primal_values[e_.col] = variable_lower_bound_;
2511 DCHECK(
IsFinite(variable_upper_bound_));
2512 solution->primal_values[e_.col] = variable_upper_bound_;
2515 if (constraint_upper_bound_ == constraint_lower_bound_) {
2530 const auto is_smaller_with_tolerance = [tolerance](
Fractional a,
2532 return ::operations_research::IsSmallerWithinTolerance(
a,
b, tolerance);
2534 if (variable_lower_bound_ != -kInfinity) {
2536 activity + e_.coeff * variable_lower_bound_;
2537 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_lb) &&
2538 is_smaller_with_tolerance(activity_at_lb, constraint_upper_bound_)) {
2539 solution->primal_values[e_.col] = variable_lower_bound_;
2544 if (variable_upper_bound_ != kInfinity) {
2546 activity + e_.coeff * variable_upper_bound_;
2547 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_ub) &&
2548 is_smaller_with_tolerance(activity_at_ub, constraint_upper_bound_)) {
2549 solution->primal_values[e_.col] = variable_upper_bound_;
2558 if (constraint_lower_bound_ == -kInfinity &&
2559 constraint_upper_bound_ == kInfinity) {
2560 solution->primal_values[e_.col] = 0.0;
2568 if (constraint_lower_bound_ == constraint_upper_bound_) {
2570 (constraint_lower_bound_ - activity) / e_.coeff;
2575 bool set_constraint_to_lower_bound;
2576 if (constraint_lower_bound_ == -kInfinity) {
2577 set_constraint_to_lower_bound =
false;
2578 }
else if (constraint_upper_bound_ == kInfinity) {
2579 set_constraint_to_lower_bound =
true;
2583 const Fractional to_lb = (constraint_lower_bound_ - activity) / e_.coeff;
2584 const Fractional to_ub = (constraint_upper_bound_ - activity) / e_.coeff;
2585 set_constraint_to_lower_bound =
2586 std::max(variable_lower_bound_ - to_lb, to_lb - variable_upper_bound_) <
2587 std::max(variable_lower_bound_ - to_ub, to_ub - variable_upper_bound_);
2590 if (set_constraint_to_lower_bound) {
2592 (constraint_lower_bound_ - activity) / e_.coeff;
2596 (constraint_upper_bound_ - activity) / e_.coeff;
2601void SingletonPreprocessor::DeleteSingletonColumnInEquality(
2602 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2605 const SparseColumn& row_as_column = transpose.column(transpose_col);
2606 undo_stack_.push_back(
2616 const Fractional rhs = lp->constraint_upper_bounds()[e.row];
2617 const Fractional cost = lp->objective_coefficients()[e.col];
2618 const Fractional multiplier = cost / e.coeff;
2619 lp->SetObjectiveOffset(lp->objective_offset() + rhs * multiplier);
2620 for (
const SparseColumn::Entry e : row_as_column) {
2624 lp->objective_coefficients()[
col] - e.coefficient() * multiplier;
2631 if (std::abs(new_cost) <
parameters_.preprocessor_zero_tolerance()) {
2634 lp->SetObjectiveCoefficient(
col, new_cost);
2639 UpdateConstraintBoundsWithVariableBounds(e, lp);
2643void SingletonUndo::SingletonColumnInEqualityUndo(
2651 solution->dual_values[e_.row] += cost_ / e_.coeff;
2658void SingletonUndo::MakeConstraintAnEqualityUndo(
2661 solution->constraint_statuses[e_.row] = constraint_status_;
2665bool SingletonPreprocessor::MakeConstraintAnEqualityIfPossible(
2666 const SparseMatrix& transpose, MatrixEntry e, LinearProgram* lp) {
2669 const Fractional cst_lower_bound = lp->constraint_lower_bounds()[e.row];
2670 const Fractional cst_upper_bound = lp->constraint_upper_bounds()[e.row];
2671 if (cst_lower_bound == cst_upper_bound)
return true;
2672 if (cst_lower_bound == -kInfinity && cst_upper_bound == kInfinity) {
2683 const DenseRow& variable_ubs = lp->variable_upper_bounds();
2684 const DenseRow& variable_lbs = lp->variable_lower_bounds();
2685 if (e.row >= row_sum_is_cached_.size() || !row_sum_is_cached_[e.row]) {
2686 if (e.row >= row_sum_is_cached_.size()) {
2687 const int new_size = e.row.value() + 1;
2688 row_sum_is_cached_.
resize(new_size);
2689 row_lb_sum_.resize(new_size);
2690 row_ub_sum_.resize(new_size);
2692 row_sum_is_cached_[e.row] =
true;
2693 row_lb_sum_[e.row].Add(cst_lower_bound);
2694 row_ub_sum_[e.row].Add(cst_upper_bound);
2695 for (
const SparseColumn::Entry entry :
2705 if (column_deletion_helper_.
IsColumnMarked(row_as_col))
continue;
2706 if (entry.coefficient() > 0.0) {
2707 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2708 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2710 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2711 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2723 c > 0.0 ? row_lb_sum_[e.row].SumWithoutLb(-c * variable_ubs[e.col]) /
c
2724 : row_ub_sum_[e.row].SumWithoutUb(-c * variable_ubs[e.col]) /
c;
2726 c > 0.0 ? row_ub_sum_[e.row].SumWithoutUb(-c * variable_lbs[e.col]) /
c
2727 : row_lb_sum_[e.row].SumWithoutLb(-c * variable_lbs[e.col]) /
c;
2733 lp->GetObjectiveCoefficientForMinimizationVersion(e.col);
2734 DCHECK_NE(cost, 0.0);
2740 ub, lp->variable_upper_bounds()[e.col])) {
2742 if (cst_upper_bound == kInfinity) {
2746 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2749 if (cst_lower_bound == -kInfinity) {
2753 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2758 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2760 << e.col <<
" has a cost (for minimization) of " << cost
2761 <<
" and is unbounded towards kInfinity.";
2762 DCHECK_EQ(ub, kInfinity);
2779 lp->SetVariableBounds(e.col, lp->variable_lower_bounds()[e.col], kInfinity);
2782 lp->variable_lower_bounds()[e.col], lb)) {
2784 if (cst_lower_bound == -kInfinity) {
2788 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2791 if (cst_upper_bound == kInfinity) {
2795 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2800 DCHECK_EQ(lb, -kInfinity);
2801 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2803 << e.col <<
" has a cost (for minimization) of " << cost
2804 <<
" and is unbounded towards -kInfinity.";
2809 lp->SetVariableBounds(e.col, -kInfinity,
2810 lp->variable_upper_bounds()[e.col]);
2813 if (lp->constraint_lower_bounds()[e.row] ==
2814 lp->constraint_upper_bounds()[e.row]) {
2815 undo_stack_.push_back(SingletonUndo(
2825 const SparseMatrix& matrix = lp->GetSparseMatrix();
2826 const SparseMatrix& transpose = lp->GetTransposeSparseMatrix();
2829 ColIndex num_cols(matrix.num_cols());
2830 RowIndex num_rows(matrix.num_rows());
2831 StrictITIVector<ColIndex, EntryIndex> column_degree(num_cols, EntryIndex(0));
2832 std::vector<ColIndex> column_to_process;
2833 for (ColIndex
col(0);
col < num_cols; ++
col) {
2834 column_degree[
col] = matrix.column(
col).num_entries();
2835 if (column_degree[
col] == 1) {
2836 column_to_process.push_back(
col);
2841 StrictITIVector<RowIndex, EntryIndex> row_degree(num_rows, EntryIndex(0));
2842 std::vector<RowIndex> row_to_process;
2843 for (RowIndex
row(0);
row < num_rows; ++
row) {
2845 if (row_degree[
row] == 1) {
2846 row_to_process.push_back(
row);
2852 (!column_to_process.empty() || !row_to_process.empty())) {
2854 const ColIndex
col = column_to_process.back();
2855 column_to_process.pop_back();
2856 if (column_degree[
col] <= 0)
continue;
2857 const MatrixEntry e = GetSingletonColumnMatrixEntry(
col, matrix);
2859 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2865 if (lp->objective_coefficients()[
col] == 0.0) {
2866 DeleteZeroCostSingletonColumn(transpose, e, lp);
2870 if (std::abs(e.coeff) <
parameters_.preprocessor_zero_tolerance()) {
2873 if (MakeConstraintAnEqualityIfPossible(transpose, e, lp)) {
2874 DeleteSingletonColumnInEquality(transpose, e, lp);
2879 --row_degree[e.row];
2880 if (row_degree[e.row] == 1) {
2881 row_to_process.push_back(e.row);
2885 const RowIndex
row = row_to_process.back();
2886 row_to_process.pop_back();
2887 if (row_degree[
row] <= 0)
continue;
2888 const MatrixEntry e = GetSingletonRowMatrixEntry(
row, transpose);
2894 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2898 DeleteSingletonRow(e, lp);
2899 --column_degree[e.col];
2900 if (column_degree[e.col] == 1) {
2901 column_to_process.push_back(e.col);
2909 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
2927 for (
int i = undo_stack_.size() - 1; i >= 0; --i) {
2936MatrixEntry SingletonPreprocessor::GetSingletonColumnMatrixEntry(
2937 ColIndex
col,
const SparseMatrix& matrix) {
2938 for (
const SparseColumn::Entry e : matrix.column(
col)) {
2940 DCHECK_NE(0.0, e.coefficient());
2941 return MatrixEntry(e.row(),
col, e.coefficient());
2946 LOG(DFATAL) <<
"No unmarked entry in a column that is supposed to have one.";
2948 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2951MatrixEntry SingletonPreprocessor::GetSingletonRowMatrixEntry(
2952 RowIndex
row,
const SparseMatrix& transpose) {
2956 DCHECK_NE(0.0, e.coefficient());
2957 return MatrixEntry(
row,
col, e.coefficient());
2962 LOG(DFATAL) <<
"No unmarked entry in a row that is supposed to have one.";
2964 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2974 const ColIndex num_cols = lp->num_variables();
2975 if (num_cols == 0)
return false;
2977 changed_columns_.clear();
2978 int num_singletons = 0;
2979 for (ColIndex
col(0);
col < num_cols; ++
col) {
2982 if (sparse_column->num_entries() == 1) {
2985 if (sparse_column->num_entries() == 1 &&
2986 sparse_column->GetFirstCoefficient() < 0) {
2987 sparse_column->MultiplyByConstant(-1.0);
2988 lp->SetVariableBounds(
col, -lp->variable_upper_bounds()[
col],
2989 -lp->variable_lower_bounds()[
col]);
2990 lp->SetObjectiveCoefficient(
col, -cost);
2991 changed_columns_.push_back(
col);
2994 VLOG(1) <<
"Changed the sign of " << changed_columns_.size() <<
" columns.";
2995 VLOG(1) << num_singletons <<
" singleton columns left.";
2996 return !changed_columns_.empty();
3003 for (
int i = 0; i < changed_columns_.size(); ++i) {
3004 const ColIndex
col = changed_columns_[i];
3027 saved_row_lower_bounds_ = lp->constraint_lower_bounds();
3028 saved_row_upper_bounds_ = lp->constraint_upper_bounds();
3031 saved_objective_ = lp->objective_coefficients();
3034 const SparseMatrix& original_transpose = lp->GetTransposeSparseMatrix();
3041 std::vector<std::pair<int64_t, RowIndex>> sorted_rows;
3042 const RowIndex num_rows(lp->num_constraints());
3043 for (RowIndex
row(0);
row < num_rows; ++
row) {
3047 lp->constraint_lower_bounds()[
row] !=
3048 lp->constraint_upper_bounds()[
row]) {
3052 for (
const SparseColumn::Entry e : original_row) {
3054 score += lp->GetSparseColumn(
col).num_entries().value();
3056 sorted_rows.push_back({score,
row});
3058 std::sort(sorted_rows.begin(), sorted_rows.end());
3064 for (
const auto p : sorted_rows) {
3065 const RowIndex
row = p.second;
3075 int entry_index = 0;
3076 for (
const SparseColumn::Entry e : original_row) {
3079 r.col[entry_index] =
col;
3080 r.coeff[entry_index] = e.coefficient();
3081 DCHECK_NE(0.0, r.coeff[entry_index]);
3088 if (entry_index < 2)
continue;
3093 r.rhs = lp->constraint_lower_bounds()[
row];
3094 for (
int col_choice = 0; col_choice < NUM_DOUBLETON_COLS; ++col_choice) {
3095 const ColIndex
col = r.col[col_choice];
3096 r.lb[col_choice] = lp->variable_lower_bounds()[
col];
3097 r.ub[col_choice] = lp->variable_upper_bounds()[
col];
3098 r.objective_coefficient[col_choice] = lp->objective_coefficients()[
col];
3103 if (r.lb[DELETED] == r.ub[DELETED] || r.lb[MODIFIED] == r.ub[MODIFIED]) {
3120 const Fractional carry_over_offset = r.rhs / r.coeff[MODIFIED];
3122 -r.coeff[DELETED] / r.coeff[MODIFIED];
3124 carry_over_factor == 0.0) {
3132 r.lb[DELETED] * carry_over_factor + carry_over_offset;
3134 r.ub[DELETED] * carry_over_factor + carry_over_offset;
3135 if (carry_over_factor < 0) {
3136 std::swap(carried_over_lb, carried_over_ub);
3138 if (carried_over_lb <= lb) {
3140 r.bound_backtracking_at_lower_bound = RestoreInfo::ColChoiceAndStatus(
3143 lb = carried_over_lb;
3144 r.bound_backtracking_at_lower_bound = RestoreInfo::ColChoiceAndStatus(
3148 carry_over_factor > 0 ? r.lb[DELETED] : r.ub[DELETED]);
3150 if (carried_over_ub >= ub) {
3152 r.bound_backtracking_at_upper_bound = RestoreInfo::ColChoiceAndStatus(
3155 ub = carried_over_ub;
3156 r.bound_backtracking_at_upper_bound = RestoreInfo::ColChoiceAndStatus(
3160 carry_over_factor > 0 ? r.ub[DELETED] : r.lb[DELETED]);
3166 lp->SetVariableBounds(r.col[MODIFIED], lb, ub);
3169 restore_stack_.push_back(r);
3176 DCHECK_NE(r.coeff[DELETED], 0.0);
3178 -r.coeff[MODIFIED] / r.coeff[DELETED];
3179 const Fractional constant_offset_factor = r.rhs / r.coeff[DELETED];
3181 if (!
IsFinite(substitution_factor) || substitution_factor == 0.0 ||
3182 !
IsFinite(constant_offset_factor)) {
3190 for (
const int col_choice : {DELETED, MODIFIED}) {
3191 const ColIndex
col = r.col[col_choice];
3195 lp->GetSparseColumn(r.col[DELETED])
3196 .AddMultipleToSparseVectorAndDeleteCommonIndex(
3197 substitution_factor, r.row,
parameters_.drop_tolerance(),
3198 lp->GetMutableSparseColumn(r.col[MODIFIED]));
3205 r.objective_coefficient[MODIFIED] +
3206 substitution_factor * r.objective_coefficient[DELETED];
3207 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
3208 lp->SetObjectiveCoefficient(r.col[MODIFIED], new_objective);
3210 lp->SetObjectiveCoefficient(r.col[MODIFIED], 0.0);
3217 SubtractColumnMultipleFromConstraintBound(r.col[DELETED],
3218 constant_offset_factor, lp);
3224 lp->GetMutableSparseColumn(r.col[DELETED])->ClearAndRelease();
3234 return !column_deletion_helper_.
IsEmpty();
3244 const ColIndex num_cols =
solution->variable_statuses.size();
3245 StrictITIVector<ColIndex, bool> new_basic_columns(num_cols,
false);
3247 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
3248 switch (
solution->variable_statuses[r.col[MODIFIED]]) {
3250 LOG(DFATAL) <<
"FIXED variable produced by DoubletonPreprocessor!";
3257 ABSL_FALLTHROUGH_INTENDED;
3262 new_basic_columns[r.col[DELETED]] =
true;
3265 ABSL_FALLTHROUGH_INTENDED;
3270 const RestoreInfo::ColChoiceAndStatus& bound_backtracking =
3273 ? r.bound_backtracking_at_lower_bound
3274 : r.bound_backtracking_at_upper_bound;
3275 const ColIndex bounded_var = r.col[bound_backtracking.col_choice];
3276 const ColIndex basic_var =
3277 r.col[OtherColChoice(bound_backtracking.col_choice)];
3278 solution->variable_statuses[bounded_var] = bound_backtracking.status;
3279 solution->primal_values[bounded_var] = bound_backtracking.value;
3281 new_basic_columns[basic_var] =
true;
3290 solution->primal_values[r.col[DELETED]] =
3292 solution->primal_values[r.col[MODIFIED]] * r.coeff[MODIFIED]) /
3320 StrictITIVector<ColIndex, std::set<int>> col_to_index(num_cols);
3321 for (
int i = 0;
i < restore_stack_.size(); ++
i) {
3322 const RestoreInfo& r = restore_stack_[
i];
3323 col_to_index[r.col[MODIFIED]].insert(i);
3324 col_to_index[r.col[DELETED]].insert(i);
3326 std::vector<ColIndex> singleton_col;
3327 for (ColIndex
col(0);
col < num_cols; ++
col) {
3328 if (!new_basic_columns[
col])
continue;
3329 if (col_to_index[
col].
size() == 1) singleton_col.push_back(
col);
3331 while (!singleton_col.empty()) {
3332 const ColIndex
col = singleton_col.back();
3333 singleton_col.pop_back();
3334 if (!new_basic_columns[
col])
continue;
3335 if (col_to_index[
col].empty())
continue;
3336 CHECK_EQ(col_to_index[
col].
size(), 1);
3337 const int index = *col_to_index[
col].begin();
3338 const RestoreInfo& r = restore_stack_[
index];
3340 const ColChoice col_choice = r.col[MODIFIED] ==
col ? MODIFIED : DELETED;
3344 CHECK_EQ(
solution->dual_values[r.row], 0.0);
3348 saved_objective_[r.col[col_choice]] -
3350 solution->dual_values[r.row] = current_reduced_cost / r.coeff[col_choice];
3353 col_to_index[r.col[DELETED]].erase(
index);
3354 col_to_index[r.col[MODIFIED]].erase(
index);
3355 if (col_to_index[r.col[DELETED]].size() == 1) {
3356 singleton_col.push_back(r.col[DELETED]);
3358 if (col_to_index[r.col[MODIFIED]].size() == 1) {
3359 singleton_col.push_back(r.col[MODIFIED]);
3365 saved_row_upper_bounds_,
solution);
3371 const RowIndex num_rows =
solution->constraint_statuses.size();
3372 DCHECK_EQ(row_lower_bounds.size(), num_rows);
3373 DCHECK_EQ(row_upper_bounds.size(), num_rows);
3374 for (RowIndex
row(0);
row < num_rows; ++
row) {
3378 if (row_lower_bounds[
row] == row_upper_bounds[
row])
continue;
3390void DoubletonEqualityRowPreprocessor::
3391 SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r) {
3393 swap(r->col[DELETED], r->col[MODIFIED]);
3394 swap(r->coeff[DELETED], r->coeff[MODIFIED]);
3395 swap(r->lb[DELETED], r->lb[MODIFIED]);
3396 swap(r->ub[DELETED], r->ub[MODIFIED]);
3397 swap(r->objective_coefficient[DELETED], r->objective_coefficient[MODIFIED]);
3407 if (
parameters_.solve_dual_problem() == GlopParameters::NEVER_DO) {
3434 if (
parameters_.solve_dual_problem() == GlopParameters::LET_SOLVER_DECIDE) {
3435 if (1.0 * primal_num_rows_.value() <
3436 parameters_.dualizer_threshold() * primal_num_cols_.value()) {
3446 variable_lower_bounds_.
assign(num_cols, 0.0);
3447 variable_upper_bounds_.
assign(num_cols, 0.0);
3448 for (ColIndex
col(0);
col < num_cols; ++
col) {
3453 variable_lower_bounds_[
col] =
lower;
3454 variable_upper_bounds_[
col] =
upper;
3458 SubtractColumnMultipleFromConstraintBound(
col,
value, lp);
3466 dual_status_correspondence_.clear();
3467 for (RowIndex
row(0);
row < primal_num_rows_; ++
row) {
3477 LOG(DFATAL) <<
"There should be no free constraint in this lp.";
3480 slack_or_surplus_mapping_.clear();
3481 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3491 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3512 dual.PopulateFromDual(*lp, &duplicated_rows_);
3524 DenseRow new_primal_values(primal_num_cols_, 0.0);
3528 for (ColIndex
col(0);
col < primal_num_cols_; ++
col) {
3546 new_variable_statuses[
col] = ComputeVariableStatus(shift,
lower,
upper);
3555 const ColIndex
end = dual_status_correspondence_.
size();
3557 DCHECK_EQ(
end - begin, slack_or_surplus_mapping_.
size());
3560 const ColIndex
col = slack_or_surplus_mapping_[
index - begin];
3568 new_primal_values[
col] = variable_upper_bounds_[
col];
3571 new_primal_values[
col] = variable_lower_bounds_[
col];
3579 DenseColumn new_dual_values(primal_num_rows_, 0.0);
3586 Fractional sign = primal_is_maximization_problem_ ? -1 : 1;
3587 for (RowIndex
row(0);
row < primal_num_rows_; ++
row) {
3595 if (
solution->variable_statuses[duplicated_rows_[
row]] ==
3605 new_constraint_statuses[
row] =
3612 new_dual_values[
row] +=
3613 sign *
solution->primal_values[duplicated_rows_[
row]];
3618 DCHECK(new_dual_values[
row] == 0 ||
3623 new_primal_values.swap(
solution->primal_values);
3625 new_variable_statuses.swap(
solution->variable_statuses);
3626 new_constraint_statuses.
swap(
solution->constraint_statuses);
3658 bool all_variable_domains_contain_zero =
true;
3659 const ColIndex num_cols = lp->num_variables();
3660 variable_initial_lbs_.
assign(num_cols, 0.0);
3661 variable_initial_ubs_.
assign(num_cols, 0.0);
3662 for (ColIndex
col(0);
col < num_cols; ++
col) {
3663 variable_initial_lbs_[
col] = lp->variable_lower_bounds()[
col];
3664 variable_initial_ubs_[
col] = lp->variable_upper_bounds()[
col];
3665 if (0.0 < variable_initial_lbs_[
col] || 0.0 > variable_initial_ubs_[
col]) {
3666 all_variable_domains_contain_zero =
false;
3669 VLOG(1) <<
"Maximum variable bounds magnitude (before shift): "
3670 << ComputeMaxVariableBoundsMagnitude(*lp);
3673 if (all_variable_domains_contain_zero)
return false;
3677 int num_bound_shifts = 0;
3678 const RowIndex num_rows = lp->num_constraints();
3681 offsets_.
assign(num_cols, 0.0);
3682 for (ColIndex
col(0);
col < num_cols; ++
col) {
3683 if (0.0 < variable_initial_lbs_[
col] || 0.0 > variable_initial_ubs_[
col]) {
3684 Fractional offset = MinInMagnitudeOrZeroIfInfinite(
3685 variable_initial_lbs_[
col], variable_initial_ubs_[
col]);
3693 offset = trunc(offset);
3695 DCHECK_NE(offset, 0.0);
3697 offsets_[
col] = offset;
3698 lp->SetVariableBounds(
col, variable_initial_lbs_[
col] - offset,
3699 variable_initial_ubs_[
col] - offset);
3701 for (
const SparseColumn::Entry e : sparse_column) {
3702 row_offsets[e.row()].Add(e.coefficient() * offset);
3704 objective_offset.Add(lp->objective_coefficients()[
col] * offset);
3708 VLOG(1) <<
"Maximum variable bounds magnitude (after " << num_bound_shifts
3709 <<
" shifts): " << ComputeMaxVariableBoundsMagnitude(*lp);
3712 for (RowIndex
row(0);
row < num_rows; ++
row) {
3713 if (!std::isfinite(row_offsets[
row].
Value())) {
3716 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3722 lp->SetConstraintBounds(
3723 row, lp->constraint_lower_bounds()[
row] - row_offsets[
row].Value(),
3724 lp->constraint_upper_bounds()[
row] - row_offsets[
row].Value());
3726 if (!std::isfinite(objective_offset.Value())) {
3727 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3728 "for the objective.";
3732 lp->SetObjectiveOffset(lp->objective_offset() + objective_offset.Value());
3740 const ColIndex num_cols =
solution->variable_statuses.size();
3741 for (ColIndex
col(0);
col < num_cols; ++
col) {
3747 ABSL_FALLTHROUGH_INTENDED;
3774 const ColIndex num_cols = lp->num_variables();
3775 variable_lower_bounds_.
assign(num_cols, 0.0);
3776 variable_upper_bounds_.
assign(num_cols, 0.0);
3777 for (ColIndex
col(0);
col < num_cols; ++
col) {
3778 variable_lower_bounds_[
col] = lp->variable_lower_bounds()[
col];
3779 variable_upper_bounds_[
col] = lp->variable_upper_bounds()[
col];
3785 cost_scaling_factor_ = lp->ScaleObjective(
parameters_.cost_scaling());
3786 bound_scaling_factor_ = lp->ScaleBounds();
3797 solution->primal_values[
col] *= bound_scaling_factor_;
3802 solution->dual_values[
row] *= cost_scaling_factor_;
3808 const ColIndex num_cols =
solution->primal_values.size();
3809 for (ColIndex
col(0);
col < num_cols; ++
col) {
3812 ABSL_FALLTHROUGH_INTENDED;
3820 ABSL_FALLTHROUGH_INTENDED;
3858 lp->AddSlackVariablesWhereNecessary(
3860 first_slack_col_ = lp->GetFirstSlackVariable();
3870 const RowIndex num_rows =
solution->dual_values.size();
3871 for (RowIndex
row(0);
row < num_rows; ++
row) {
3874 solution->variable_statuses[slack_col];
3878 switch (variable_status) {
3889 solution->constraint_statuses[
row] = constraint_status;
3893 solution->primal_values.resize(first_slack_col_, 0.0);
void EnableLogging(bool enable)
void SetLogToStdOut(bool enable)
Should all messages be displayed on stdout ?
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
const DenseRow & GetStoredValue() const
void Clear()
Restores the class to its initial state.
const DenseBooleanRow & GetMarkedColumns() const
Returns a Boolean vector of the column to be deleted.
void RestoreDeletedColumns(ProblemSolution *solution) const
bool IsEmpty() const
Returns true if no columns have been marked for deletion.
bool IsColumnMarked(ColIndex col) const
Returns whether or not the given column is marked for deletion.
void MarkColumnForDeletion(ColIndex col)
void MarkColumnForDeletionWithState(ColIndex col, Fractional value, VariableStatus status)
const SparseColumn & SavedOrEmptyColumn(ColIndex col) const
void SaveColumn(ColIndex col, const SparseColumn &column)
Saves a column. The first version CHECKs that it is not already done.
const SparseColumn & SavedColumn(ColIndex col) const
Returns the saved column. The first version CHECKs that it was saved.
void SaveColumnIfNotAlreadyDone(ColIndex col, const SparseColumn &column)
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
ProblemStatus ChangeStatusToDualStatus(ProblemStatus status) const
Convert the given problem status to the one of its dual.
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
Removes the fixed variables from the problem.
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
Removes the constraints with no bounds from the problem.
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
const DenseRow & objective_coefficients() const
Returns the objective coefficients (or cost) of variables as a row vector.
void SetObjectiveOffset(Fractional objective_offset)
DenseColumn * mutable_constraint_upper_bounds()
void SetObjectiveCoefficient(ColIndex col, Fractional value)
void SetObjectiveScalingFactor(Fractional objective_scaling_factor)
const DenseColumn & constraint_lower_bounds() const
const DenseRow & variable_lower_bounds() const
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
DenseColumn * mutable_constraint_lower_bounds()
SparseMatrix * GetMutableTransposeSparseMatrix()
bool IsMaximizationProblem() const
const SparseMatrix & GetSparseMatrix() const
void DeleteRows(const DenseBooleanColumn &rows_to_delete)
void UseTransposeMatrixAsReference()
const DenseRow & variable_upper_bounds() const
const DenseColumn & constraint_upper_bounds() const
const SparseColumn & GetSparseColumn(ColIndex col) const
Fractional objective_scaling_factor() const
ColIndex num_variables() const
Returns the number of variables.
Fractional objective_offset() const
Returns the objective offset and scaling factor.
RowIndex num_constraints() const
Returns the number of constraints.
void SetMaximizationProblem(bool maximize)
bool Run(LinearProgram *lp) final
void DestructiveRecoverSolution(ProblemSolution *solution)
void RecoverSolution(ProblemSolution *solution) const override
ProblemStatus status() const
bool IsSmallerWithinFeasibilityTolerance(Fractional a, Fractional b) const
Preprocessor(const GlopParameters *parameters)
const GlopParameters & parameters_
bool IsSmallerWithinPreprocessorZeroTolerance(Fractional a, Fractional b) const
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool IsEmpty() const
Returns true if no rows have been marked for deletion.
void Clear()
Restores the class to its initial state.
void MarkRowForDeletion(RowIndex row)
Adds a deleted row to the helper.
bool IsRowMarked(RowIndex row) const
Returns whether or not the given row is marked for deletion.
const DenseBooleanColumn & GetMarkedRows() const
Returns a Boolean vector of the row to be deleted.
void RestoreDeletedRows(ProblemSolution *solution) const
void UnmarkRow(RowIndex row)
If the given row was marked for deletion, unmark it.
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
SingletonUndo(OperationType type, const LinearProgram &lp, MatrixEntry e, ConstraintStatus status)
@ MAKE_CONSTRAINT_AN_EQUALITY
@ SINGLETON_COLUMN_IN_EQUALITY
@ ZERO_COST_SINGLETON_COLUMN
void Undo(const GlopParameters ¶meters, const SparseColumn &saved_column, const SparseColumn &saved_row, ProblemSolution *solution) const
void ScaleColumnVector(bool up, DenseColumn *column_vector) const
void ScaleRowVector(bool up, DenseRow *row_vector) const
const SparseColumn & column(ColIndex col) const
Access the underlying sparse columns.
SparseColumn * mutable_column(ColIndex col)
EntryIndex num_entries() const
Note this method can only be used when the vector has no duplicates.
Fractional LookUpCoefficient(Index index) const
void assign(IntType size, const T &v)
void resize(IntType size)
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
void RecoverSolution(ProblemSolution *solution) const final
void RemoveZeroCostUnconstrainedVariable(ColIndex col, Fractional target_bound, LinearProgram *lp)
double coefficient(Variable variable) const
Returns 0.0 if the variable is not used in the constraint.
void swap(StrongVector &x) noexcept
void push_back(const value_type &val)
void resize(size_type new_size)
const std::string name
A name for logging purposes.
ReverseView< Container > reversed_view(const Container &c)
constexpr ColIndex kInvalidCol(-1)
constexpr double kInfinity
Infinity for type Fractional.
StrictITIVector< RowIndex, ColIndex > RowToColMapping
StrictITIVector< RowIndex, bool > DenseBooleanColumn
Column of booleans.
StrictITIVector< ColIndex, ColIndex > ColMapping
Row of column indices. Used to represent mappings between columns.
StrictITIVector< RowIndex, ConstraintStatus > ConstraintStatusColumn
Column of constraints (slack variables) statuses.
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
AccurateSum< Fractional > KahanSum
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
SumWithOneMissing< true > SumWithPositiveInfiniteAndOneMissing
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
bool IsFinite(Fractional value)
constexpr RowIndex kInvalidRow(-1)
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
Row of variable statuses.
void Scale(LinearProgram *lp, SparseMatrixScaler *scaler)
void FixConstraintWithFixedStatuses(const DenseColumn &row_lower_bounds, const DenseColumn &row_upper_bounds, ProblemSolution *solution)
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
StrictITIVector< ColIndex, bool > DenseBooleanRow
Row of booleans.
SumWithOneMissing< false > SumWithNegativeInfiniteAndOneMissing
ProblemStatus
Different statuses for a given problem.
@ INFEASIBLE_OR_UNBOUNDED
@ ABNORMAL
An error occurred during the solving process.
@ INVALID_PROBLEM
The input problem was invalid (see LinearProgram.IsValid()).
@ INIT
The solver didn't had a chance to prove anything.
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
Returns the ConstraintStatus corresponding to a given VariableStatus.
ColMapping FindProportionalColumns(const SparseMatrix &matrix, Fractional tolerance)
std::string GetProblemStatusString(ProblemStatus problem_status)
Returns the string representation of the ProblemStatus enum.
std::function< int64_t(const Model &)> Value(IntegerVariable v)
This checks that the variable is fixed.
In SWIG mode, we don't want anything besides these top-level includes.
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
util_intops::StrongVector< ColumnEntryIndex, ElementIndex, ElementAllocator > SparseColumn
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)
Contains the solution of a LinearProgram as returned by a preprocessor.
#define SOLVER_LOG(logger,...)