30#include "absl/strings/str_format.h"
31#include "absl/strings/string_view.h"
48double trunc(
double d) {
return d > 0 ?
floor(d) : ceil(d); }
67#define RUN_PREPROCESSOR(name) \
68 RunAndPushIfRelevant(std::unique_ptr<Preprocessor>(new name(¶meters_)), \
69 #name, time_limit_, lp)
80 initial_num_rows_ = lp->num_constraints();
81 initial_num_cols_ = lp->num_variables();
82 initial_num_entries_ = lp->num_entries();
88 const int kMaxNumPasses = 20;
89 for (
int i = 0; i < kMaxNumPasses; ++i) {
90 const int old_stack_size = preprocessors_.size();
103 if (preprocessors_.size() == old_stack_size) {
105 SOLVER_LOG(logger_,
"Reached fixed point after presolve pass #", i);
119 const int old_stack_size = preprocessors_.size();
125 if (old_stack_size != preprocessors_.size()) {
139 return !preprocessors_.empty();
142#undef RUN_PREPROCESSOR
144void MainLpPreprocessor::RunAndPushIfRelevant(
145 std::unique_ptr<Preprocessor> preprocessor, absl::string_view name,
151 const double start_time =
time_limit->GetElapsedTime();
156 if (lp->num_variables() == 0 && lp->num_constraints() == 0) {
161 if (preprocessor->Run(lp)) {
162 const EntryIndex new_num_entries = lp->num_entries();
163 const double preprocess_time =
time_limit->GetElapsedTime() - start_time;
166 "%-45s: %d(%d) rows, %d(%d) columns, %d(%d) entries. (%fs)",
167 name, lp->num_constraints().value(),
168 (lp->num_constraints() - initial_num_rows_).value(),
169 lp->num_variables().value(),
170 (lp->num_variables() - initial_num_cols_).value(),
173 static_cast<int64_t
>(new_num_entries.value()),
174 static_cast<int64_t
>(new_num_entries.value() -
175 initial_num_entries_.value()),
177 status_ = preprocessor->status();
178 preprocessors_.push_back(std::move(preprocessor));
183 status_ = preprocessor->status();
185 SOLVER_LOG(logger_, name,
" detected that the problem is ",
200 while (!preprocessors_.empty()) {
201 preprocessors_.back()->RecoverSolution(
solution);
202 preprocessors_.pop_back();
211 const int index = saved_columns_.size();
212 CHECK(saved_columns_index_.insert({col, index}).second);
213 saved_columns_.push_back(column);
218 const int index = saved_columns_.size();
219 const bool inserted = saved_columns_index_.insert({col, index}).second;
220 if (inserted) saved_columns_.push_back(column);
224 const auto it = saved_columns_index_.find(col);
225 CHECK(it != saved_columns_index_.end());
226 return saved_columns_[it->second];
230 const auto it = saved_columns_index_.find(col);
231 return it == saved_columns_index_.
end() ? empty_column_
232 : saved_columns_[it->second];
236 is_column_deleted_.clear();
237 stored_value_.clear();
247 if (col >= is_column_deleted_.size()) {
248 is_column_deleted_.resize(col + 1,
false);
249 stored_value_.resize(col + 1, 0.0);
252 is_column_deleted_[col] =
true;
253 stored_value_[col] = fixed_value;
254 stored_status_[col] = status;
261 ColIndex old_index(0);
262 for (ColIndex col(0); col < is_column_deleted_.
size(); ++col) {
263 if (is_column_deleted_[col]) {
264 new_primal_values.push_back(stored_value_[col]);
265 new_variable_statuses.push_back(stored_status_[col]);
267 new_primal_values.push_back(
solution->primal_values[old_index]);
268 new_variable_statuses.push_back(
solution->variable_statuses[old_index]);
274 const ColIndex num_cols =
solution->primal_values.size();
275 DCHECK_EQ(num_cols,
solution->variable_statuses.size());
276 for (; old_index < num_cols; ++old_index) {
277 new_primal_values.push_back(
solution->primal_values[old_index]);
278 new_variable_statuses.push_back(
solution->variable_statuses[old_index]);
280 new_primal_values.swap(
solution->primal_values);
281 new_variable_statuses.swap(
solution->variable_statuses);
292 if (row >= is_row_deleted_.size()) {
293 is_row_deleted_.resize(row + 1,
false);
295 is_row_deleted_[row] =
true;
299 if (row >= is_row_deleted_.size())
return;
300 is_row_deleted_[row] =
false;
304 return is_row_deleted_;
310 RowIndex old_index(0);
311 const RowIndex
end = is_row_deleted_.size();
312 for (RowIndex row(0); row <
end; ++row) {
313 if (is_row_deleted_[row]) {
319 solution->constraint_statuses[old_index]);
325 const RowIndex num_rows =
solution->dual_values.size();
326 DCHECK_EQ(num_rows,
solution->constraint_statuses.size());
327 for (; old_index < num_rows; ++old_index) {
332 new_constraint_statuses.
swap(
solution->constraint_statuses);
346 if (lower_bound == upper_bound) {
347 DCHECK_EQ(value, lower_bound);
351 if (value == lower_bound) {
355 if (value == upper_bound) {
368 const Fractional value = std::abs(a) < std::abs(b) ? a :
b;
369 return IsFinite(value) ? value : 0.0;
373 return IsFinite(value) ? std::abs(value) : 0.0;
380 const ColIndex num_cols = lp.num_variables();
381 for (ColIndex col(0); col < num_cols; ++col) {
382 max_bounds_magnitude = std::max(
383 max_bounds_magnitude,
384 std::max(MagnitudeOrZeroIfInfinite(lp.variable_lower_bounds()[col]),
385 MagnitudeOrZeroIfInfinite(lp.variable_upper_bounds()[col])));
387 return max_bounds_magnitude;
395 column_deletion_helper_.Clear();
396 const ColIndex num_cols = lp->num_variables();
397 for (ColIndex col(0); col < num_cols; ++col) {
398 if (lp->GetSparseColumn(col).IsEmpty()) {
399 const Fractional lower_bound = lp->variable_lower_bounds()[col];
400 const Fractional upper_bound = lp->variable_upper_bounds()[col];
402 lp->GetObjectiveCoefficientForMinimizationVersion(col);
404 if (objective_coefficient == 0) {
416 value = objective_coefficient > 0 ? lower_bound : upper_bound;
418 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, empty column " << col
419 <<
" has a minimization cost of " << objective_coefficient
421 <<
" [" << lower_bound <<
"," << upper_bound <<
"]";
425 lp->SetObjectiveOffset(lp->objective_offset() +
426 value * lp->objective_coefficients()[col]);
429 col, value, ComputeVariableStatus(value, lower_bound, upper_bound));
432 lp->DeleteColumns(column_deletion_helper_.GetMarkedColumns());
433 return !column_deletion_helper_.IsEmpty();
439 column_deletion_helper_.RestoreDeletedColumns(
solution);
451void SubtractColumnMultipleFromConstraintBound(ColIndex col,
457 const RowIndex row = e.row();
458 const Fractional delta = multiple * e.coefficient();
459 (*lbs)[row] -= delta;
460 (*ubs)[row] -= delta;
471struct ColumnWithRepresentativeAndScaledCost {
472 ColumnWithRepresentativeAndScaledCost(ColIndex _col, ColIndex _representative,
474 : col(_col), representative(_representative), scaled_cost(_scaled_cost) {}
476 ColIndex representative;
479 bool operator<(
const ColumnWithRepresentativeAndScaledCost& other)
const {
480 if (representative == other.representative) {
481 if (scaled_cost == other.scaled_cost) {
482 return col < other.col;
484 return scaled_cost < other.scaled_cost;
486 return representative < other.representative;
503 int num_proportionality_classes = 0;
504 std::vector<ColIndex> proportional_columns;
505 for (ColIndex col(0); col < mapping.
size(); ++col) {
506 const ColIndex representative = mapping[col];
509 proportional_columns.push_back(representative);
510 ++num_proportionality_classes;
511 mapping[representative] = representative;
513 proportional_columns.push_back(col);
516 if (proportional_columns.empty())
return false;
517 VLOG(1) <<
"The problem contains " << proportional_columns.size()
518 <<
" columns which belong to " << num_proportionality_classes
519 <<
" proportionality classes.";
523 column_factors_.
assign(num_cols, 0.0);
524 for (
const ColIndex col : proportional_columns) {
538 for (
const ColIndex col : proportional_columns) {
539 const ColIndex representative = mapping[col];
542 const bool is_rc_positive_or_zero =
544 const bool is_rc_negative_or_zero =
546 bool is_slope_upper_bounded = is_rc_positive_or_zero;
547 bool is_slope_lower_bounded = is_rc_negative_or_zero;
548 if (column_factors_[col] < 0.0) {
549 std::swap(is_slope_lower_bounded, is_slope_upper_bounded);
553 column_factors_[col];
554 if (is_slope_lower_bounded) {
555 slope_lower_bound[representative] =
556 std::max(slope_lower_bound[representative], slope);
558 if (is_slope_upper_bounded) {
559 slope_upper_bound[representative] =
560 std::min(slope_upper_bound[representative], slope);
565 for (
const ColIndex col : proportional_columns) {
566 const ColIndex representative = mapping[col];
569 if (representative == col) {
571 slope_lower_bound[representative],
572 slope_upper_bound[representative])) {
573 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, no feasible dual values"
574 <<
" can satisfy the constraints of the proportional columns"
575 <<
" with representative " << representative <<
"."
576 <<
" the associated quantity must be in ["
577 << slope_lower_bound[representative] <<
","
578 << slope_upper_bound[representative] <<
"].";
586 for (
const ColIndex col : proportional_columns) {
587 const ColIndex representative = mapping[col];
590 column_factors_[col];
593 bool variable_can_be_fixed =
false;
601 variable_can_be_fixed =
true;
602 target_bound = (column_factors_[col] >= 0.0) ? upper_bound : lower_bound;
604 slope, slope_upper_bound[representative])) {
606 variable_can_be_fixed =
true;
607 target_bound = (column_factors_[col] >= 0.0) ? lower_bound : upper_bound;
610 if (variable_can_be_fixed) {
615 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED.";
619 SubtractColumnMultipleFromConstraintBound(col, target_bound, lp);
620 column_deletion_helper_.MarkColumnForDeletionWithState(
622 ComputeVariableStatus(target_bound, lower_bound, upper_bound));
628 std::vector<ColumnWithRepresentativeAndScaledCost> sorted_columns;
629 for (
const ColIndex col : proportional_columns) {
630 const ColIndex representative = mapping[col];
634 sorted_columns.push_back(ColumnWithRepresentativeAndScaledCost(
639 std::sort(sorted_columns.begin(), sorted_columns.end());
643 lower_bounds_.assign(num_cols, -
kInfinity);
644 upper_bounds_.assign(num_cols,
kInfinity);
645 new_lower_bounds_.assign(num_cols, -
kInfinity);
646 new_upper_bounds_.assign(num_cols,
kInfinity);
648 for (
int i = 0;
i < sorted_columns.size();) {
649 const ColIndex target_col = sorted_columns[
i].col;
650 const ColIndex target_representative = sorted_columns[
i].representative;
651 const Fractional target_scaled_cost = sorted_columns[
i].scaled_cost;
658 for (++i;
i < sorted_columns.size(); ++
i) {
659 if (sorted_columns[i].representative != target_representative)
break;
660 if (std::abs(sorted_columns[i].scaled_cost - target_scaled_cost) >=
665 const ColIndex col = sorted_columns[
i].col;
668 lower_bounds_[col] = lower_bound;
669 upper_bounds_[col] = upper_bound;
670 merged_columns_[col] = target_col;
675 column_factors_[col] / column_factors_[target_col];
686 MinInMagnitudeOrZeroIfInfinite(lower_bound, upper_bound);
687 Fractional lower_diff = (lower_bound - target_value) * bound_factor;
688 Fractional upper_diff = (upper_bound - target_value) * bound_factor;
689 if (bound_factor < 0.0) {
690 std::swap(lower_diff, upper_diff);
695 SubtractColumnMultipleFromConstraintBound(col, target_value, lp);
696 column_deletion_helper_.MarkColumnForDeletionWithState(
698 ComputeVariableStatus(target_value, lower_bound, upper_bound));
704 if (num_merged > 0) {
705 merged_columns_[target_col] = target_col;
706 const Fractional target_value = MinInMagnitudeOrZeroIfInfinite(
707 lower_bounds_[target_col], upper_bounds_[target_col]);
711 SubtractColumnMultipleFromConstraintBound(target_col, target_value, lp);
717 lp->
DeleteColumns(column_deletion_helper_.GetMarkedColumns());
718 return !column_deletion_helper_.IsEmpty();
725 column_deletion_helper_.RestoreDeletedColumns(
solution);
729 const ColIndex num_cols = merged_columns_.size();
732 DenseRow distance_to_bound(num_cols, 0.0);
737 for (ColIndex col(0); col < num_cols; ++col) {
738 if (merged_columns_[col] == col) {
740 const Fractional distance_to_upper_bound = new_upper_bounds_[col] - value;
741 const Fractional distance_to_lower_bound = value - new_lower_bounds_[col];
742 if (distance_to_upper_bound < distance_to_lower_bound) {
743 distance_to_bound[col] = distance_to_upper_bound;
744 is_distance_to_upper_bound[col] =
true;
746 distance_to_bound[col] = distance_to_lower_bound;
747 is_distance_to_upper_bound[col] =
false;
749 is_representative_basic[col] =
754 wanted_value[col] = value;
755 solution->primal_values[col] = MinInMagnitudeOrZeroIfInfinite(
756 lower_bounds_[col], upper_bounds_[col]);
757 solution->variable_statuses[col] = ComputeVariableStatus(
758 solution->primal_values[col], lower_bounds_[col], upper_bounds_[col]);
763 for (ColIndex col(0); col < num_cols; ++col) {
764 const ColIndex representative = merged_columns_[col];
766 if (
IsFinite(distance_to_bound[representative])) {
771 column_factors_[col] / column_factors_[representative];
773 distance_to_bound[representative] / std::abs(bound_factor);
774 const Fractional width = upper_bounds_[col] - lower_bounds_[col];
775 const bool to_upper_bound =
776 (bound_factor > 0.0) == is_distance_to_upper_bound[representative];
777 if (width <= scaled_distance) {
779 to_upper_bound ? lower_bounds_[col] : upper_bounds_[col];
781 ComputeVariableStatus(
solution->primal_values[col],
782 lower_bounds_[col], upper_bounds_[col]);
783 distance_to_bound[representative] -= width * std::abs(bound_factor);
786 to_upper_bound ? upper_bounds_[col] - scaled_distance
787 : lower_bounds_[col] + scaled_distance;
789 is_representative_basic[representative]
791 : ComputeVariableStatus(
solution->primal_values[col],
794 distance_to_bound[representative] = 0.0;
795 is_representative_basic[representative] =
false;
801 const Fractional error = wanted_value[representative];
803 if (is_representative_basic[representative]) {
805 is_representative_basic[representative] =
false;
809 column_factors_[col] / column_factors_[representative];
810 const bool use_this_variable =
811 (error * bound_factor > 0.0) ? (upper_bounds_[col] ==
kInfinity)
813 if (use_this_variable) {
814 wanted_value[representative] = 0.0;
815 solution->primal_values[col] += error / bound_factor;
816 if (is_representative_basic[representative]) {
818 is_representative_basic[representative] =
false;
839 const RowIndex num_rows = lp->num_constraints();
840 const SparseMatrix& transpose = lp->GetTransposeSparseMatrix();
846 row_factors_.assign(num_rows, 0.0);
847 for (RowIndex row(0); row < num_rows; ++row) {
849 if (!row_transpose.IsEmpty()) {
850 row_factors_[row] = row_transpose.GetFirstCoefficient();
870 int num_proportional_rows = 0;
871 for (RowIndex row(0); row < num_rows; ++row) {
872 const ColIndex representative_row_as_col = mapping[
RowToColIndex(row)];
874 mapping[representative_row_as_col] = representative_row_as_col;
875 is_a_representative[
ColToRowIndex(representative_row_as_col)] =
true;
876 ++num_proportional_rows;
882 for (RowIndex row(0); row < num_rows; ++row) {
887 row_deletion_helper_.MarkRowForDeletion(row);
888 const RowIndex representative_row =
ColToRowIndex(mapping[row_as_col]);
891 row_factors_[representative_row] / row_factors_[row];
892 Fractional implied_lb = factor * lp->constraint_lower_bounds()[row];
893 Fractional implied_ub = factor * lp->constraint_upper_bounds()[row];
895 std::swap(implied_lb, implied_ub);
899 if (implied_lb >= lower_bounds[representative_row]) {
900 lower_bounds[representative_row] = implied_lb;
901 lower_bound_sources_[representative_row] = row;
903 if (implied_ub <= upper_bounds[representative_row]) {
904 upper_bounds[representative_row] = implied_ub;
905 upper_bound_sources_[representative_row] = row;
913 for (RowIndex row(0); row < num_rows; ++row) {
914 if (!is_a_representative[row])
continue;
915 const RowIndex lower_source = lower_bound_sources_[row];
916 const RowIndex upper_source = upper_bound_sources_[row];
921 if (lower_source == upper_source) {
925 row_deletion_helper_.UnmarkRow(lower_source);
930 upper_bounds[row])) {
936 if (lp->constraint_lower_bounds()[lower_source] ==
937 lp->constraint_upper_bounds()[lower_source]) {
938 row_deletion_helper_.UnmarkRow(lower_source);
941 if (lp->constraint_lower_bounds()[upper_source] ==
942 lp->constraint_upper_bounds()[upper_source]) {
943 row_deletion_helper_.UnmarkRow(upper_source);
951 RowIndex new_representative = lower_source;
952 RowIndex other = upper_source;
953 if (std::abs(row_factors_[new_representative]) <
954 std::abs(row_factors_[other])) {
955 std::swap(new_representative, other);
960 row_factors_[new_representative] / row_factors_[other];
961 Fractional new_lb = factor * lp->constraint_lower_bounds()[other];
962 Fractional new_ub = factor * lp->constraint_upper_bounds()[other];
964 std::swap(new_lb, new_ub);
967 lower_bound_sources_[new_representative] = new_representative;
968 upper_bound_sources_[new_representative] = new_representative;
970 if (new_lb > lp->constraint_lower_bounds()[new_representative]) {
971 lower_bound_sources_[new_representative] = other;
973 new_lb = lp->constraint_lower_bounds()[new_representative];
975 if (new_ub < lp->constraint_upper_bounds()[new_representative]) {
976 upper_bound_sources_[new_representative] = other;
978 new_ub = lp->constraint_upper_bounds()[new_representative];
980 const RowIndex new_lower_source =
981 lower_bound_sources_[new_representative];
982 if (new_lower_source == upper_bound_sources_[new_representative]) {
983 row_deletion_helper_.UnmarkRow(new_lower_source);
984 lower_bound_sources_[new_representative] =
kInvalidRow;
985 upper_bound_sources_[new_representative] =
kInvalidRow;
994 if (new_lb > new_ub) {
995 if (lower_bound_sources_[new_representative] == new_representative) {
996 new_ub = lp->constraint_lower_bounds()[new_representative];
998 new_lb = lp->constraint_upper_bounds()[new_representative];
1001 row_deletion_helper_.UnmarkRow(new_representative);
1002 lp->SetConstraintBounds(new_representative, new_lb, new_ub);
1006 lp_is_maximization_problem_ = lp->IsMaximizationProblem();
1007 lp->DeleteRows(row_deletion_helper_.GetMarkedRows());
1008 return !row_deletion_helper_.IsEmpty();
1015 row_deletion_helper_.RestoreDeletedRows(
solution);
1020 const RowIndex num_rows =
solution->dual_values.size();
1021 for (RowIndex row(0); row < num_rows; ++row) {
1022 const RowIndex lower_source = lower_bound_sources_[row];
1023 const RowIndex upper_source = upper_bound_sources_[row];
1025 DCHECK_NE(lower_source, upper_source);
1026 DCHECK(lower_source == row || upper_source == row);
1038 const Fractional corrected_dual_value = lp_is_maximization_problem_
1041 if (corrected_dual_value != 0.0) {
1051 DCHECK_EQ(0.0,
solution->dual_values[lower_source]);
1052 const Fractional factor = row_factors_[row] / row_factors_[lower_source];
1056 solution->constraint_statuses[lower_source] =
1061 DCHECK_EQ(0.0,
solution->dual_values[upper_source]);
1062 const Fractional factor = row_factors_[row] / row_factors_[upper_source];
1066 solution->constraint_statuses[upper_source] =
1074 solution->constraint_statuses[row] =
1088 const ColIndex num_cols = lp->num_variables();
1089 for (ColIndex col(0); col < num_cols; ++col) {
1090 const Fractional lower_bound = lp->variable_lower_bounds()[col];
1091 const Fractional upper_bound = lp->variable_upper_bounds()[col];
1092 if (lower_bound == upper_bound) {
1097 SubtractColumnMultipleFromConstraintBound(col, fixed_value, lp);
1098 column_deletion_helper_.MarkColumnForDeletionWithState(
1104 return !column_deletion_helper_.
IsEmpty();
1121 const RowIndex num_rows = lp->num_constraints();
1126 const ColIndex num_cols = lp->num_variables();
1128 for (ColIndex col(0); col < num_cols; ++col) {
1129 const Fractional lower = lp->variable_lower_bounds()[col];
1130 const Fractional upper = lp->variable_upper_bounds()[col];
1131 for (
const SparseColumn::Entry e : lp->GetSparseColumn(col)) {
1132 const RowIndex row = e.row();
1135 implied_lower_bounds[row] += lower * coeff;
1136 implied_upper_bounds[row] += upper * coeff;
1138 implied_lower_bounds[row] += upper * coeff;
1139 implied_upper_bounds[row] += lower * coeff;
1147 int num_implied_free_constraints = 0;
1148 int num_forcing_constraints = 0;
1149 is_forcing_up_.assign(num_rows,
false);
1151 for (RowIndex row(0); row < num_rows; ++row) {
1152 if (row_degree[row] == 0)
continue;
1153 Fractional lower = lp->constraint_lower_bounds()[row];
1154 Fractional upper = lp->constraint_upper_bounds()[row];
1158 implied_upper_bounds[row]) ||
1161 VLOG(1) <<
"implied bound " << implied_lower_bounds[row] <<
" "
1162 << implied_upper_bounds[row];
1163 VLOG(1) <<
"constraint bound " << lower <<
" " << upper;
1172 is_forcing_down[row] =
true;
1173 ++num_forcing_constraints;
1177 implied_lower_bounds[row])) {
1178 is_forcing_up_[row] =
true;
1179 ++num_forcing_constraints;
1190 implied_lower_bounds[row]) &&
1194 ++num_implied_free_constraints;
1198 if (num_implied_free_constraints > 0) {
1199 VLOG(1) << num_implied_free_constraints <<
" implied free constraints.";
1202 if (num_forcing_constraints > 0) {
1203 VLOG(1) << num_forcing_constraints <<
" forcing constraints.";
1204 lp_is_maximization_problem_ = lp->IsMaximizationProblem();
1205 costs_.resize(num_cols, 0.0);
1206 for (ColIndex col(0); col < num_cols; ++col) {
1208 const Fractional lower = lp->variable_lower_bounds()[col];
1209 const Fractional upper = lp->variable_upper_bounds()[col];
1210 bool is_forced =
false;
1212 for (
const SparseColumn::Entry e : column) {
1213 if (is_forcing_down[e.row()]) {
1214 const Fractional candidate = e.coefficient() < 0.0 ? lower : upper;
1215 if (is_forced && candidate != target_bound) {
1223 target_bound = std::abs(lower) < std::abs(upper) ? lower : upper;
1226 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1227 << std::fixed << std::setprecision(10) << lower <<
", "
1228 << upper <<
"]. coeff:" << e.coefficient();
1232 target_bound = candidate;
1235 if (is_forcing_up_[e.row()]) {
1236 const Fractional candidate = e.coefficient() < 0.0 ? upper : lower;
1237 if (is_forced && candidate != target_bound) {
1241 target_bound = std::abs(lower) < std::abs(upper) ? lower : upper;
1244 VLOG(1) <<
"A variable is forced in both directions! bounds: ["
1245 << std::fixed << std::setprecision(10) << lower <<
", "
1246 << upper <<
"]. coeff:" << e.coefficient();
1250 target_bound = candidate;
1257 SubtractColumnMultipleFromConstraintBound(col, target_bound, lp);
1258 column_deletion_helper_.MarkColumnForDeletionWithState(
1260 ComputeVariableStatus(target_bound, lower, upper));
1261 columns_saver_.SaveColumn(col, column);
1262 costs_[col] = lp->objective_coefficients()[col];
1265 for (RowIndex row(0); row < num_rows; ++row) {
1272 if (is_forcing_down[row] || is_forcing_up_[row]) {
1273 row_deletion_helper_.MarkRowForDeletion(row);
1278 lp->DeleteColumns(column_deletion_helper_.GetMarkedColumns());
1279 lp->DeleteRows(row_deletion_helper_.GetMarkedRows());
1280 return !column_deletion_helper_.IsEmpty();
1287 column_deletion_helper_.RestoreDeletedColumns(
solution);
1288 row_deletion_helper_.RestoreDeletedRows(
solution);
1290 struct DeletionEntry {
1295 std::vector<DeletionEntry> entries;
1298 const ColIndex size = column_deletion_helper_.GetMarkedColumns().size();
1299 for (ColIndex col(0); col < size; ++col) {
1300 if (!column_deletion_helper_.IsColumnMarked(col))
continue;
1304 for (
const SparseColumn::Entry e : columns_saver_.SavedColumn(col)) {
1305 const RowIndex row = e.row();
1306 if (row_deletion_helper_.IsRowMarked(row)) {
1308 last_coefficient = e.coefficient();
1312 entries.push_back({last_row, col, last_coefficient});
1317 std::sort(entries.begin(), entries.end(),
1318 [](
const DeletionEntry& a,
const DeletionEntry& b) {
1319 if (a.row == b.row) return a.col < b.col;
1320 return a.row < b.row;
1333 for (
int i = 0;
i < entries.size();) {
1334 const RowIndex row = entries[
i].row;
1335 DCHECK(row_deletion_helper_.IsRowMarked(row));
1340 for (;
i < entries.size(); ++
i) {
1341 if (entries[i].row != row)
break;
1342 const ColIndex col = entries[
i].col;
1346 const Fractional reduced_cost = costs_[col] - scalar_product;
1348 if (is_forcing_up_[row] == !lp_is_maximization_problem_) {
1349 if (bound < new_dual_value) {
1350 new_dual_value =
bound;
1351 new_basic_column = col;
1354 if (bound > new_dual_value) {
1355 new_dual_value =
bound;
1356 new_basic_column = col;
1361 solution->dual_values[row] = new_dual_value;
1363 solution->constraint_statuses[row] =
1377 if (!
parameters_.use_implied_free_preprocessor())
return false;
1378 const RowIndex num_rows = lp->num_constraints();
1379 const ColIndex num_cols = lp->num_variables();
1385 const int size = num_rows.value();
1388 util_intops::StrongVector<RowIndex, SumWithNegativeInfiniteAndOneMissing>
1390 util_intops::StrongVector<RowIndex, SumWithPositiveInfiniteAndOneMissing>
1394 for (ColIndex col(0); col < num_cols; ++col) {
1395 const Fractional lower_bound = lp->variable_lower_bounds()[col];
1396 const Fractional upper_bound = lp->variable_upper_bounds()[col];
1397 for (
const SparseColumn::Entry e : lp->GetSparseColumn(col)) {
1398 Fractional entry_lb = e.coefficient() * lower_bound;
1399 Fractional entry_ub = e.coefficient() * upper_bound;
1400 if (e.coefficient() < 0.0) std::swap(entry_lb, entry_ub);
1401 lb_sums[e.row()].Add(entry_lb);
1402 ub_sums[e.row()].Add(entry_ub);
1412 for (RowIndex row(0); row < num_rows; ++row) {
1413 lb_sums[row].Add(-lp->constraint_upper_bounds()[row]);
1414 ub_sums[row].Add(-lp->constraint_lower_bounds()[row]);
1421 variable_offsets_.
assign(num_cols, 0.0);
1438 std::vector<std::pair<EntryIndex, ColIndex>> col_by_degree;
1439 col_by_degree.reserve(num_cols.value());
1440 for (ColIndex col(0); col < num_cols; ++col) {
1441 col_by_degree.push_back({lp->GetSparseColumn(col).num_entries(), col});
1443 std::sort(col_by_degree.begin(), col_by_degree.end());
1446 int num_already_free_variables = 0;
1447 int num_implied_free_variables = 0;
1448 int num_fixed_variables = 0;
1449 for (
const auto [_, col] : col_by_degree) {
1451 const Fractional lower_bound = lp->variable_lower_bounds()[col];
1452 const Fractional upper_bound = lp->variable_upper_bounds()[col];
1454 ++num_already_free_variables;
1457 if (lower_bound == upper_bound)
continue;
1462 for (
const SparseColumn::Entry e : lp->GetSparseColumn(col)) {
1465 if (used_rows[e.row()])
continue;
1471 if (coeff < 0.0) std::swap(entry_lb, entry_ub);
1482 coeff > 0.0 ? -ub_sums[e.row()].SumWithoutUb(entry_ub) / coeff
1483 : -lb_sums[e.row()].SumWithoutLb(entry_lb) / coeff;
1485 coeff > 0.0 ? -lb_sums[e.row()].SumWithoutLb(entry_lb) / coeff
1486 : -ub_sums[e.row()].SumWithoutUb(entry_ub) / coeff;
1488 overall_implied_lb = std::max(overall_implied_lb, implied_lb);
1489 overall_implied_ub = std::min(overall_implied_ub, implied_ub);
1496 overall_implied_ub)) {
1504 overall_implied_lb) ||
1510 ++num_fixed_variables;
1513 overall_implied_lb)) {
1519 ++num_fixed_variables;
1526 overall_implied_lb) &&
1529 ++num_implied_free_variables;
1531 for (
const SparseColumn::Entry e : lp->GetSparseColumn(col)) {
1532 used_rows[e.row()] =
true;
1553 DCHECK_NE(lower_bound, upper_bound);
1555 MinInMagnitudeOrZeroIfInfinite(lower_bound, upper_bound);
1556 if (offset != 0.0) {
1557 variable_offsets_[col] = offset;
1558 SubtractColumnMultipleFromConstraintBound(col, offset, lp);
1560 postsolve_status_of_free_variables_[col] =
1561 ComputeVariableStatus(offset, lower_bound, upper_bound);
1564 VLOG(1) << num_already_free_variables <<
" free variables in the problem.";
1565 VLOG(1) << num_implied_free_variables <<
" implied free columns.";
1566 VLOG(1) << num_fixed_variables <<
" variables can be fixed.";
1568 return num_implied_free_variables > 0;
1574 const ColIndex num_cols =
solution->variable_statuses.size();
1575 for (ColIndex col(0); col < num_cols; ++col) {
1578 DCHECK_EQ(0.0, variable_offsets_[col]);
1583 postsolve_status_of_free_variables_[col];
1587 solution->primal_values[col] += variable_offsets_[col];
1605 for (ColIndex doubleton_col(0); doubleton_col < num_cols; ++doubleton_col) {
1614 r.col = doubleton_col;
1617 for (
const SparseColumn::Entry e : original_matrix.
column(r.col)) {
1618 if (row_deletion_helper_.IsRowMarked(e.row()))
break;
1619 r.row[index] = e.row();
1620 r.coeff[index] = e.coefficient();
1621 DCHECK_NE(0.0, e.coefficient());
1624 if (index != NUM_ROWS)
continue;
1630 DCHECK_EQ(r.coeff[MODIFIED],
1636 if (std::abs(r.coeff[DELETED]) < std::abs(r.coeff[MODIFIED])) {
1637 std::swap(r.coeff[DELETED], r.coeff[MODIFIED]);
1638 std::swap(r.row[DELETED], r.row[MODIFIED]);
1645 r.deleted_row_as_column.Swap(
1654 new_variable_lb /= r.coeff[DELETED];
1655 new_variable_ub /= r.coeff[DELETED];
1656 if (r.coeff[DELETED] < 0.0) std::swap(new_variable_lb, new_variable_ub);
1662 r.deleted_row_as_column.AddMultipleToSparseVectorAndIgnoreCommonIndex(
1663 -r.coeff[MODIFIED] / r.coeff[DELETED],
ColToRowIndex(r.col),
1669 if (r.objective_coefficient != 0.0) {
1670 for (
const SparseColumn::Entry e : r.deleted_row_as_column) {
1672 if (col == r.col)
continue;
1675 e.coefficient() * r.objective_coefficient / r.coeff[DELETED];
1688 row_deletion_helper_.MarkRowForDeletion(r.row[DELETED]);
1689 restore_stack_.push_back(r);
1692 if (!row_deletion_helper_.IsEmpty()) {
1695 lp->
DeleteRows(row_deletion_helper_.GetMarkedRows());
1704 row_deletion_helper_.RestoreDeletedRows(
solution);
1705 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
1707 switch (
solution->variable_statuses[r.col]) {
1709 solution->constraint_statuses[r.row[DELETED]] =
1713 solution->constraint_statuses[r.row[DELETED]] =
1718 solution->constraint_statuses[r.row[DELETED]] =
1727 DCHECK_EQ(
solution->constraint_statuses[r.row[DELETED]],
1735 for (
const SparseColumn::Entry e : r.deleted_row_as_column) {
1737 if (col == r.col)
continue;
1738 new_variable_value -= (e.coefficient() / r.coeff[DELETED]) *
1741 solution->primal_values[r.col] = new_variable_value;
1751 r.objective_coefficient -
1752 r.coeff[MODIFIED] *
solution->dual_values[r.row[MODIFIED]];
1754 solution->dual_values[r.row[DELETED]] =
1755 current_reduced_cost / r.coeff[DELETED];
1757 DCHECK_EQ(
solution->dual_values[r.row[DELETED]], 0.0);
1773 return direction > 0.0 ? lp.constraint_upper_bounds()[row] !=
kInfinity
1774 : lp.constraint_lower_bounds()[row] != -
kInfinity;
1781 DCHECK_EQ(0.0, lp->objective_coefficients()[col]);
1783 rhs_.resize(lp->num_constraints(), 0.0);
1784 activity_sign_correction_.resize(lp->num_constraints(), 1.0);
1785 is_unbounded_.resize(lp->num_variables(),
false);
1787 const bool is_unbounded_up = (target_bound ==
kInfinity);
1789 for (
const SparseColumn::Entry e : column) {
1790 const RowIndex row = e.row();
1791 if (!row_deletion_helper_.IsRowMarked(row)) {
1792 row_deletion_helper_.MarkRowForDeletion(row);
1793 rows_saver_.SaveColumn(
1797 const bool is_constraint_upper_bound_relevant =
1798 e.coefficient() > 0.0 ? !is_unbounded_up : is_unbounded_up;
1799 activity_sign_correction_[row] =
1800 is_constraint_upper_bound_relevant ? 1.0 : -1.0;
1801 rhs_[row] = is_constraint_upper_bound_relevant
1802 ? lp->constraint_upper_bounds()[row]
1803 : lp->constraint_lower_bounds()[row];
1810 is_unbounded_[col] =
true;
1811 Fractional initial_feasible_value = MinInMagnitudeOrZeroIfInfinite(
1812 lp->variable_lower_bounds()[col], lp->variable_upper_bounds()[col]);
1814 col, initial_feasible_value,
1815 ComputeVariableStatus(initial_feasible_value,
1816 lp->variable_lower_bounds()[col],
1817 lp->variable_upper_bounds()[col]));
1835 const RowIndex num_rows = lp->num_constraints();
1838 for (RowIndex row(0); row < num_rows; ++row) {
1839 if (lp->constraint_lower_bounds()[row] == -
kInfinity) {
1840 dual_ub_[row] = 0.0;
1842 if (lp->constraint_upper_bounds()[row] ==
kInfinity) {
1843 dual_lb_[row] = 0.0;
1847 const ColIndex num_cols = lp->num_variables();
1848 may_have_participated_lb_.
assign(num_cols,
false);
1849 may_have_participated_ub_.
assign(num_cols,
false);
1852 std::deque<ColIndex> columns_to_process;
1854 std::vector<RowIndex> changed_rows;
1855 for (ColIndex col(0); col < num_cols; ++col) {
1856 columns_to_process.push_back(col);
1862 const int limit = 5 * num_cols.value();
1863 for (
int count = 0; !columns_to_process.empty() && count < limit; ++count) {
1864 const ColIndex col = columns_to_process.front();
1865 columns_to_process.pop_front();
1866 in_columns_to_process[col] =
false;
1871 lp->GetObjectiveCoefficientForMinimizationVersion(col);
1872 const Fractional col_lb = lp->variable_lower_bounds()[col];
1873 const Fractional col_ub = lp->variable_upper_bounds()[col];
1878 rc_lb.
Add(col_cost);
1879 rc_ub.Add(col_cost);
1880 for (
const SparseColumn::Entry e : column) {
1881 if (row_deletion_helper_.
IsRowMarked(e.row()))
continue;
1884 rc_lb.Add(-coeff * dual_ub_[e.row()]);
1885 rc_ub.Add(-coeff * dual_lb_[e.row()]);
1887 rc_lb.Add(-coeff * dual_lb_[e.row()]);
1888 rc_ub.Add(-coeff * dual_ub_[e.row()]);
1896 bool can_be_removed =
false;
1898 bool rc_is_away_from_zero;
1899 if (rc_ub.Sum() <= low_tolerance) {
1900 can_be_removed =
true;
1901 target_bound = col_ub;
1903 target_bound = std::floor(target_bound + high_tolerance);
1906 rc_is_away_from_zero = rc_ub.Sum() <= -high_tolerance;
1907 can_be_removed = !may_have_participated_ub_[col];
1909 if (rc_lb.Sum() >= -low_tolerance) {
1912 if (!can_be_removed || !
IsFinite(target_bound)) {
1913 can_be_removed =
true;
1914 target_bound = col_lb;
1916 target_bound = std::ceil(target_bound - high_tolerance);
1919 rc_is_away_from_zero = rc_lb.Sum() >= high_tolerance;
1920 can_be_removed = !may_have_participated_lb_[col];
1924 if (can_be_removed) {
1928 column_deletion_helper_.MarkColumnForDeletionWithState(
1930 ComputeVariableStatus(target_bound, col_lb, col_ub));
1936 if (rc_is_away_from_zero) {
1937 VLOG(1) <<
"Problem INFEASIBLE_OR_UNBOUNDED, variable " << col
1938 <<
" can move to " << target_bound
1939 <<
" and its reduced cost is in [" << rc_lb.Sum() <<
", "
1940 << rc_ub.Sum() <<
"]";
1953 if (col_cost != 0.0)
continue;
1955 const double sign_correction = (target_bound ==
kInfinity) ? 1.0 : -1.0;
1957 for (
const SparseColumn::Entry e : column) {
1960 if (IsConstraintBlockingVariable(
1961 *lp, sign_correction * e.coefficient(), e.row())) {
1979 DCHECK(!can_be_removed);
1986 changed_rows.clear();
1987 for (
const SparseColumn::Entry e : column) {
1988 if (row_deletion_helper_.IsRowMarked(e.row()))
continue;
1990 const RowIndex row = e.row();
1994 rc_ub.SumWithoutUb(-c * dual_lb_[row]) /
c;
1995 if (candidate < dual_ub_[row]) {
1996 dual_ub_[row] = candidate;
1997 may_have_participated_lb_[col] =
true;
1998 changed_rows.push_back(row);
2002 rc_ub.SumWithoutUb(-c * dual_ub_[row]) /
c;
2003 if (candidate > dual_lb_[row]) {
2004 dual_lb_[row] = candidate;
2005 may_have_participated_lb_[col] =
true;
2006 changed_rows.push_back(row);
2013 rc_lb.SumWithoutLb(-c * dual_ub_[row]) /
c;
2014 if (candidate > dual_lb_[row]) {
2015 dual_lb_[row] = candidate;
2016 may_have_participated_ub_[col] =
true;
2017 changed_rows.push_back(row);
2021 rc_lb.SumWithoutLb(-c * dual_lb_[row]) /
c;
2022 if (candidate < dual_ub_[row]) {
2023 dual_ub_[row] = candidate;
2024 may_have_participated_ub_[col] =
true;
2025 changed_rows.push_back(row);
2031 if (!changed_rows.empty()) {
2032 const SparseMatrix& transpose = lp->GetTransposeSparseMatrix();
2033 for (
const RowIndex row : changed_rows) {
2034 for (
const SparseColumn::Entry entry :
2037 if (!in_columns_to_process[col]) {
2038 columns_to_process.push_back(col);
2039 in_columns_to_process[col] =
true;
2050 const ColIndex
end = column_deletion_helper_.GetMarkedColumns().size();
2051 for (ColIndex col(0); col <
end; ++col) {
2052 if (column_deletion_helper_.IsColumnMarked(col)) {
2054 column_deletion_helper_.GetStoredValue()[col];
2055 SubtractColumnMultipleFromConstraintBound(col, target_bound, lp);
2059 lp->DeleteColumns(column_deletion_helper_.GetMarkedColumns());
2060 lp->DeleteRows(row_deletion_helper_.GetMarkedRows());
2061 return !column_deletion_helper_.IsEmpty() || !row_deletion_helper_.IsEmpty();
2068 column_deletion_helper_.RestoreDeletedColumns(
solution);
2069 row_deletion_helper_.RestoreDeletedRows(
solution);
2071 struct DeletionEntry {
2076 std::vector<DeletionEntry> entries;
2079 const RowIndex num_rows =
solution->dual_values.size();
2081 for (RowIndex row(0); row < num_rows; ++row) {
2082 if (!row_deletion_helper_.IsRowMarked(row))
continue;
2086 for (
const SparseColumn::Entry e :
2089 if (is_unbounded_[col]) {
2091 last_coefficient = e.coefficient();
2095 entries.push_back({row, last_col, last_coefficient});
2100 std::sort(entries.begin(), entries.end(),
2101 [](
const DeletionEntry& a,
const DeletionEntry& b) {
2102 if (a.col == b.col) return a.row < b.row;
2103 return a.col < b.col;
2107 for (
int i = 0;
i < entries.size();) {
2108 const ColIndex col = entries[
i].col;
2109 CHECK(is_unbounded_[col]);
2113 for (;
i < entries.size(); ++
i) {
2114 if (entries[i].col != col)
break;
2115 const RowIndex row = entries[
i].row;
2123 if (!
IsFinite(rhs_[row]))
continue;
2134 if (activity * activity_sign_correction_[row] < 0.0) {
2136 if (std::abs(bound) > std::abs(primal_value_shift)) {
2137 primal_value_shift =
bound;
2142 solution->primal_values[col] += primal_value_shift;
2145 solution->constraint_statuses[row_at_bound] =
2146 activity_sign_correction_[row_at_bound] == 1.0
2160 const RowIndex num_rows = lp->num_constraints();
2161 for (RowIndex row(0); row < num_rows; ++row) {
2162 const Fractional lower_bound = lp->constraint_lower_bounds()[row];
2163 const Fractional upper_bound = lp->constraint_upper_bounds()[row];
2165 row_deletion_helper_.MarkRowForDeletion(row);
2168 lp->DeleteRows(row_deletion_helper_.GetMarkedRows());
2169 return !row_deletion_helper_.IsEmpty();
2176 row_deletion_helper_.RestoreDeletedRows(
solution);
2191 for (ColIndex col(0); col < num_cols; ++col) {
2198 for (RowIndex row(0); row < num_rows; ++row) {
2199 if (degree[row] == 0) {
2206 VLOG(1) <<
"Problem PRIMAL_INFEASIBLE, constraint " << row
2207 <<
" is empty and its range ["
2217 return !row_deletion_helper_.
IsEmpty();
2224 row_deletion_helper_.RestoreDeletedRows(
solution);
2234 is_maximization_(lp.IsMaximizationProblem()),
2236 cost_(lp.objective_coefficients()[e.col]),
2237 variable_lower_bound_(lp.variable_lower_bounds()[e.col]),
2238 variable_upper_bound_(lp.variable_upper_bounds()[e.col]),
2239 constraint_lower_bound_(lp.constraint_lower_bounds()[e.row]),
2240 constraint_upper_bound_(lp.constraint_upper_bounds()[e.row]),
2241 constraint_status_(status) {}
2249 SingletonRowUndo(saved_column,
solution);
2252 ZeroCostSingletonColumnUndo(parameters, saved_row,
solution);
2255 SingletonColumnInEqualityUndo(parameters, saved_row,
solution);
2258 MakeConstraintAnEqualityUndo(
solution);
2263void SingletonPreprocessor::DeleteSingletonRow(
MatrixEntry e,
2269 if (e.
coeff < 0.0) {
2270 std::swap(implied_lower_bound, implied_upper_bound);
2279 implied_lower_bound - potential_error > old_lower_bound
2280 ? implied_lower_bound
2283 implied_upper_bound + potential_error < old_upper_bound
2284 ? implied_upper_bound
2289 VLOG(1) <<
"Problem ProblemStatus::PRIMAL_INFEASIBLE, singleton "
2290 "row causes the bound of the variable "
2291 << e.
col <<
" to go to infinity.";
2296 if (new_upper_bound < new_lower_bound) {
2299 VLOG(1) <<
"Problem ProblemStatus::PRIMAL_INFEASIBLE, singleton "
2300 "row causes the bound of the variable "
2301 << e.
col <<
" to be infeasible by "
2302 << new_lower_bound - new_upper_bound;
2309 new_upper_bound = new_lower_bound;
2312 new_lower_bound = new_upper_bound;
2322 new_upper_bound = new_lower_bound;
2324 row_deletion_helper_.MarkRowForDeletion(e.
row);
2333void SingletonUndo::SingletonRowUndo(
const SparseColumn& saved_column,
2335 DCHECK_EQ(0,
solution->dual_values[e_.row]);
2343 Fractional implied_lower_bound = constraint_lower_bound_ / e_.coeff;
2344 Fractional implied_upper_bound = constraint_upper_bound_ / e_.coeff;
2345 if (e_.coeff < 0.0) {
2346 std::swap(implied_lower_bound, implied_upper_bound);
2348 const bool lower_bound_changed = implied_lower_bound > variable_lower_bound_;
2349 const bool upper_bound_changed = implied_upper_bound < variable_upper_bound_;
2351 if (!lower_bound_changed && !upper_bound_changed)
return;
2359 const Fractional reduced_cost_for_minimization =
2360 is_maximization_ ? -reduced_cost : reduced_cost;
2363 DCHECK(lower_bound_changed || upper_bound_changed);
2364 if (reduced_cost_for_minimization >= 0.0 && !lower_bound_changed) {
2368 if (reduced_cost_for_minimization <= 0.0 && !upper_bound_changed) {
2386 solution->dual_values[e_.row] = reduced_cost / e_.coeff;
2389 (!lower_bound_changed || !upper_bound_changed)) {
2390 new_constraint_status = lower_bound_changed
2394 if (e_.coeff < 0.0) {
2402 solution->constraint_statuses[e_.row] = new_constraint_status;
2405void SingletonPreprocessor::UpdateConstraintBoundsWithVariableBounds(
2407 Fractional lower_delta = -e.coeff * lp->variable_upper_bounds()[e.col];
2408 Fractional upper_delta = -e.coeff * lp->variable_lower_bounds()[e.col];
2409 if (e.coeff < 0.0) {
2410 std::swap(lower_delta, upper_delta);
2412 lp->SetConstraintBounds(e.row,
2413 lp->constraint_lower_bounds()[e.row] + lower_delta,
2414 lp->constraint_upper_bounds()[e.row] + upper_delta);
2417bool SingletonPreprocessor::IntegerSingletonColumnIsRemovable(
2420 DCHECK(lp.IsVariableInteger(matrix_entry.col));
2421 const SparseMatrix& transpose = lp.GetTransposeSparseMatrix();
2422 for (
const SparseColumn::Entry entry :
2429 const Fractional coefficient = entry.coefficient();
2430 const Fractional coefficient_ratio = coefficient / matrix_entry.coeff;
2439 lp.constraint_lower_bounds()[matrix_entry.row];
2441 const Fractional lower_bound_ratio = constraint_lb / matrix_entry.coeff;
2449 lp.constraint_upper_bounds()[matrix_entry.row];
2451 const Fractional upper_bound_ratio = constraint_ub / matrix_entry.coeff;
2461void SingletonPreprocessor::DeleteZeroCostSingletonColumn(
2466 const SparseColumn& row_as_col = transpose.column(transpose_col);
2467 rows_saver_.SaveColumnIfNotAlreadyDone(
RowToColIndex(e.row), row_as_col);
2468 UpdateConstraintBoundsWithVariableBounds(e, lp);
2469 column_deletion_helper_.MarkColumnForDeletion(e.col);
2473void SingletonUndo::ZeroCostSingletonColumnUndo(
2480 if (variable_upper_bound_ == variable_lower_bound_) {
2481 solution->primal_values[e_.col] = variable_lower_bound_;
2488 const Fractional corrected_dual = is_maximization_
2491 if (corrected_dual > 0) {
2492 DCHECK(
IsFinite(variable_lower_bound_));
2493 solution->primal_values[e_.col] = variable_lower_bound_;
2496 DCHECK(
IsFinite(variable_upper_bound_));
2497 solution->primal_values[e_.col] = variable_upper_bound_;
2505 DCHECK(
IsFinite(variable_lower_bound_));
2506 solution->primal_values[e_.col] = variable_lower_bound_;
2509 DCHECK(
IsFinite(variable_upper_bound_));
2510 solution->primal_values[e_.col] = variable_upper_bound_;
2513 if (constraint_upper_bound_ == constraint_lower_bound_) {
2527 const Fractional tolerance = parameters.preprocessor_zero_tolerance();
2528 const auto is_smaller_with_tolerance = [tolerance](
Fractional a,
2530 return ::operations_research::IsSmallerWithinTolerance(a, b, tolerance);
2532 if (variable_lower_bound_ != -
kInfinity) {
2534 activity + e_.coeff * variable_lower_bound_;
2535 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_lb) &&
2536 is_smaller_with_tolerance(activity_at_lb, constraint_upper_bound_)) {
2537 solution->primal_values[e_.col] = variable_lower_bound_;
2542 if (variable_upper_bound_ !=
kInfinity) {
2544 activity + e_.coeff * variable_upper_bound_;
2545 if (is_smaller_with_tolerance(constraint_lower_bound_, activity_at_ub) &&
2546 is_smaller_with_tolerance(activity_at_ub, constraint_upper_bound_)) {
2547 solution->primal_values[e_.col] = variable_upper_bound_;
2556 if (constraint_lower_bound_ == -
kInfinity &&
2558 solution->primal_values[e_.col] = 0.0;
2566 if (constraint_lower_bound_ == constraint_upper_bound_) {
2568 (constraint_lower_bound_ - activity) / e_.coeff;
2573 bool set_constraint_to_lower_bound;
2574 if (constraint_lower_bound_ == -
kInfinity) {
2575 set_constraint_to_lower_bound =
false;
2576 }
else if (constraint_upper_bound_ ==
kInfinity) {
2577 set_constraint_to_lower_bound =
true;
2581 const Fractional to_lb = (constraint_lower_bound_ - activity) / e_.coeff;
2582 const Fractional to_ub = (constraint_upper_bound_ - activity) / e_.coeff;
2583 set_constraint_to_lower_bound =
2584 std::max(variable_lower_bound_ - to_lb, to_lb - variable_upper_bound_) <
2585 std::max(variable_lower_bound_ - to_ub, to_ub - variable_upper_bound_);
2588 if (set_constraint_to_lower_bound) {
2590 (constraint_lower_bound_ - activity) / e_.coeff;
2594 (constraint_upper_bound_ - activity) / e_.coeff;
2599void SingletonPreprocessor::DeleteSingletonColumnInEquality(
2603 const SparseColumn& row_as_column = transpose.column(transpose_col);
2607 rows_saver_.SaveColumnIfNotAlreadyDone(
RowToColIndex(e.row), row_as_column);
2614 const Fractional rhs = lp->constraint_upper_bounds()[e.row];
2615 const Fractional cost = lp->objective_coefficients()[e.col];
2616 const Fractional multiplier = cost / e.coeff;
2617 lp->SetObjectiveOffset(lp->objective_offset() + rhs * multiplier);
2618 for (
const SparseColumn::Entry e : row_as_column) {
2620 if (!column_deletion_helper_.IsColumnMarked(col)) {
2622 lp->objective_coefficients()[col] - e.coefficient() * multiplier;
2629 if (std::abs(new_cost) <
parameters_.preprocessor_zero_tolerance()) {
2632 lp->SetObjectiveCoefficient(col, new_cost);
2637 UpdateConstraintBoundsWithVariableBounds(e, lp);
2638 column_deletion_helper_.MarkColumnForDeletion(e.col);
2641void SingletonUndo::SingletonColumnInEqualityUndo(
2645 ZeroCostSingletonColumnUndo(parameters, saved_row,
solution);
2649 solution->dual_values[e_.row] += cost_ / e_.coeff;
2656void SingletonUndo::MakeConstraintAnEqualityUndo(
2659 solution->constraint_statuses[e_.row] = constraint_status_;
2663bool SingletonPreprocessor::MakeConstraintAnEqualityIfPossible(
2667 const Fractional cst_lower_bound = lp->constraint_lower_bounds()[e.row];
2668 const Fractional cst_upper_bound = lp->constraint_upper_bounds()[e.row];
2669 if (cst_lower_bound == cst_upper_bound)
return true;
2681 const DenseRow& variable_ubs = lp->variable_upper_bounds();
2682 const DenseRow& variable_lbs = lp->variable_lower_bounds();
2683 if (e.row >= row_sum_is_cached_.size() || !row_sum_is_cached_[e.row]) {
2684 if (e.row >= row_sum_is_cached_.size()) {
2685 const int new_size = e.row.value() + 1;
2686 row_sum_is_cached_.resize(new_size);
2687 row_lb_sum_.resize(new_size);
2688 row_ub_sum_.resize(new_size);
2690 row_sum_is_cached_[e.row] =
true;
2691 row_lb_sum_[e.row].Add(cst_lower_bound);
2692 row_ub_sum_[e.row].Add(cst_upper_bound);
2693 for (
const SparseColumn::Entry entry :
2703 if (column_deletion_helper_.IsColumnMarked(row_as_col))
continue;
2704 if (entry.coefficient() > 0.0) {
2705 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2706 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2708 row_lb_sum_[e.row].Add(-entry.coefficient() * variable_lbs[row_as_col]);
2709 row_ub_sum_[e.row].Add(-entry.coefficient() * variable_ubs[row_as_col]);
2721 c > 0.0 ? row_lb_sum_[e.row].SumWithoutLb(-c * variable_ubs[e.col]) /
c
2722 : row_ub_sum_[e.row].SumWithoutUb(-c * variable_ubs[e.col]) /
c;
2724 c > 0.0 ? row_ub_sum_[e.row].SumWithoutUb(-c * variable_lbs[e.col]) /
c
2725 : row_lb_sum_[e.row].SumWithoutLb(-c * variable_lbs[e.col]) /
c;
2731 lp->GetObjectiveCoefficientForMinimizationVersion(e.col);
2732 DCHECK_NE(cost, 0.0);
2738 ub, lp->variable_upper_bounds()[e.col])) {
2744 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2751 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2756 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2758 << e.col <<
" has a cost (for minimization) of " << cost
2759 <<
" and is unbounded towards kInfinity.";
2777 lp->SetVariableBounds(e.col, lp->variable_lower_bounds()[e.col],
kInfinity);
2780 lp->variable_lower_bounds()[e.col], lb)) {
2786 lp->SetConstraintBounds(e.row, cst_lower_bound, cst_lower_bound);
2793 lp->SetConstraintBounds(e.row, cst_upper_bound, cst_upper_bound);
2799 VLOG(1) <<
"Problem ProblemStatus::INFEASIBLE_OR_UNBOUNDED, singleton "
2801 << e.col <<
" has a cost (for minimization) of " << cost
2802 <<
" and is unbounded towards -kInfinity.";
2807 lp->SetVariableBounds(e.col, -
kInfinity,
2808 lp->variable_upper_bounds()[e.col]);
2811 if (lp->constraint_lower_bounds()[e.row] ==
2812 lp->constraint_upper_bounds()[e.row]) {
2813 undo_stack_.push_back(SingletonUndo(
2823 const SparseMatrix& matrix = lp->GetSparseMatrix();
2824 const SparseMatrix& transpose = lp->GetTransposeSparseMatrix();
2827 ColIndex num_cols(matrix.num_cols());
2828 RowIndex num_rows(matrix.num_rows());
2829 StrictITIVector<ColIndex, EntryIndex> column_degree(num_cols, EntryIndex(0));
2830 std::vector<ColIndex> column_to_process;
2831 for (ColIndex col(0); col < num_cols; ++col) {
2832 column_degree[col] = matrix.column(col).num_entries();
2833 if (column_degree[col] == 1) {
2834 column_to_process.push_back(col);
2839 StrictITIVector<RowIndex, EntryIndex> row_degree(num_rows, EntryIndex(0));
2840 std::vector<RowIndex> row_to_process;
2841 for (RowIndex row(0); row < num_rows; ++row) {
2842 row_degree[row] = transpose.column(
RowToColIndex(row)).num_entries();
2843 if (row_degree[row] == 1) {
2844 row_to_process.push_back(row);
2850 (!column_to_process.empty() || !row_to_process.empty())) {
2852 const ColIndex col = column_to_process.back();
2853 column_to_process.pop_back();
2854 if (column_degree[col] <= 0)
continue;
2855 const MatrixEntry e = GetSingletonColumnMatrixEntry(col, matrix);
2857 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2863 if (lp->objective_coefficients()[col] == 0.0) {
2864 DeleteZeroCostSingletonColumn(transpose, e, lp);
2871 if (MakeConstraintAnEqualityIfPossible(transpose, e, lp)) {
2872 DeleteSingletonColumnInEquality(transpose, e, lp);
2877 --row_degree[e.
row];
2878 if (row_degree[e.
row] == 1) {
2879 row_to_process.push_back(e.
row);
2883 const RowIndex row = row_to_process.back();
2884 row_to_process.pop_back();
2885 if (row_degree[row] <= 0)
continue;
2886 const MatrixEntry e = GetSingletonRowMatrixEntry(row, transpose);
2892 !IntegerSingletonColumnIsRemovable(e, *lp)) {
2896 DeleteSingletonRow(e, lp);
2897 --column_degree[e.
col];
2898 if (column_degree[e.
col] == 1) {
2899 column_to_process.push_back(e.
col);
2907 return !column_deletion_helper_.
IsEmpty() || !row_deletion_helper_.
IsEmpty();
2925 for (
int i = undo_stack_.size() - 1; i >= 0; --i) {
2934MatrixEntry SingletonPreprocessor::GetSingletonColumnMatrixEntry(
2936 for (
const SparseColumn::Entry e : matrix.column(col)) {
2937 if (!row_deletion_helper_.IsRowMarked(e.row())) {
2938 DCHECK_NE(0.0, e.coefficient());
2939 return MatrixEntry(e.row(), col, e.coefficient());
2944 LOG(DFATAL) <<
"No unmarked entry in a column that is supposed to have one.";
2946 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2949MatrixEntry SingletonPreprocessor::GetSingletonRowMatrixEntry(
2950 RowIndex row,
const SparseMatrix& transpose) {
2951 for (
const SparseColumn::Entry e : transpose.column(
RowToColIndex(row))) {
2954 DCHECK_NE(0.0, e.coefficient());
2955 return MatrixEntry(row, col, e.coefficient());
2960 LOG(DFATAL) <<
"No unmarked entry in a row that is supposed to have one.";
2962 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
2972 const ColIndex num_cols = lp->num_variables();
2973 if (num_cols == 0)
return false;
2975 changed_columns_.clear();
2976 int num_singletons = 0;
2977 for (ColIndex col(0); col < num_cols; ++col) {
2978 SparseColumn* sparse_column = lp->GetMutableSparseColumn(col);
2979 const Fractional cost = lp->objective_coefficients()[col];
2980 if (sparse_column->num_entries() == 1) {
2983 if (sparse_column->num_entries() == 1 &&
2984 sparse_column->GetFirstCoefficient() < 0) {
2985 sparse_column->MultiplyByConstant(-1.0);
2986 lp->SetVariableBounds(col, -lp->variable_upper_bounds()[col],
2987 -lp->variable_lower_bounds()[col]);
2988 lp->SetObjectiveCoefficient(col, -cost);
2989 changed_columns_.push_back(col);
2992 VLOG(1) <<
"Changed the sign of " << changed_columns_.size() <<
" columns.";
2993 VLOG(1) << num_singletons <<
" singleton columns left.";
2994 return !changed_columns_.empty();
3001 for (
int i = 0; i < changed_columns_.size(); ++i) {
3002 const ColIndex col = changed_columns_[i];
3025 saved_row_lower_bounds_ = lp->constraint_lower_bounds();
3026 saved_row_upper_bounds_ = lp->constraint_upper_bounds();
3029 saved_objective_ = lp->objective_coefficients();
3032 const SparseMatrix& original_transpose = lp->GetTransposeSparseMatrix();
3039 std::vector<std::pair<int64_t, RowIndex>> sorted_rows;
3040 const RowIndex num_rows(lp->num_constraints());
3041 for (RowIndex row(0); row < num_rows; ++row) {
3045 lp->constraint_lower_bounds()[row] !=
3046 lp->constraint_upper_bounds()[row]) {
3050 for (
const SparseColumn::Entry e : original_row) {
3052 score += lp->GetSparseColumn(col).num_entries().value();
3054 sorted_rows.push_back({score, row});
3056 std::sort(sorted_rows.begin(), sorted_rows.end());
3062 for (
const auto p : sorted_rows) {
3063 const RowIndex row = p.second;
3073 int entry_index = 0;
3074 for (
const SparseColumn::Entry e : original_row) {
3077 r.col[entry_index] = col;
3078 r.coeff[entry_index] = e.coefficient();
3079 DCHECK_NE(0.0, r.coeff[entry_index]);
3086 if (entry_index < 2)
continue;
3091 r.rhs = lp->constraint_lower_bounds()[row];
3092 for (
int col_choice = 0; col_choice < NUM_DOUBLETON_COLS; ++col_choice) {
3093 const ColIndex col = r.col[col_choice];
3094 r.lb[col_choice] = lp->variable_lower_bounds()[col];
3095 r.ub[col_choice] = lp->variable_upper_bounds()[col];
3096 r.objective_coefficient[col_choice] = lp->objective_coefficients()[col];
3101 if (r.lb[DELETED] == r.ub[DELETED] || r.lb[MODIFIED] == r.ub[MODIFIED]) {
3118 const Fractional carry_over_offset = r.rhs / r.coeff[MODIFIED];
3120 -r.coeff[DELETED] / r.coeff[MODIFIED];
3122 carry_over_factor == 0.0) {
3130 r.lb[DELETED] * carry_over_factor + carry_over_offset;
3132 r.ub[DELETED] * carry_over_factor + carry_over_offset;
3133 if (carry_over_factor < 0) {
3134 std::swap(carried_over_lb, carried_over_ub);
3136 if (carried_over_lb <= lb) {
3141 lb = carried_over_lb;
3146 carry_over_factor > 0 ? r.lb[DELETED] : r.ub[DELETED]);
3148 if (carried_over_ub >= ub) {
3153 ub = carried_over_ub;
3158 carry_over_factor > 0 ? r.ub[DELETED] : r.lb[DELETED]);
3164 lp->SetVariableBounds(r.col[MODIFIED], lb, ub);
3167 restore_stack_.push_back(r);
3174 DCHECK_NE(r.coeff[DELETED], 0.0);
3176 -r.coeff[MODIFIED] / r.coeff[DELETED];
3177 const Fractional constant_offset_factor = r.rhs / r.coeff[DELETED];
3179 if (!
IsFinite(substitution_factor) || substitution_factor == 0.0 ||
3180 !
IsFinite(constant_offset_factor)) {
3188 for (
const int col_choice : {DELETED, MODIFIED}) {
3189 const ColIndex col = r.col[col_choice];
3190 columns_saver_.SaveColumnIfNotAlreadyDone(col, lp->GetSparseColumn(col));
3193 lp->GetSparseColumn(r.col[DELETED])
3194 .AddMultipleToSparseVectorAndDeleteCommonIndex(
3195 substitution_factor, r.row,
parameters_.drop_tolerance(),
3196 lp->GetMutableSparseColumn(r.col[MODIFIED]));
3203 r.objective_coefficient[MODIFIED] +
3204 substitution_factor * r.objective_coefficient[DELETED];
3205 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
3206 lp->SetObjectiveCoefficient(r.col[MODIFIED], new_objective);
3208 lp->SetObjectiveCoefficient(r.col[MODIFIED], 0.0);
3215 SubtractColumnMultipleFromConstraintBound(r.col[DELETED],
3216 constant_offset_factor, lp);
3222 lp->GetMutableSparseColumn(r.col[DELETED])->ClearAndRelease();
3225 column_deletion_helper_.MarkColumnForDeletion(r.col[DELETED]);
3226 row_deletion_helper_.MarkRowForDeletion(r.row);
3229 lp->DeleteColumns(column_deletion_helper_.GetMarkedColumns());
3230 lp->DeleteRows(row_deletion_helper_.GetMarkedRows());
3232 return !column_deletion_helper_.IsEmpty();
3239 column_deletion_helper_.RestoreDeletedColumns(
solution);
3240 row_deletion_helper_.RestoreDeletedRows(
solution);
3242 const ColIndex num_cols =
solution->variable_statuses.size();
3243 StrictITIVector<ColIndex, bool> new_basic_columns(num_cols,
false);
3245 for (
const RestoreInfo& r :
Reverse(restore_stack_)) {
3246 switch (
solution->variable_statuses[r.col[MODIFIED]]) {
3248 LOG(DFATAL) <<
"FIXED variable produced by DoubletonPreprocessor!";
3255 ABSL_FALLTHROUGH_INTENDED;
3260 new_basic_columns[r.col[DELETED]] =
true;
3263 ABSL_FALLTHROUGH_INTENDED;
3271 ? r.bound_backtracking_at_lower_bound
3272 : r.bound_backtracking_at_upper_bound;
3273 const ColIndex bounded_var = r.col[bound_backtracking.col_choice];
3274 const ColIndex basic_var =
3275 r.col[OtherColChoice(bound_backtracking.col_choice)];
3276 solution->variable_statuses[bounded_var] = bound_backtracking.status;
3277 solution->primal_values[bounded_var] = bound_backtracking.value;
3279 new_basic_columns[basic_var] =
true;
3288 solution->primal_values[r.col[DELETED]] =
3290 solution->primal_values[r.col[MODIFIED]] * r.coeff[MODIFIED]) /
3318 StrictITIVector<ColIndex, std::set<int>> col_to_index(num_cols);
3319 for (
int i = 0;
i < restore_stack_.size(); ++
i) {
3320 const RestoreInfo& r = restore_stack_[
i];
3321 col_to_index[r.col[MODIFIED]].insert(i);
3322 col_to_index[r.col[DELETED]].insert(i);
3324 std::vector<ColIndex> singleton_col;
3325 for (ColIndex col(0); col < num_cols; ++col) {
3326 if (!new_basic_columns[col])
continue;
3327 if (col_to_index[col].size() == 1) singleton_col.push_back(col);
3329 while (!singleton_col.empty()) {
3330 const ColIndex col = singleton_col.back();
3331 singleton_col.pop_back();
3332 if (!new_basic_columns[col])
continue;
3333 if (col_to_index[col].empty())
continue;
3334 CHECK_EQ(col_to_index[col].size(), 1);
3335 const int index = *col_to_index[col].begin();
3336 const RestoreInfo& r = restore_stack_[index];
3338 const ColChoice col_choice = r.col[MODIFIED] == col ? MODIFIED : DELETED;
3342 CHECK_EQ(
solution->dual_values[r.row], 0.0);
3344 columns_saver_.SavedColumn(r.col[col_choice]);
3346 saved_objective_[r.col[col_choice]] -
3348 solution->dual_values[r.row] = current_reduced_cost / r.coeff[col_choice];
3351 col_to_index[r.col[DELETED]].erase(index);
3352 col_to_index[r.col[MODIFIED]].erase(index);
3353 if (col_to_index[r.col[DELETED]].size() == 1) {
3354 singleton_col.push_back(r.col[DELETED]);
3356 if (col_to_index[r.col[MODIFIED]].size() == 1) {
3357 singleton_col.push_back(r.col[MODIFIED]);
3363 saved_row_upper_bounds_,
solution);
3369 const RowIndex num_rows =
solution->constraint_statuses.size();
3370 DCHECK_EQ(row_lower_bounds.size(), num_rows);
3371 DCHECK_EQ(row_upper_bounds.size(), num_rows);
3372 for (RowIndex row(0); row < num_rows; ++row) {
3376 if (row_lower_bounds[row] == row_upper_bounds[row])
continue;
3380 if (
solution->dual_values[row] > 0) {
3388void DoubletonEqualityRowPreprocessor::
3389 SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r) {
3391 swap(r->col[DELETED], r->col[MODIFIED]);
3392 swap(r->coeff[DELETED], r->coeff[MODIFIED]);
3393 swap(r->lb[DELETED], r->lb[MODIFIED]);
3394 swap(r->ub[DELETED], r->ub[MODIFIED]);
3395 swap(r->objective_coefficient[DELETED], r->objective_coefficient[MODIFIED]);
3433 if (1.0 * primal_num_rows_.value() <
3444 variable_lower_bounds_.assign(num_cols, 0.0);
3445 variable_upper_bounds_.assign(num_cols, 0.0);
3446 for (ColIndex col(0); col < num_cols; ++col) {
3451 variable_lower_bounds_[col] = lower;
3452 variable_upper_bounds_[col] = upper;
3453 const Fractional value = MinInMagnitudeOrZeroIfInfinite(lower, upper);
3456 SubtractColumnMultipleFromConstraintBound(col, value, lp);
3464 dual_status_correspondence_.clear();
3465 for (RowIndex row(0); row < primal_num_rows_; ++row) {
3468 if (lower_bound == upper_bound) {
3475 LOG(DFATAL) <<
"There should be no free constraint in this lp.";
3478 slack_or_surplus_mapping_.clear();
3479 for (ColIndex col(0); col < primal_num_cols_; ++col) {
3483 dual_status_correspondence_.push_back(
3486 slack_or_surplus_mapping_.push_back(col);
3489 for (ColIndex col(0); col < primal_num_cols_; ++col) {
3493 dual_status_correspondence_.push_back(
3496 slack_or_surplus_mapping_.push_back(col);
3510 dual.PopulateFromDual(*lp, &duplicated_rows_);
3522 DenseRow new_primal_values(primal_num_cols_, 0.0);
3526 for (ColIndex col(0); col < primal_num_cols_; ++col) {
3528 const Fractional lower = variable_lower_bounds_[col];
3529 const Fractional upper = variable_upper_bounds_[col];
3533 const Fractional shift = MinInMagnitudeOrZeroIfInfinite(lower, upper);
3534 new_primal_values[col] =
solution->dual_values[row] + shift;
3544 new_variable_statuses[col] = ComputeVariableStatus(shift, lower, upper);
3553 const ColIndex
end = dual_status_correspondence_.size();
3555 DCHECK_EQ(
end -
begin, slack_or_surplus_mapping_.size());
3556 for (ColIndex index(
begin); index <
end; ++index) {
3558 const ColIndex col = slack_or_surplus_mapping_[index -
begin];
3563 new_variable_statuses[col] =
status;
3566 new_primal_values[col] = variable_upper_bounds_[col];
3569 new_primal_values[col] = variable_lower_bounds_[col];
3577 DenseColumn new_dual_values(primal_num_rows_, 0.0);
3584 Fractional sign = primal_is_maximization_problem_ ? -1 : 1;
3585 for (RowIndex row(0); row < primal_num_rows_; ++row) {
3587 new_dual_values[row] = sign *
solution->primal_values[col];
3593 if (
solution->variable_statuses[duplicated_rows_[row]] ==
3603 new_constraint_statuses[row] =
3610 new_dual_values[row] +=
3611 sign *
solution->primal_values[duplicated_rows_[row]];
3616 DCHECK(new_dual_values[row] == 0 ||
3621 new_primal_values.swap(
solution->primal_values);
3623 new_variable_statuses.swap(
solution->variable_statuses);
3624 new_constraint_statuses.
swap(
solution->constraint_statuses);
3656 bool all_variable_domains_contain_zero =
true;
3657 const ColIndex num_cols = lp->num_variables();
3658 variable_initial_lbs_.assign(num_cols, 0.0);
3659 variable_initial_ubs_.assign(num_cols, 0.0);
3660 for (ColIndex col(0); col < num_cols; ++col) {
3661 variable_initial_lbs_[col] = lp->variable_lower_bounds()[col];
3662 variable_initial_ubs_[col] = lp->variable_upper_bounds()[col];
3663 if (0.0 < variable_initial_lbs_[col] || 0.0 > variable_initial_ubs_[col]) {
3664 all_variable_domains_contain_zero =
false;
3667 VLOG(1) <<
"Maximum variable bounds magnitude (before shift): "
3668 << ComputeMaxVariableBoundsMagnitude(*lp);
3671 if (all_variable_domains_contain_zero)
return false;
3675 int num_bound_shifts = 0;
3676 const RowIndex num_rows = lp->num_constraints();
3679 offsets_.
assign(num_cols, 0.0);
3680 for (ColIndex col(0); col < num_cols; ++col) {
3681 if (0.0 < variable_initial_lbs_[col] || 0.0 > variable_initial_ubs_[col]) {
3682 Fractional offset = MinInMagnitudeOrZeroIfInfinite(
3683 variable_initial_lbs_[col], variable_initial_ubs_[col]);
3691 offset = trunc(offset);
3693 DCHECK_NE(offset, 0.0);
3695 offsets_[col] = offset;
3696 lp->SetVariableBounds(col, variable_initial_lbs_[col] - offset,
3697 variable_initial_ubs_[col] - offset);
3698 const SparseColumn& sparse_column = lp->GetSparseColumn(col);
3699 for (
const SparseColumn::Entry e : sparse_column) {
3700 row_offsets[e.row()].Add(e.coefficient() * offset);
3702 objective_offset.Add(lp->objective_coefficients()[col] * offset);
3706 VLOG(1) <<
"Maximum variable bounds magnitude (after " << num_bound_shifts
3707 <<
" shifts): " << ComputeMaxVariableBoundsMagnitude(*lp);
3710 for (RowIndex row(0); row < num_rows; ++row) {
3711 if (!std::isfinite(row_offsets[row].
Value())) {
3714 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3720 lp->SetConstraintBounds(
3721 row, lp->constraint_lower_bounds()[row] - row_offsets[row].Value(),
3722 lp->constraint_upper_bounds()[row] - row_offsets[row].Value());
3724 if (!std::isfinite(objective_offset.Value())) {
3725 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3726 "for the objective.";
3730 lp->SetObjectiveOffset(lp->objective_offset() + objective_offset.Value());
3738 const ColIndex num_cols =
solution->variable_statuses.size();
3739 for (ColIndex col(0); col < num_cols; ++col) {
3741 solution->primal_values[col] += offsets_[col];
3743 switch (
solution->variable_statuses[col]) {
3745 ABSL_FALLTHROUGH_INTENDED;
3747 solution->primal_values[col] = variable_initial_lbs_[col];
3750 solution->primal_values[col] = variable_initial_ubs_[col];
3753 solution->primal_values[col] += offsets_[col];
3772 const ColIndex num_cols = lp->num_variables();
3773 variable_lower_bounds_.
assign(num_cols, 0.0);
3774 variable_upper_bounds_.
assign(num_cols, 0.0);
3775 for (ColIndex col(0); col < num_cols; ++col) {
3776 variable_lower_bounds_[col] = lp->variable_lower_bounds()[col];
3777 variable_upper_bounds_[col] = lp->variable_upper_bounds()[col];
3784 bound_scaling_factor_ = lp->ScaleBounds();
3794 for (ColIndex col(0); col <
solution->primal_values.size(); ++col) {
3795 solution->primal_values[col] *= bound_scaling_factor_;
3799 for (RowIndex row(0); row <
solution->dual_values.size(); ++row) {
3800 solution->dual_values[row] *= cost_scaling_factor_;
3806 const ColIndex num_cols =
solution->primal_values.size();
3807 for (ColIndex col(0); col < num_cols; ++col) {
3808 switch (
solution->variable_statuses[col]) {
3810 ABSL_FALLTHROUGH_INTENDED;
3812 solution->primal_values[col] = variable_upper_bounds_[col];
3815 solution->primal_values[col] = variable_lower_bounds_[col];
3818 ABSL_FALLTHROUGH_INTENDED;
3856 lp->AddSlackVariablesWhereNecessary(
3858 first_slack_col_ = lp->GetFirstSlackVariable();
3868 const RowIndex num_rows =
solution->dual_values.size();
3869 for (RowIndex row(0); row < num_rows; ++row) {
3870 const ColIndex slack_col = first_slack_col_ +
RowToColIndex(row);
3872 solution->variable_statuses[slack_col];
3876 switch (variable_status) {
3887 solution->constraint_statuses[row] = constraint_status;
3891 solution->primal_values.resize(first_slack_col_, 0.0);
void EnableLogging(bool enable)
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
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
double preprocessor_zero_tolerance() const
static constexpr SolverBehavior LET_SOLVER_DECIDE
static constexpr SolverBehavior NEVER_DO
double dualizer_threshold() const
::operations_research::glop::GlopParameters_SolverBehavior solve_dual_problem() const
double drop_tolerance() const
::operations_research::glop::GlopParameters_CostScalingAlgorithm cost_scaling() const
::operations_research::glop::GlopParameters_ScalingAlgorithm scaling_method() const
bool log_search_progress() const
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 DeleteColumns(const DenseBooleanRow &columns_to_delete)
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
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)
std::unique_ptr< TimeLimit > infinite_time_limit_
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)
void Undo(const GlopParameters ¶meters, const SparseColumn &saved_column, const SparseColumn &saved_row, ProblemSolution *solution) const
@ MAKE_CONSTRAINT_AN_EQUALITY
@ SINGLETON_COLUMN_IN_EQUALITY
@ ZERO_COST_SINGLETON_COLUMN
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 GetFirstCoefficient() const
Fractional LookUpCoefficient(Index index) const
void assign(IntType size, const T &v)
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)
ReverseView< Container > reversed_view(const Container &c)
constexpr ColIndex kInvalidCol(-1)
StrictITIVector< RowIndex, ColIndex > RowToColMapping
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
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)
constexpr Fractional kInfinity
Infinity for type Fractional.
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)
static constexpr double kInfinity
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
ClosedInterval::Iterator end(ClosedInterval interval)
util_intops::StrongVector< ColumnEntryIndex, ElementIndex > SparseColumn
ClosedInterval::Iterator begin(ClosedInterval interval)
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
#define RUN_PREPROCESSOR(name)
#define RETURN_IF_NULL(x)
#define RETURN_VALUE_IF_NULL(x, v)
#define SCOPED_INSTRUCTION_COUNT(time_limit)
Holds a triplet (row, col, coefficient).
Contains the solution of a LinearProgram as returned by a preprocessor.
#define SOLVER_LOG(logger,...)