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)
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();
152 preprocessor->SetTimeLimit(time_limit);
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)",
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;
381 for (ColIndex col(0); col < num_cols; ++col) {
382 max_bounds_magnitude = std::max(
383 max_bounds_magnitude,
387 return max_bounds_magnitude;
395 column_deletion_helper_.Clear();
397 for (ColIndex col(0); col < num_cols; ++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 <<
"]";
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;
846 row_factors_.assign(num_rows, 0.0);
847 for (RowIndex row(0); row < num_rows; ++row) {
849 if (!row_transpose.
IsEmpty()) {
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];
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])) {
938 row_deletion_helper_.UnmarkRow(lower_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];
964 std::swap(new_lb, new_ub);
967 lower_bound_sources_[new_representative] = new_representative;
968 upper_bound_sources_[new_representative] = new_representative;
971 lower_bound_sources_[new_representative] = other;
975 if (new_ub < lp->constraint_upper_bounds()[new_representative]) {
976 upper_bound_sources_[new_representative] = other;
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) {
1001 row_deletion_helper_.UnmarkRow(new_representative);
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] =
1089 for (ColIndex col(0); col < num_cols; ++col) {
1092 if (lower_bound == upper_bound) {
1097 SubtractColumnMultipleFromConstraintBound(col, fixed_value, lp);
1098 column_deletion_helper_.MarkColumnForDeletionWithState(
1104 return !column_deletion_helper_.
IsEmpty();
1128 for (ColIndex col(0); col < num_cols; ++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;
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.";
1205 costs_.resize(num_cols, 0.0);
1206 for (ColIndex col(0); col < num_cols; ++col) {
1210 bool is_forced =
false;
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);
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;
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;
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) {
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) {
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) {
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) {
1454 ++num_already_free_variables;
1457 if (lower_bound == upper_bound)
continue;
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;
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;
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) {
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]],
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);
1787 const bool is_unbounded_up = (target_bound ==
kInfinity);
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
1810 is_unbounded_[col] =
true;
1811 Fractional initial_feasible_value = MinInMagnitudeOrZeroIfInfinite(
1814 col, initial_feasible_value,
1815 ComputeVariableStatus(initial_feasible_value,
1838 for (RowIndex row(0); row < num_rows; ++row) {
1840 dual_ub_[row] = 0.0;
1843 dual_lb_[row] = 0.0;
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;
1878 rc_lb.Add(col_cost);
1879 rc_ub.Add(col_cost);
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;
1960 if (IsConstraintBlockingVariable(
1961 *lp, sign_correction * e.coefficient(), e.row())) {
1979 DCHECK(!can_be_removed);
1986 changed_rows.clear();
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()) {
2033 for (
const RowIndex row : changed_rows) {
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;
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
2161 for (RowIndex row(0); row < num_rows; ++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(
2409 if (e.coeff < 0.0) {
2410 std::swap(lower_delta, upper_delta);
2417bool SingletonPreprocessor::IntegerSingletonColumnIsRemovable(
2429 const Fractional coefficient = entry.coefficient();
2430 const Fractional coefficient_ratio = coefficient / matrix_entry.coeff;
2441 const Fractional lower_bound_ratio = constraint_lb / matrix_entry.coeff;
2451 const Fractional upper_bound_ratio = constraint_ub / matrix_entry.coeff;
2461void SingletonPreprocessor::DeleteZeroCostSingletonColumn(
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(
2604 undo_stack_.push_back(
2607 rows_saver_.SaveColumnIfNotAlreadyDone(
RowToColIndex(e.row), row_as_column);
2616 const Fractional multiplier = cost / e.coeff;
2620 if (!column_deletion_helper_.IsColumnMarked(col)) {
2629 if (std::abs(new_cost) <
parameters_.preprocessor_zero_tolerance()) {
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(
2669 if (cst_lower_bound == cst_upper_bound)
return true;
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);
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;
2732 DCHECK_NE(cost, 0.0);
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.";
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.";
2813 undo_stack_.push_back(SingletonUndo(
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) {
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) {
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)) {
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(
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);
2950MatrixEntry SingletonPreprocessor::GetSingletonRowMatrixEntry(
2955 DCHECK_NE(0.0, e.coefficient());
2956 return MatrixEntry(row, col, e.coefficient());
2961 LOG(DFATAL) <<
"No unmarked entry in a row that is supposed to have one.";
2963 return MatrixEntry(RowIndex(0), ColIndex(0), 0.0);
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) {
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];
3041 std::vector<std::pair<int64_t, RowIndex>> sorted_rows;
3043 for (RowIndex row(0); row < num_rows; ++row) {
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;
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;
3094 for (
int col_choice = 0; col_choice < NUM_DOUBLETON_COLS; ++col_choice) {
3095 const ColIndex col = r.col[col_choice];
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) {
3143 lb = carried_over_lb;
3148 carry_over_factor > 0 ? r.lb[DELETED] : r.ub[DELETED]);
3150 if (carried_over_ub >= ub) {
3155 ub = carried_over_ub;
3160 carry_over_factor > 0 ? r.ub[DELETED] : r.lb[DELETED]);
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];
3192 columns_saver_.SaveColumnIfNotAlreadyDone(col, lp->
GetSparseColumn(col));
3197 substitution_factor, r.row,
parameters_.drop_tolerance(),
3205 r.objective_coefficient[MODIFIED] +
3206 substitution_factor * r.objective_coefficient[DELETED];
3207 if (std::abs(new_objective) >
parameters_.drop_tolerance()) {
3217 SubtractColumnMultipleFromConstraintBound(r.col[DELETED],
3218 constant_offset_factor, lp);
3227 column_deletion_helper_.MarkColumnForDeletion(r.col[DELETED]);
3228 row_deletion_helper_.MarkRowForDeletion(r.row);
3231 lp->
DeleteColumns(column_deletion_helper_.GetMarkedColumns());
3232 lp->
DeleteRows(row_deletion_helper_.GetMarkedRows());
3234 return !column_deletion_helper_.IsEmpty();
3241 column_deletion_helper_.RestoreDeletedColumns(
solution);
3242 row_deletion_helper_.RestoreDeletedRows(
solution);
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;
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);
3346 columns_saver_.SavedColumn(r.col[col_choice]);
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;
3382 if (
solution->dual_values[row] > 0) {
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]);
3435 if (1.0 * primal_num_rows_.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;
3455 const Fractional value = MinInMagnitudeOrZeroIfInfinite(lower, upper);
3458 SubtractColumnMultipleFromConstraintBound(col, value, lp);
3466 dual_status_correspondence_.clear();
3467 for (RowIndex row(0); row < primal_num_rows_; ++row) {
3470 if (lower_bound == upper_bound) {
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) {
3485 dual_status_correspondence_.push_back(
3488 slack_or_surplus_mapping_.push_back(col);
3491 for (ColIndex col(0); col < primal_num_cols_; ++col) {
3495 dual_status_correspondence_.push_back(
3498 slack_or_surplus_mapping_.push_back(col);
3524 DenseRow new_primal_values(primal_num_cols_, 0.0);
3528 for (ColIndex col(0); col < primal_num_cols_; ++col) {
3530 const Fractional lower = variable_lower_bounds_[col];
3531 const Fractional upper = variable_upper_bounds_[col];
3535 const Fractional shift = MinInMagnitudeOrZeroIfInfinite(lower, upper);
3536 new_primal_values[col] =
solution->dual_values[row] + shift;
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());
3558 for (ColIndex index(
begin); index <
end; ++index) {
3560 const ColIndex col = slack_or_surplus_mapping_[index -
begin];
3565 new_variable_statuses[col] =
status;
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) {
3589 new_dual_values[row] = sign *
solution->primal_values[col];
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;
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) {
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;
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;
3699 variable_initial_ubs_[col] - offset);
3702 row_offsets[e.row()].Add(e.coefficient() * 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 "
3726 if (!std::isfinite(objective_offset.Value())) {
3727 VLOG(1) <<
"Shifting variable bounds causes a floating point overflow "
3728 "for the objective.";
3740 const ColIndex num_cols =
solution->variable_statuses.size();
3741 for (ColIndex col(0); col < num_cols; ++col) {
3743 solution->primal_values[col] += offsets_[col];
3745 switch (
solution->variable_statuses[col]) {
3747 ABSL_FALLTHROUGH_INTENDED;
3749 solution->primal_values[col] = variable_initial_lbs_[col];
3752 solution->primal_values[col] = variable_initial_ubs_[col];
3755 solution->primal_values[col] += offsets_[col];
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) {
3796 for (ColIndex col(0); col <
solution->primal_values.size(); ++col) {
3797 solution->primal_values[col] *= bound_scaling_factor_;
3801 for (RowIndex row(0); row <
solution->dual_values.size(); ++row) {
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) {
3810 switch (
solution->variable_statuses[col]) {
3812 ABSL_FALLTHROUGH_INTENDED;
3814 solution->primal_values[col] = variable_upper_bounds_[col];
3817 solution->primal_values[col] = variable_lower_bounds_[col];
3820 ABSL_FALLTHROUGH_INTENDED;
3870 const RowIndex num_rows =
solution->dual_values.size();
3871 for (RowIndex row(0); row < num_rows; ++row) {
3872 const ColIndex slack_col = first_slack_col_ +
RowToColIndex(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);
const DenseRow & objective_coefficients() const
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
void SetObjectiveOffset(Fractional objective_offset)
void SetObjectiveCoefficient(ColIndex col, Fractional value)
const DenseColumn & constraint_lower_bounds() const
const SparseMatrix & GetTransposeSparseMatrix() const
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
const DenseRow & variable_lower_bounds() const
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
EntryIndex num_entries() const
bool IsMaximizationProblem() const
SparseColumn * GetMutableSparseColumn(ColIndex col)
const SparseMatrix & GetSparseMatrix() const
Fractional ScaleObjective(GlopParameters::CostScalingAlgorithm method)
void Swap(LinearProgram *linear_program)
void DeleteRows(const DenseBooleanColumn &rows_to_delete)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
const DenseRow & variable_upper_bounds() const
const DenseColumn & constraint_upper_bounds() const
const SparseColumn & GetSparseColumn(ColIndex col) const
bool IsVariableInteger(ColIndex col) const
ColIndex num_variables() const
ColIndex GetFirstSlackVariable() const
Fractional objective_offset() const
RowIndex num_constraints() const
void PopulateFromDual(const LinearProgram &dual, RowToColMapping *duplicated_rows)
const SparseColumn & column(ColIndex col) const
RowIndex num_rows() const
ColIndex num_cols() const
void EnableLogging(bool enable)
void RecoverSolution(ProblemSolution *solution) const final
bool Run(LinearProgram *lp) final
const DenseBooleanRow & GetMarkedColumns() const
void RestoreDeletedColumns(ProblemSolution *solution) const
bool IsColumnMarked(ColIndex col) const
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)
const SparseColumn & SavedColumn(ColIndex col) const
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
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
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
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
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
Fractional objective_offset() const
RowIndex num_constraints() const
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
void MarkRowForDeletion(RowIndex row)
bool IsRowMarked(RowIndex row) const
const DenseBooleanColumn & GetMarkedRows() const
void RestoreDeletedRows(ProblemSolution *solution) const
void UnmarkRow(RowIndex row)
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
SparseColumn * mutable_column(ColIndex col)
EntryIndex num_entries() const
Fractional GetFirstCoefficient() const
void AddMultipleToSparseVectorAndDeleteCommonIndex(Fractional multiplier, Index removed_common_index, Fractional drop_tolerance, SparseVector *accumulator_vector) const
typename Iterator::Entry Entry
void MultiplyByConstant(Fractional factor)
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
void swap(StrongVector &x) noexcept
void push_back(const value_type &val)
#define RUN_PREPROCESSOR(name)
SumWithOneMissing< true > SumWithPositiveInfiniteAndOneMissing
RowIndex ColToRowIndex(ColIndex col)
constexpr Fractional kInfinity
SumWithOneMissing< false > SumWithNegativeInfiniteAndOneMissing
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
StrictITIVector< ColIndex, ColIndex > ColMapping
StrictITIVector< RowIndex, ConstraintStatus > ConstraintStatusColumn
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
AccurateSum< Fractional > KahanSum
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
ColIndex RowToColIndex(RowIndex row)
bool IsFinite(Fractional value)
constexpr RowIndex kInvalidRow(-1)
RowIndex ColToRowIndex(ColIndex col)
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
void Scale(LinearProgram *lp, SparseMatrixScaler *scaler)
void FixConstraintWithFixedStatuses(const DenseColumn &row_lower_bounds, const DenseColumn &row_upper_bounds, ProblemSolution *solution)
constexpr Fractional kInfinity
StrictITIVector< RowIndex, Fractional > DenseColumn
StrictITIVector< ColIndex, Fractional > DenseRow
StrictITIVector< ColIndex, bool > DenseBooleanRow
@ INFEASIBLE_OR_UNBOUNDED
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
ColMapping FindProportionalColumns(const SparseMatrix &matrix, Fractional tolerance)
std::string GetProblemStatusString(ProblemStatus problem_status)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
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 RETURN_IF_NULL(x)
#define RETURN_VALUE_IF_NULL(x, v)
#define SCOPED_INSTRUCTION_COUNT(time_limit)
#define SOLVER_LOG(logger,...)