25#include "absl/strings/str_cat.h"
26#include "absl/strings/str_format.h"
41ABSL_FLAG(
bool, simplex_display_numbers_as_fractions,
false,
42 "Display numbers as fractions.");
43ABSL_FLAG(
bool, simplex_stop_after_first_basis,
false,
44 "Stop after first basis has been computed.");
45ABSL_FLAG(
bool, simplex_stop_after_feasibility,
false,
46 "Stop after first phase has been completed.");
47ABSL_FLAG(
bool, simplex_display_stats,
false,
"Display algorithm statistics.");
57 explicit Cleanup(std::function<
void()> closure)
58 : closure_(
std::move(closure)) {}
59 ~Cleanup() { closure_(); }
62 std::function<void()> closure_;
66#define DCHECK_COL_BOUNDS(col) \
69 DCHECK_GT(num_cols_, col); \
73#define DCHECK_ROW_BOUNDS(row) \
76 DCHECK_GT(num_rows_, row); \
89 random_(deterministic_random_),
90 basis_factorization_(&compact_matrix_, &basis_),
91 variables_info_(compact_matrix_),
92 primal_edge_norms_(compact_matrix_, variables_info_,
93 basis_factorization_),
94 dual_edge_norms_(basis_factorization_),
95 dual_prices_(random_),
96 variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
97 basis_factorization_, &dual_edge_norms_, &dual_prices_),
98 update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
99 basis_factorization_),
100 reduced_costs_(compact_matrix_,
objective_, basis_, variables_info_,
101 basis_factorization_, random_),
102 entering_variable_(variables_info_, random_, &reduced_costs_),
103 primal_prices_(random_, variables_info_, &primal_edge_norms_,
107 function_stats_(
"SimplexFunctionStats"),
116 variable_starting_values_.
clear();
121 solution_state_ = state;
122 solution_state_has_been_set_externally_ =
true;
127 variable_starting_values_ = values;
131 notify_that_matrix_is_unchanged_ =
true;
138 Cleanup update_deterministic_time_on_return(
143 const double start_time =
time_limit->GetElapsedTime();
146 dual_infeasibility_improvement_direction_.
clear();
150 phase_ = Phase::FEASIBILITY;
152 num_feasibility_iterations_ = 0;
153 num_optimization_iterations_ = 0;
154 num_push_iterations_ = 0;
155 feasibility_time_ = 0.0;
156 optimization_time_ = 0.0;
164 solution_state_has_been_set_externally_ =
true;
167 ComputeNumberOfEmptyRows();
168 ComputeNumberOfEmptyColumns();
169 DisplayBasicVariableStatistics();
172 if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
180 LOG(
INFO) <<
"------ " << (use_dual ?
"Dual simplex." :
"Primal simplex.");
181 LOG(
INFO) <<
"The matrix has " << compact_matrix_.
num_rows() <<
" rows, "
182 << compact_matrix_.
num_cols() <<
" columns, "
184 LOG(
INFO) <<
"Initial number of super-basic variables: "
185 << ComputeNumberOfSuperBasicVariables();
190 if (log_info)
LOG(
INFO) <<
"------ First phase: feasibility.";
200 DualMinimize(phase_ == Phase::FEASIBILITY,
time_limit));
201 DisplayIterationInfo();
218 MakeBoxedVariableDualFeasible(
233 if (initial_infeasibility <
235 if (log_info)
LOG(
INFO) <<
"Initial basis is dual feasible.";
237 MakeBoxedVariableDualFeasible(
253 DisplayIterationInfo();
261 variable_starting_values_);
276 if (log_info)
LOG(
INFO) <<
"Infeasible after first phase.";
284 DisplayIterationInfo();
288 InitializeObjectiveAndTestIfUnchanged(lp);
295 phase_ = Phase::OPTIMIZATION;
296 feasibility_time_ =
time_limit->GetElapsedTime() - start_time;
298 num_feasibility_iterations_ = num_iterations_;
300 if (log_info)
LOG(
INFO) <<
"------ Second phase: optimization.";
314 for (
int num_optims = 0;
319 !objective_limit_reached_ &&
320 (num_iterations_ == 0 ||
323 !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
333 DualMinimize(phase_ == Phase::FEASIBILITY,
time_limit));
344 if (!integrality_scale_.
empty() &&
359 DisplayIterationInfo();
377 LOG(
INFO) <<
"DUAL_UNBOUNDED was reported, but the residual and/or "
378 <<
"dual infeasibility is above the tolerance";
393 if (primal_residual > solution_tolerance ||
394 dual_residual > solution_tolerance) {
396 LOG(
INFO) <<
"OPTIMAL was reported, yet one of the residuals is "
397 "above the solution feasibility tolerance after the "
398 "shift/perturbation are removed.";
417 if (primal_infeasibility > primal_tolerance &&
418 dual_infeasibility > dual_tolerance) {
420 LOG(
INFO) <<
"OPTIMAL was reported, yet both of the infeasibility "
421 "are above the tolerance after the "
422 "shift/perturbation are removed.";
427 }
else if (primal_infeasibility > primal_tolerance) {
430 LOG(
INFO) <<
"The primal infeasibility is still higher than the "
431 "requested internal tolerance, but the maximum "
432 "number of optimization is reached.";
436 if (log_info)
LOG(
INFO) <<
"Re-optimizing with dual simplex ... ";
438 }
else if (dual_infeasibility > dual_tolerance) {
441 LOG(
INFO) <<
"The dual infeasibility is still higher than the "
442 "requested internal tolerance, but the maximum "
443 "number of optimization is reached.";
447 if (log_info)
LOG(
INFO) <<
"Re-optimizing with primal simplex ... ";
479 total_time_ =
time_limit->GetElapsedTime() - start_time;
480 optimization_time_ = total_time_ - feasibility_time_;
481 num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
485 if (!variable_starting_values_.
empty()) {
486 const int num_super_basic = ComputeNumberOfSuperBasicVariables();
487 if (num_super_basic > 0) {
489 LOG(
INFO) <<
"Num super-basic variables left after optimize phase: "
494 if (log_info)
LOG(
INFO) <<
"------ Third phase: push.";
495 phase_ = Phase::PUSH;
499 }
else if (log_info) {
500 LOG(
INFO) <<
"Skipping push phase because optimize didn't succeed.";
506 total_time_ =
time_limit->GetElapsedTime() - start_time;
507 push_time_ = total_time_ - feasibility_time_ - optimization_time_;
508 num_push_iterations_ = num_iterations_ - num_feasibility_iterations_ -
509 num_optimization_iterations_;
512 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
525 solution_objective_value_ =
529 solution_objective_value_ = -solution_objective_value_;
533 variable_starting_values_.
clear();
539 return problem_status_;
543 return solution_objective_value_;
547 return num_iterations_;
555 return variable_values_.
Get(
col);
559 return solution_reduced_costs_[
col];
563 return solution_reduced_costs_;
567 return solution_dual_values_[
row];
579 return -variable_values_.
Get(SlackColIndex(
row));
597 return solution_primal_ray_;
601 return solution_dual_ray_;
606 return solution_dual_ray_row_combination_;
613 return basis_factorization_;
616std::string RevisedSimplex::GetPrettySolverStats()
const {
617 return absl::StrFormat(
618 "Problem status : %s\n"
619 "Solving time : %-6.4g\n"
620 "Number of iterations : %u\n"
621 "Time for solvability (first phase) : %-6.4g\n"
622 "Number of iterations for solvability : %u\n"
623 "Time for optimization : %-6.4g\n"
624 "Number of iterations for optimization : %u\n"
625 "Stop after first basis : %d\n",
627 feasibility_time_, num_feasibility_iterations_, optimization_time_,
628 num_optimization_iterations_,
629 absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
642void RevisedSimplex::SetVariableNames() {
643 variable_name_.resize(num_cols_,
"");
644 for (ColIndex
col(0);
col < first_slack_col_; ++
col) {
645 const ColIndex var_index =
col + 1;
648 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
649 const ColIndex var_index =
col - first_slack_col_ + 1;
654void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
660bool RevisedSimplex::BasisIsConsistent()
const {
663 for (RowIndex
row(0);
row < num_rows_; ++
row) {
664 const ColIndex
col = basis_[
row];
665 if (!is_basic.IsSet(
col))
return false;
668 ColIndex cols_in_basis(0);
669 ColIndex cols_not_in_basis(0);
670 for (ColIndex
col(0);
col < num_cols_; ++
col) {
671 cols_in_basis += is_basic.IsSet(
col);
672 cols_not_in_basis += !is_basic.IsSet(
col);
673 if (is_basic.IsSet(
col) !=
679 if (cols_not_in_basis != num_cols_ -
RowToColIndex(num_rows_))
return false;
685void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
694 DCHECK_NE(basis_[basis_row], entering_col);
697 const ColIndex leaving_col = basis_[basis_row];
708 basis_[basis_row] = entering_col;
716class ColumnComparator {
719 bool operator()(ColIndex col_a, ColIndex col_b)
const {
720 return value_[col_a] < value_[col_b];
737void RevisedSimplex::UseSingletonColumnInInitialBasis(
RowToColMapping* basis) {
744 std::vector<ColIndex> singleton_column;
745 DenseRow cost_variation(num_cols_, 0.0);
748 for (ColIndex
col(0);
col < num_cols_; ++
col) {
753 cost_variation[
col] = objective_[
col] / std::abs(slope);
755 cost_variation[
col] = -objective_[
col] / std::abs(slope);
757 singleton_column.push_back(
col);
759 if (singleton_column.empty())
return;
766 ColumnComparator comparator(cost_variation);
767 std::sort(singleton_column.begin(), singleton_column.end(), comparator);
768 DCHECK_LE(cost_variation[singleton_column.front()],
769 cost_variation[singleton_column.back()]);
777 for (
const ColIndex
col : singleton_column) {
789 if (error_[
row] == 0.0)
continue;
814 error_[
row] -= coeff * box_width;
815 SetNonBasicVariableStatusAndDeriveValue(
col,
821 error_[
row] += coeff * box_width;
822 SetNonBasicVariableStatusAndDeriveValue(
col,
829bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
830 const LinearProgram& lp,
bool lp_is_in_equation_form,
831 bool* only_change_is_new_rows,
bool* only_change_is_new_cols,
832 ColIndex* num_new_cols) {
834 DCHECK(only_change_is_new_rows !=
nullptr);
835 DCHECK(only_change_is_new_cols !=
nullptr);
836 DCHECK(num_new_cols !=
nullptr);
841 const bool old_part_of_matrix_is_unchanged =
843 num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
846 const ColIndex lp_first_slack =
847 lp_is_in_equation_form ? lp.GetFirstSlackVariable() : lp.num_variables();
852 if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
853 lp_first_slack == first_slack_col_) {
859 *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
860 lp.num_constraints() > num_rows_ &&
861 lp_first_slack == first_slack_col_;
865 *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
866 lp.num_constraints() == num_rows_ &&
867 lp_first_slack > first_slack_col_;
868 *num_new_cols = *only_change_is_new_cols ? lp_first_slack - first_slack_col_
872 first_slack_col_ = lp_first_slack;
875 num_rows_ = lp.num_constraints();
876 num_cols_ = lp_first_slack +
RowToColIndex(lp.num_constraints());
881 if (lp_is_in_equation_form) {
896bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
897 const LinearProgram& lp,
bool lp_is_in_equation_form,
898 ColIndex num_new_cols) {
900 DCHECK_LE(num_new_cols, first_slack_col_);
901 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
906 for (ColIndex
col(0);
col < first_new_col; ++
col) {
914 for (ColIndex
col(first_new_col);
col < first_slack_col_; ++
col) {
915 if (lp.variable_lower_bounds()[
col] != 0.0 &&
916 lp.variable_upper_bounds()[
col] != 0.0) {
922 if (lp_is_in_equation_form) {
923 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
930 DCHECK_EQ(num_rows_, lp.num_constraints());
931 for (RowIndex
row(0);
row < num_rows_; ++
row) {
934 -lp.constraint_upper_bounds()[
row] ||
936 -lp.constraint_lower_bounds()[
row]) {
944bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
945 const LinearProgram& lp) {
948 bool objective_is_unchanged =
true;
949 objective_.
resize(num_cols_, 0.0);
953 DCHECK_GE(num_cols_, lp.num_variables());
954 for (ColIndex
col(lp.num_variables());
col < num_cols_; ++
col) {
955 if (objective_[
col] != 0.0) {
956 objective_is_unchanged =
false;
957 objective_[
col] = 0.0;
961 if (lp.IsMaximizationProblem()) {
963 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
965 if (objective_[
col] != coeff) {
966 objective_is_unchanged =
false;
967 objective_[
col] = coeff;
970 objective_offset_ = -lp.objective_offset();
971 objective_scaling_factor_ = -lp.objective_scaling_factor();
973 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
975 if (objective_[
col] != coeff) {
976 objective_is_unchanged =
false;
977 objective_[
col] = coeff;
980 objective_offset_ = lp.objective_offset();
981 objective_scaling_factor_ = lp.objective_scaling_factor();
984 return objective_is_unchanged;
987void RevisedSimplex::InitializeObjectiveLimit(
const LinearProgram& lp) {
988 objective_limit_reached_ =
false;
989 DCHECK(std::isfinite(objective_offset_));
990 DCHECK(std::isfinite(objective_scaling_factor_));
991 DCHECK_NE(0.0, objective_scaling_factor_);
994 for (
const bool set_dual : {
true,
false}) {
1006 const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
1010 limit / objective_scaling_factor_ - objective_offset_;
1012 dual_objective_limit_ = shifted_limit;
1014 primal_objective_limit_ = shifted_limit;
1024Status RevisedSimplex::CreateInitialBasis() {
1036 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1037 basis[
row] = SlackColIndex(
row);
1050 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1056 SetNonBasicVariableStatusAndDeriveValue(
col,
1060 SetNonBasicVariableStatusAndDeriveValue(
col,
1067 ComputeVariableValuesError();
1076 UseSingletonColumnInInitialBasis(&basis);
1079 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1081 basis[
row] = SlackColIndex(
row);
1088 return InitializeFirstBasis(basis);
1091 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1096 initial_basis.GetDualMarosBasis(num_cols_, &basis);
1098 initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1100 int number_changed = 0;
1101 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1102 if (basis[
row] != SlackColIndex(
row)) {
1106 VLOG(1) <<
"Number of Maros basis changes: " << number_changed;
1110 int num_fixed_variables = 0;
1111 for (RowIndex
row(0);
row < basis.size(); ++
row) {
1112 const ColIndex
col = basis[
row];
1115 ++num_fixed_variables;
1119 if (num_fixed_variables == 0) {
1121 <<
" but there is no equality rows to remove from initial all "
1125 VLOG(1) <<
"Trying to remove " << num_fixed_variables
1126 <<
" fixed variables from the initial basis.";
1127 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1132 initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1134 VLOG(1) <<
"Bixby initial basis algorithm requires the problem "
1135 <<
"to be scaled. Skipping Bixby's algorithm.";
1143 initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1145 initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1148 const Status status = InitializeFirstBasis(basis);
1152 VLOG(1) <<
"Reverting to all slack basis.";
1154 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1155 basis[
row] = SlackColIndex(
row);
1161 LOG(
WARNING) <<
"Unsupported initial_basis parameters: "
1165 return InitializeFirstBasis(basis);
1168Status RevisedSimplex::InitializeFirstBasis(
const RowToColMapping& basis) {
1174 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1176 basis_[
row] = SlackColIndex(
row);
1191 const std::string error_message =
1192 absl::StrCat(
"The matrix condition number upper bound is too high: ",
1193 condition_number_ub);
1194 VLOG(1) << error_message;
1199 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1202 DCHECK(BasisIsConsistent());
1213 VLOG(1) << absl::StrCat(
1214 "The primal residual of the initial basis is above the tolerance, ",
1221Status RevisedSimplex::Initialize(
const LinearProgram& lp) {
1222 parameters_ = initial_parameters_;
1223 PropagateParameters();
1231 const bool lp_is_in_equation_form = lp.IsInEquationForm();
1238 ColIndex num_new_cols(0);
1239 bool only_change_is_new_rows =
false;
1240 bool only_change_is_new_cols =
false;
1241 bool matrix_is_unchanged =
true;
1242 bool only_new_bounds =
false;
1243 if (solution_state_.
IsEmpty() || !notify_that_matrix_is_unchanged_) {
1244 matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1245 lp, lp_is_in_equation_form, &only_change_is_new_rows,
1246 &only_change_is_new_cols, &num_new_cols);
1247 only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1248 OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1249 lp, lp_is_in_equation_form, num_new_cols);
1251 CHECK(InitializeMatrixAndTestIfUnchanged(
1252 lp, lp_is_in_equation_form, &only_change_is_new_rows,
1253 &only_change_is_new_cols, &num_new_cols));
1255 notify_that_matrix_is_unchanged_ =
false;
1258 const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1260 const bool bounds_are_unchanged =
1261 lp_is_in_equation_form
1263 lp.variable_lower_bounds(), lp.variable_upper_bounds())
1265 lp.variable_lower_bounds(), lp.variable_upper_bounds(),
1266 lp.constraint_lower_bounds(), lp.constraint_upper_bounds());
1272 if (objective_is_unchanged && !bounds_are_unchanged) {
1274 PropagateParameters();
1276 if (bounds_are_unchanged && !objective_is_unchanged) {
1278 PropagateParameters();
1282 InitializeObjectiveLimit(lp);
1298 bool solve_from_scratch =
true;
1301 if (!solution_state_.
IsEmpty() && !solution_state_has_been_set_externally_) {
1307 dual_edge_norms_.
Clear();
1308 dual_pricing_vector_.
clear();
1309 if (matrix_is_unchanged && bounds_are_unchanged) {
1313 solve_from_scratch =
false;
1314 }
else if (only_change_is_new_cols && only_new_bounds) {
1318 variable_starting_values_);
1320 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1321 for (ColIndex& col_ref : basis_) {
1322 if (col_ref >= first_new_col) {
1323 col_ref += num_new_cols;
1330 primal_edge_norms_.
Clear();
1332 solve_from_scratch =
false;
1338 primal_edge_norms_.
Clear();
1339 if (objective_is_unchanged) {
1340 if (matrix_is_unchanged) {
1341 if (!bounds_are_unchanged) {
1343 first_slack_col_, ColIndex(0), solution_state_);
1345 variable_starting_values_);
1348 solve_from_scratch =
false;
1349 }
else if (only_change_is_new_rows) {
1353 first_slack_col_, ColIndex(0), solution_state_);
1360 dual_pricing_vector_.
clear();
1363 if (InitializeFirstBasis(basis_).ok()) {
1364 solve_from_scratch =
false;
1374 if (solve_from_scratch && !solution_state_.
IsEmpty()) {
1375 basis_factorization_.
Clear();
1377 primal_edge_norms_.
Clear();
1378 dual_edge_norms_.
Clear();
1379 dual_pricing_vector_.
clear();
1387 std::vector<ColIndex> candidates;
1389 candidates.push_back(
col);
1392 LOG(
INFO) <<
"The warm-start state contains " << candidates.size()
1393 <<
" candidates for the basis (num_rows = " << num_rows_
1400 if (candidates.size() == num_rows_) {
1402 for (
const ColIndex
col : candidates) {
1403 basis_.push_back(
col);
1409 if (InitializeFirstBasis(basis_).ok()) {
1410 solve_from_scratch =
false;
1414 if (solve_from_scratch) {
1416 const int num_super_basic =
1420 variable_starting_values_);
1422 LOG(
INFO) <<
"The initial basis did not use " << num_super_basic
1423 <<
" BASIC columns from the initial state and used "
1424 << (num_rows_ - (candidates.size() - num_super_basic))
1425 <<
" slack variables that were not marked BASIC.";
1426 if (num_snapped > 0) {
1428 <<
" of the FREE variables where moved to their bound.";
1432 if (InitializeFirstBasis(basis_).ok()) {
1433 solve_from_scratch =
false;
1436 LOG(
INFO) <<
"RevisedSimplex is not using the warm start "
1437 "basis because it is not factorizable.";
1443 if (solve_from_scratch) {
1444 if (log_info)
LOG(
INFO) <<
"Solve from scratch.";
1445 basis_factorization_.
Clear();
1447 primal_edge_norms_.
Clear();
1448 dual_edge_norms_.
Clear();
1449 dual_pricing_vector_.
clear();
1452 if (log_info)
LOG(
INFO) <<
"Incremental solve.";
1454 DCHECK(BasisIsConsistent());
1458void RevisedSimplex::DisplayBasicVariableStatistics() {
1461 int num_fixed_variables = 0;
1462 int num_free_variables = 0;
1463 int num_variables_at_bound = 0;
1464 int num_slack_variables = 0;
1465 int num_infeasible_variables = 0;
1472 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1473 const ColIndex
col = basis_[
row];
1476 ++num_free_variables;
1480 ++num_infeasible_variables;
1482 if (
col >= first_slack_col_) {
1483 ++num_slack_variables;
1486 ++num_fixed_variables;
1489 ++num_variables_at_bound;
1493 VLOG(1) <<
"Basis size: " << num_rows_;
1494 VLOG(1) <<
"Number of basic infeasible variables: "
1495 << num_infeasible_variables;
1496 VLOG(1) <<
"Number of basic slack variables: " << num_slack_variables;
1497 VLOG(1) <<
"Number of basic variables at bound: " << num_variables_at_bound;
1498 VLOG(1) <<
"Number of basic fixed variables: " << num_fixed_variables;
1499 VLOG(1) <<
"Number of basic free variables: " << num_free_variables;
1502void RevisedSimplex::SaveState() {
1505 solution_state_has_been_set_externally_ =
false;
1508RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1510 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1512 contains_data[e.row()] =
true;
1515 RowIndex num_empty_rows(0);
1516 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1517 if (!contains_data[
row]) {
1519 VLOG(1) <<
"Row " <<
row <<
" is empty.";
1522 return num_empty_rows;
1525ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1526 ColIndex num_empty_cols(0);
1527 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1530 VLOG(2) <<
"Column " <<
col <<
" is empty.";
1533 return num_empty_cols;
1536int RevisedSimplex::ComputeNumberOfSuperBasicVariables()
const {
1538 int num_super_basic = 0;
1539 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1541 variable_values_.
Get(
col) != 0.0) {
1545 return num_super_basic;
1548void RevisedSimplex::CorrectErrorsOnVariableValues() {
1563 VLOG(1) <<
"Primal infeasibility (bounds error) = "
1565 <<
", Primal residual |A.x - b| = "
1570void RevisedSimplex::ComputeVariableValuesError() {
1574 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1580void RevisedSimplex::ComputeDirection(ColIndex
col) {
1584 direction_infinity_norm_ = 0.0;
1587 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1591 direction_infinity_norm_ =
1596 for (
const auto e : direction_) {
1597 direction_infinity_norm_ =
1598 std::max(direction_infinity_norm_, std::abs(e.coefficient()));
1602 num_rows_ == 0 ? 0.0
1603 :
static_cast<double>(direction_.non_zeros.size()) /
1604 static_cast<double>(num_rows_.value())));
1607Fractional RevisedSimplex::ComputeDirectionError(ColIndex
col) {
1610 for (
const auto e : direction_) {
1617template <
bool is_entering_reduced_cost_positive>
1620 RowIndex
row)
const {
1621 const ColIndex
col = basis_[
row];
1626 if (is_entering_reduced_cost_positive) {
1627 if (direction > 0.0) {
1633 if (direction > 0.0) {
1641template <
bool is_entering_reduced_cost_positive>
1642Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1643 Fractional bound_flip_ratio, SparseColumn* leaving_candidates)
const {
1655 leaving_candidates->Clear();
1667 for (
const auto e : direction_) {
1668 const Fractional magnitude = std::abs(e.coefficient());
1669 if (magnitude <= threshold)
continue;
1672 if (
ratio <= harris_ratio) {
1673 leaving_candidates->SetCoefficient(e.row(),
ratio);
1685 harris_ratio =
std::min(harris_ratio,
1686 std::max(minimum_delta / magnitude,
1687 ratio + harris_tolerance / magnitude));
1690 return harris_ratio;
1703 if (current >= 0.0) {
1704 return candidate >= 0.0 && candidate <= current;
1706 return candidate >= current;
1714Status RevisedSimplex::ChooseLeavingVariableRow(
1715 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1725 int stats_num_leaving_choices = 0;
1726 equivalent_leaving_choices_.clear();
1730 stats_num_leaving_choices = 0;
1734 const Fractional entering_value = variable_values_.
Get(entering_col);
1736 (reduced_cost > 0.0) ? entering_value -
lower_bounds[entering_col]
1744 (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1745 current_ratio, &leaving_candidates_)
1746 : ComputeHarrisRatioAndLeavingCandidates<false>(
1747 current_ratio, &leaving_candidates_);
1752 if (current_ratio <= harris_ratio) {
1754 *step_length = current_ratio;
1764 stats_num_leaving_choices = 0;
1766 equivalent_leaving_choices_.clear();
1769 if (
ratio > harris_ratio)
continue;
1770 ++stats_num_leaving_choices;
1771 const RowIndex
row = e.row();
1776 const Fractional candidate_magnitude = std::abs(direction_[
row]);
1777 if (candidate_magnitude < pivot_magnitude)
continue;
1778 if (candidate_magnitude == pivot_magnitude) {
1779 if (!IsRatioMoreOrEquallyStable(
ratio, current_ratio))
continue;
1780 if (
ratio == current_ratio) {
1782 equivalent_leaving_choices_.push_back(
row);
1786 equivalent_leaving_choices_.clear();
1787 current_ratio =
ratio;
1788 pivot_magnitude = candidate_magnitude;
1793 if (!equivalent_leaving_choices_.empty()) {
1794 equivalent_leaving_choices_.push_back(*leaving_row);
1796 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1797 0, equivalent_leaving_choices_.size() - 1)(random_)];
1809 if (current_ratio <= 0.0) {
1815 *step_length = minimum_delta / pivot_magnitude;
1817 *step_length = current_ratio;
1824 TestPivot(entering_col, *leaving_row);
1837 if (pivot_magnitude <
1843 VLOG(1) <<
"Refactorizing to avoid pivoting by "
1844 << direction_[*leaving_row]
1845 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1846 <<
" reduced cost = " << reduced_cost;
1847 *refactorize =
true;
1857 VLOG(1) <<
"Couldn't avoid pivoting by " << direction_[*leaving_row]
1858 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1859 <<
" reduced cost = " << reduced_cost;
1860 DCHECK_GE(std::abs(direction_[*leaving_row]),
1869 const bool is_reduced_cost_positive = (reduced_cost > 0.0);
1870 const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
1871 *
target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
1878 ratio_test_stats_.leaving_choices.Add(stats_num_leaving_choices);
1879 if (!equivalent_leaving_choices_.empty()) {
1880 ratio_test_stats_.num_perfect_ties.Add(
1881 equivalent_leaving_choices_.size());
1884 ratio_test_stats_.abs_used_pivot.Add(std::abs(direction_[*leaving_row]));
1906 bool operator<(
const BreakPoint& other)
const {
1907 if (
ratio == other.ratio) {
1909 return row > other.row;
1913 return ratio > other.ratio;
1924void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
1925 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1926 RowIndex* leaving_row,
Fractional* step_length,
1939 const Fractional entering_value = variable_values_.
Get(entering_col);
1940 Fractional current_ratio = (reduced_cost > 0.0)
1945 std::vector<BreakPoint> breakpoints;
1947 for (
const auto e : direction_) {
1949 reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
1950 const Fractional magnitude = std::abs(direction);
1951 if (magnitude < tolerance)
continue;
1966 const ColIndex
col = basis_[e.row()];
1977 if (to_lower >= 0.0 && to_lower < current_ratio) {
1978 breakpoints.push_back(
1979 BreakPoint(e.row(), to_lower, magnitude,
lower_bound));
1981 if (to_upper >= 0.0 && to_upper < current_ratio) {
1982 breakpoints.push_back(
1983 BreakPoint(e.row(), to_upper, magnitude,
upper_bound));
1989 std::make_heap(breakpoints.begin(), breakpoints.end());
1993 Fractional improvement = std::abs(reduced_cost);
1996 while (!breakpoints.empty()) {
1997 const BreakPoint top = breakpoints.front();
2005 if (top.coeff_magnitude > best_magnitude) {
2006 *leaving_row = top.row;
2007 current_ratio = top.ratio;
2008 best_magnitude = top.coeff_magnitude;
2014 improvement -= top.coeff_magnitude;
2015 if (improvement <= 0.0)
break;
2016 std::pop_heap(breakpoints.begin(), breakpoints.end());
2017 breakpoints.pop_back();
2024 if (best_magnitude < threshold && !basis_factorization_.
IsRefactorized()) {
2025 *refactorize =
true;
2029 *step_length = current_ratio;
2033Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
2042 if (dual_prices_.Size() == 0) {
2048 *leaving_row = dual_prices_.GetMaximum();
2053 const ColIndex leaving_col = basis_[*leaving_row];
2073 if (
cost == 0.0)
return false;
2083template <
bool use_dense_update>
2084void RevisedSimplex::OnDualPriceChange(
const DenseColumn& squared_norm,
2088 const bool is_candidate =
2089 IsDualPhaseILeavingCandidate(price, type, threshold);
2091 if (use_dense_update) {
2092 dual_prices_.DenseAddOrUpdate(
row,
Square(price) / squared_norm[
row]);
2094 dual_prices_.AddOrUpdate(
row,
Square(price) / squared_norm[
row]);
2097 dual_prices_.Remove(
row);
2101void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
2102 ColIndex entering_col) {
2115 dual_pricing_vector_[leaving_row] / direction_[leaving_row];
2116 for (
const auto e : direction_) {
2117 dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2118 OnDualPriceChange(squared_norms, e.row(), variable_type[basis_[e.row()]],
2121 dual_pricing_vector_[leaving_row] = step;
2125 dual_pricing_vector_[leaving_row] -=
2126 dual_infeasibility_improvement_direction_[entering_col];
2127 if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2128 --num_dual_infeasible_positions_;
2130 dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2133 dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2136 OnDualPriceChange(squared_norms, leaving_row, variable_type[entering_col],
2140template <
typename Cols>
2141void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2144 bool something_to_do =
false;
2149 for (ColIndex
col : cols) {
2152 (can_increase.IsSet(
col) && reduced_cost < -tolerance) ? 1.0
2153 : (can_decrease.IsSet(
col) && reduced_cost > tolerance) ? -1.0
2155 if (sign != dual_infeasibility_improvement_direction_[
col]) {
2157 --num_dual_infeasible_positions_;
2158 }
else if (dual_infeasibility_improvement_direction_[
col] == 0.0) {
2159 ++num_dual_infeasible_positions_;
2161 if (!something_to_do) {
2162 initially_all_zero_scratchpad_.
values.
resize(num_rows_, 0.0);
2164 initially_all_zero_scratchpad_.
non_zeros.clear();
2165 something_to_do =
true;
2169 num_update_price_operations_ +=
2172 col, sign - dual_infeasibility_improvement_direction_[
col],
2173 &initially_all_zero_scratchpad_);
2174 dual_infeasibility_improvement_direction_[
col] = sign;
2177 if (something_to_do) {
2184 basis_factorization_.
RightSolve(&initially_all_zero_scratchpad_);
2185 if (initially_all_zero_scratchpad_.
non_zeros.empty()) {
2186 dual_prices_.StartDenseUpdates();
2187 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2188 if (initially_all_zero_scratchpad_[
row] == 0.0)
continue;
2189 dual_pricing_vector_[
row] += initially_all_zero_scratchpad_[
row];
2190 OnDualPriceChange<
true>(
2191 squared_norms,
row, variable_type[basis_[
row]], threshold);
2195 for (
const auto e : initially_all_zero_scratchpad_) {
2196 dual_pricing_vector_[e.row()] += e.coefficient();
2197 OnDualPriceChange(squared_norms, e.row(),
2198 variable_type[basis_[e.row()]], threshold);
2199 initially_all_zero_scratchpad_[e.row()] = 0.0;
2202 initially_all_zero_scratchpad_.non_zeros.clear();
2206Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2207 RowIndex* leaving_row,
Fractional* cost_variation,
2227 dual_pricing_vector_.
empty()) {
2229 num_dual_infeasible_positions_ = 0;
2231 dual_prices_.ClearAndResize(num_rows_);
2232 dual_infeasibility_improvement_direction_.
AssignToZero(num_cols_);
2233 DualPhaseIUpdatePriceOnReducedCostChange(
2243 if (num_dual_infeasible_positions_ == 0)
return Status::OK();
2245 *leaving_row = dual_prices_.GetMaximum();
2250 *cost_variation = dual_pricing_vector_[*leaving_row];
2251 const ColIndex leaving_col = basis_[*leaving_row];
2252 if (*cost_variation < 0.0) {
2261template <
typename BoxedVariableCols>
2262void RevisedSimplex::MakeBoxedVariableDualFeasible(
2263 const BoxedVariableCols& cols,
bool update_basic_values) {
2265 std::vector<ColIndex> changed_cols;
2275 const Fractional dual_feasibility_tolerance =
2278 for (
const ColIndex
col : cols) {
2287 if (reduced_cost > dual_feasibility_tolerance &&
2291 changed_cols.push_back(
col);
2292 }
else if (reduced_cost < -dual_feasibility_tolerance &&
2296 changed_cols.push_back(
col);
2300 if (!changed_cols.empty()) {
2301 iteration_stats_.num_dual_flips.Add(changed_cols.size());
2303 update_basic_values);
2307Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2312 const ColIndex leaving_col = basis_[leaving_row];
2313 const Fractional leaving_variable_value = variable_values_.
Get(leaving_col);
2323 return unscaled_step / direction_[leaving_row];
2326bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2327 VLOG(1) <<
"Test pivot.";
2329 const ColIndex leaving_col = basis_[leaving_row];
2330 basis_[leaving_row] = entering_col;
2334 CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2336 basis_[leaving_row] = leaving_col;
2343void RevisedSimplex::PermuteBasis() {
2350 if (col_perm.empty())
return;
2356 if (!dual_pricing_vector_.
empty()) {
2371Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2372 RowIndex leaving_row,
2389 pivot_from_update_row = update_row_.
GetCoefficient(entering_col);
2400 const ColIndex leaving_col = basis_[leaving_row];
2408 ratio_test_stats_.bound_shift.Add(variable_values_.
Get(leaving_col) -
2411 UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2414 const Fractional pivot_from_direction = direction_[leaving_row];
2416 std::abs(pivot_from_update_row - pivot_from_direction);
2418 (1 + std::abs(pivot_from_direction))) {
2419 VLOG(1) <<
"Refactorizing: imprecise pivot " << pivot_from_direction
2420 <<
" diff = " << diff;
2424 basis_factorization_.
Update(entering_col, leaving_row, direction_));
2432Status RevisedSimplex::RefactorizeBasisIfNeeded(
bool* refactorize) {
2439 *refactorize =
false;
2444 if (
col >= integrality_scale_.
size()) {
2445 integrality_scale_.
resize(
col + 1, 0.0);
2447 integrality_scale_[
col] = scale;
2452 Cleanup update_deterministic_time_on_return(
2459 std::vector<ColIndex> candidates;
2465 bool refactorize =
false;
2468 for (
int i = 0; i < 10; ++i) {
2471 if (num_pivots >= 5)
break;
2472 if (candidates.empty())
break;
2476 std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2477 const ColIndex entering_col = candidates[
index];
2479 candidates.pop_back();
2493 ComputeDirection(entering_col);
2495 RowIndex leaving_row;
2497 bool local_refactorize =
false;
2499 ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2502 if (local_refactorize)
continue;
2504 if (std::abs(step_length) <= 1e-6)
continue;
2505 if (leaving_row !=
kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2508 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2514 const auto get_diff = [
this](ColIndex
col,
Fractional old_value,
2516 if (
col >= integrality_scale_.
size() || integrality_scale_[
col] == 0.0) {
2520 return (std::abs(new_value * s - std::round(new_value * s)) -
2521 std::abs(old_value * s - std::round(old_value * s)));
2523 Fractional diff = get_diff(entering_col, variable_values_.
Get(entering_col),
2524 variable_values_.
Get(entering_col) + step);
2525 for (
const auto e : direction_) {
2526 const ColIndex
col = basis_[e.row()];
2528 const Fractional new_value = old_value - e.coefficient() * step;
2529 diff += get_diff(
col, old_value, new_value);
2533 if (diff > -1e-2)
continue;
2543 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2545 }
else if (step < 0.0) {
2546 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2553 const ColIndex leaving_col = basis_[leaving_row];
2564 entering_col, leaving_col, leaving_row, direction_, &update_row_);
2566 entering_col, leaving_row, direction_,
2575 const Fractional dir = -direction_[leaving_row] * step;
2576 const bool is_degenerate =
2580 if (!is_degenerate) {
2584 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2587 VLOG(1) <<
"Polish num_pivots: " << num_pivots <<
" gain:" << total_gain;
2606Status RevisedSimplex::PrimalMinimize(TimeLimit*
time_limit) {
2608 Cleanup update_deterministic_time_on_return(
2610 num_consecutive_degenerate_iterations_ = 0;
2611 DisplayIterationInfo();
2612 bool refactorize =
false;
2618 if (phase_ == Phase::FEASIBILITY) {
2639 CorrectErrorsOnVariableValues();
2640 DisplayIterationInfo();
2642 if (phase_ == Phase::FEASIBILITY) {
2654 if (phase_ == Phase::OPTIMIZATION &&
2655 ComputeObjectiveValue() < primal_objective_limit_) {
2656 VLOG(1) <<
"Stopping the primal simplex because"
2657 <<
" the objective limit " << primal_objective_limit_
2658 <<
" has been reached.";
2660 objective_limit_reached_ =
true;
2663 }
else if (phase_ == Phase::FEASIBILITY) {
2676 if (phase_ == Phase::FEASIBILITY) {
2679 if (primal_infeasibility <
2683 VLOG(1) <<
"Infeasible problem! infeasibility = "
2684 << primal_infeasibility;
2693 VLOG(1) <<
"Optimal reached, double checking...";
2702 ComputeDirection(entering_col);
2716 VLOG(1) <<
"Skipping col #" << entering_col
2717 <<
" whose reduced cost is no longer valid under precise reduced "
2734 RowIndex leaving_row;
2736 if (phase_ == Phase::FEASIBILITY) {
2737 PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2738 &refactorize, &leaving_row,
2742 ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2745 if (refactorize)
continue;
2750 VLOG(1) <<
"Infinite step length, double checking...";
2754 if (phase_ == Phase::FEASIBILITY) {
2756 VLOG(1) <<
"Unbounded feasibility problem !?";
2759 VLOG(1) <<
"Unbounded problem.";
2762 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2763 const ColIndex
col = basis_[
row];
2764 solution_primal_ray_[
col] = -direction_[
row];
2766 solution_primal_ray_[entering_col] = 1.0;
2774 Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2775 if (phase_ == Phase::FEASIBILITY && leaving_row !=
kInvalidRow) {
2785 step = ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2789 const ColIndex leaving_col =
2795 bool is_degenerate =
false;
2797 Fractional dir = -direction_[leaving_row] * step;
2805 if (!is_degenerate) {
2806 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
2815 entering_col, basis_[leaving_row], leaving_row, direction_,
2818 direction_, &update_row_);
2820 if (!is_degenerate) {
2829 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2831 if (is_degenerate) {
2832 timer.AlsoUpdate(&iteration_stats_.degenerate);
2834 timer.AlsoUpdate(&iteration_stats_.normal);
2843 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2845 }
else if (step < 0.0) {
2846 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2853 if (phase_ == Phase::FEASIBILITY && leaving_row !=
kInvalidRow) {
2859 &objective_[leaving_col]);
2864 if (step_length == 0.0) {
2865 num_consecutive_degenerate_iterations_++;
2867 if (num_consecutive_degenerate_iterations_ > 0) {
2868 iteration_stats_.degenerate_run_size.Add(
2869 num_consecutive_degenerate_iterations_);
2870 num_consecutive_degenerate_iterations_ = 0;
2875 if (num_consecutive_degenerate_iterations_ > 0) {
2876 iteration_stats_.degenerate_run_size.Add(
2877 num_consecutive_degenerate_iterations_);
2893Status RevisedSimplex::DualMinimize(
bool feasibility_phase,
2895 Cleanup update_deterministic_time_on_return(
2897 num_consecutive_degenerate_iterations_ = 0;
2898 bool refactorize =
false;
2900 bound_flip_candidates_.clear();
2903 RowIndex leaving_row;
2908 ColIndex entering_col;
2917 const bool old_refactorize_value = refactorize;
2938 if (feasibility_phase || old_refactorize_value) {
2950 if (!feasibility_phase) {
2951 MakeBoxedVariableDualFeasible(
2964 if (phase_ == Phase::OPTIMIZATION &&
2966 ComputeObjectiveValue() > dual_objective_limit_) {
2967 VLOG(1) <<
"Stopping the dual simplex because"
2968 <<
" the objective limit " << dual_objective_limit_
2969 <<
" has been reached.";
2971 objective_limit_reached_ =
true;
2976 DisplayIterationInfo();
2980 if (!feasibility_phase) {
2983 MakeBoxedVariableDualFeasible(bound_flip_candidates_,
2985 bound_flip_candidates_.clear();
2993 if (feasibility_phase) {
3005 VLOG(1) <<
"Optimal reached, double checking.";
3011 if (feasibility_phase) {
3016 if (num_dual_infeasible_positions_ == 0) {
3019 VLOG(1) <<
"DUAL infeasible in dual phase I.";
3030 if (feasibility_phase) {
3037 &bound_flip_candidates_, &entering_col));
3043 VLOG(1) <<
"No entering column. Double checking...";
3049 if (feasibility_phase) {
3051 VLOG(1) <<
"Unbounded dual feasibility problem !?";
3055 solution_dual_ray_ =
3058 solution_dual_ray_row_combination_.
AssignToZero(num_cols_);
3060 solution_dual_ray_row_combination_[
col] =
3063 if (cost_variation < 0) {
3065 ChangeSign(&solution_dual_ray_row_combination_);
3078 VLOG(1) <<
"Trying not to pivot by " << entering_coeff;
3084 ComputeDirection(entering_col);
3090 if (std::abs(direction_[leaving_row]) <
3093 VLOG(1) <<
"Trying not pivot by " << entering_coeff <<
" ("
3094 << direction_[leaving_row]
3095 <<
") because the direction has a norm of "
3096 << direction_infinity_norm_;
3123 const bool increasing_rc_is_needed =
3124 (cost_variation > 0.0) == (entering_coeff > 0.0);
3130 timer.AlsoUpdate(&iteration_stats_.degenerate);
3132 timer.AlsoUpdate(&iteration_stats_.normal);
3144 entering_col, leaving_row, direction_,
3150 if (feasibility_phase) {
3151 DualPhaseIUpdatePrice(leaving_row, entering_col);
3154 ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
3159 const ColIndex leaving_col = basis_[leaving_row];
3161 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3180Status RevisedSimplex::PrimalPush(TimeLimit*
time_limit) {
3182 Cleanup update_deterministic_time_on_return(
3185 DisplayIterationInfo();
3186 bool refactorize =
false;
3190 primal_edge_norms_.
Clear();
3191 dual_edge_norms_.
Clear();
3195 std::vector<ColIndex> super_basic_cols;
3198 variable_values_.
Get(
col) != 0) {
3199 super_basic_cols.push_back(
col);
3203 while (!super_basic_cols.empty()) {
3211 CorrectErrorsOnVariableValues();
3212 DisplayIterationInfo();
3216 ColIndex entering_col = super_basic_cols.back();
3230 const Fractional entering_value = variable_values_.
Get(entering_col);
3231 if (variables_info_.
GetTypeRow()[entering_col] ==
3233 if (entering_value > 0) {
3245 if (diff_lb <= diff_ub) {
3253 ComputeDirection(entering_col);
3256 RowIndex leaving_row;
3260 &refactorize, &leaving_row,
3263 if (refactorize)
continue;
3266 super_basic_cols.pop_back();
3269 if (variables_info_.
GetTypeRow()[entering_col] ==
3271 step_length = std::fabs(entering_value);
3273 VLOG(1) <<
"Infinite step for bounded variable ?!";
3279 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
3282 const ColIndex leaving_col =
3291 bool is_degenerate =
false;
3293 Fractional dir = -direction_[leaving_row] * step;
3301 if (!is_degenerate) {
3302 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
3309 if (!is_degenerate) {
3318 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3320 if (is_degenerate) {
3321 timer.AlsoUpdate(&iteration_stats_.degenerate);
3323 timer.AlsoUpdate(&iteration_stats_.normal);
3330 if (variables_info_.
GetTypeRow()[entering_col] ==
3332 variable_values_.
Set(entering_col, 0.0);
3333 }
else if (step > 0.0) {
3334 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3336 }
else if (step < 0.0) {
3337 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3346 if (!super_basic_cols.empty() > 0 &&
3348 LOG(
INFO) <<
"Push terminated early with " << super_basic_cols.size()
3349 <<
" super-basic variables remaining.";
3359ColIndex RevisedSimplex::SlackColIndex(RowIndex
row)
const {
3366 result.append(iteration_stats_.StatString());
3367 result.append(ratio_test_stats_.StatString());
3368 result.append(entering_variable_.
StatString());
3369 result.append(dual_prices_.StatString());
3371 result.append(variable_values_.
StatString());
3372 result.append(primal_edge_norms_.
StatString());
3373 result.append(dual_edge_norms_.
StatString());
3375 result.append(basis_factorization_.
StatString());
3380void RevisedSimplex::DisplayAllStats() {
3381 if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3383 absl::FPrintF(stderr,
"%s", GetPrettySolverStats());
3387Fractional RevisedSimplex::ComputeObjectiveValue()
const {
3393Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue()
const {
3397 return objective_scaling_factor_ * (sum + objective_offset_);
3402 deterministic_random_.seed(
parameters.random_seed());
3406 PropagateParameters();
3409void RevisedSimplex::PropagateParameters() {
3419void RevisedSimplex::DisplayIterationInfo() {
3424 case Phase::FEASIBILITY: {
3425 const int64_t iter = num_iterations_;
3437 name =
"sum_dual_infeasibilities";
3440 name =
"sum_primal_infeasibilities";
3443 LOG(
INFO) <<
"Feasibility phase, iteration # " << iter <<
", " <<
name
3444 <<
" = " << absl::StrFormat(
"%.15E", objective);
3447 case Phase::OPTIMIZATION: {
3448 const int64_t iter = num_iterations_ - num_feasibility_iterations_;
3454 const Fractional objective = ComputeInitialProblemObjectiveValue();
3455 LOG(
INFO) <<
"Optimization phase, iteration # " << iter
3456 <<
", objective = " << absl::StrFormat(
"%.15E", objective);
3460 const int64_t iter = num_iterations_ - num_feasibility_iterations_ -
3461 num_optimization_iterations_;
3462 LOG(
INFO) <<
"Push phase, iteration # " << iter
3463 <<
", remaining_variables_to_push = "
3464 << ComputeNumberOfSuperBasicVariables();
3469void RevisedSimplex::DisplayErrors() {
3471 LOG(
INFO) <<
"Primal infeasibility (bounds) = "
3473 LOG(
INFO) <<
"Primal residual |A.x - b| = "
3475 LOG(
INFO) <<
"Dual infeasibility (reduced costs) = "
3477 LOG(
INFO) <<
"Dual residual |c_B - y.B| = "
3484std::string StringifyMonomialWithFlags(
const Fractional a,
3485 const std::string& x) {
3487 a, x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3493std::string StringifyWithFlags(
const Fractional x) {
3495 absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3500std::string RevisedSimplex::SimpleVariableInfo(ColIndex
col)
const {
3506 absl::StrAppendFormat(&output,
"%d (%s) = %s, %s, %s, [%s,%s]",
col.value(),
3507 variable_name_[
col],
3508 StringifyWithFlags(variable_values_.
Get(
col)),
3516void RevisedSimplex::DisplayInfoOnVariables()
const {
3518 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3522 objective_coefficient * variable_value;
3523 VLOG(3) << SimpleVariableInfo(
col) <<
". " << variable_name_[
col] <<
" = "
3524 << StringifyWithFlags(variable_value) <<
" * "
3525 << StringifyWithFlags(objective_coefficient)
3526 <<
"(obj) = " << StringifyWithFlags(objective_contribution);
3528 VLOG(3) <<
"------";
3532void RevisedSimplex::DisplayVariableBounds() {
3537 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3538 switch (variable_type[
col]) {
3542 VLOG(3) << variable_name_[
col]
3546 VLOG(3) << variable_name_[
col]
3551 <<
" <= " << variable_name_[
col]
3555 VLOG(3) << variable_name_[
col] <<
" = "
3559 LOG(DFATAL) <<
"Column " <<
col <<
" has no meaningful status.";
3569 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3570 ComputeDirection(
col);
3571 for (
const auto e : direction_) {
3572 if (column_scales ==
nullptr) {
3573 dictionary[e.row()].SetCoefficient(
col, e.coefficient());
3577 col < column_scales->
size() ? (*column_scales)[
col] : 1.0;
3579 ? (*column_scales)[
GetBasis(e.row())]
3581 dictionary[e.row()].SetCoefficient(
3582 col, direction_[e.row()] * (numerator / denominator));
3591 Status status = Initialize(linear_program);
3594 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3598void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3601 DisplayInfoOnVariables();
3603 std::string output =
"z = " + StringifyWithFlags(ComputeObjectiveValue());
3606 absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[
col],
3607 variable_name_[
col]));
3609 VLOG(3) << output <<
";";
3611 const RevisedSimplexDictionary dictionary(
nullptr,
this);
3613 for (
const SparseRow&
row : dictionary) {
3615 ColIndex basic_col = basis_[r];
3616 absl::StrAppend(&output, variable_name_[basic_col],
" = ",
3617 StringifyWithFlags(variable_values_.
Get(basic_col)));
3618 for (
const SparseRowEntry e :
row) {
3619 if (e.col() != basic_col) {
3620 absl::StrAppend(&output,
3621 StringifyMonomialWithFlags(e.coefficient(),
3622 variable_name_[e.col()]));
3625 VLOG(3) << output <<
";";
3627 VLOG(3) <<
"------";
3628 DisplayVariableBounds();
3633void RevisedSimplex::DisplayProblem()
const {
3637 DisplayInfoOnVariables();
3638 std::string output =
"min: ";
3639 bool has_objective =
false;
3640 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3642 has_objective |= (coeff != 0.0);
3643 absl::StrAppend(&output,
3644 StringifyMonomialWithFlags(coeff, variable_name_[
col]));
3646 if (!has_objective) {
3647 absl::StrAppend(&output,
" 0");
3649 VLOG(3) << output <<
";";
3650 for (RowIndex
row(0);
row < num_rows_; ++
row) {
3652 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3653 absl::StrAppend(&output,
3654 StringifyMonomialWithFlags(
3656 variable_name_[
col]));
3658 VLOG(3) << output <<
" = 0;";
3660 VLOG(3) <<
"------";
3664void RevisedSimplex::AdvanceDeterministicTime(TimeLimit*
time_limit) {
3667 const double deterministic_time_delta =
3668 current_deterministic_time - last_deterministic_time_update_;
3669 time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3670 last_deterministic_time_update_ = current_deterministic_time;
3673#undef DCHECK_COL_BOUNDS
3674#undef DCHECK_ROW_BOUNDS
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
void push_back(const value_type &x)
bool IsSet(IndexType i) const
std::string StatString() const
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Fractional ComputeInfinityNormConditionNumberUpperBound() const
ABSL_MUST_USE_RESULT Status Refactorize()
const ColumnPermutation & GetColumnPermutation() const
ABSL_MUST_USE_RESULT Status Initialize()
bool IsRefactorized() const
ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
RowToColMapping ComputeInitialBasis(const std::vector< ColIndex > &candidates)
void RightSolveForProblemColumn(ColIndex col, ScatteredColumn *d) const
void SetColumnPermutationToIdentity()
void SetParameters(const GlopParameters ¶meters)
void RightSolve(ScatteredColumn *d) const
double DeterministicTime() const
ABSL_MUST_USE_RESULT Status ForceRefactorization()
std::string StatString() const
Fractional LookUpCoefficient(RowIndex index) const
Fractional EntryCoefficient(EntryIndex i) const
Fractional GetFirstCoefficient() const
RowIndex EntryRow(EntryIndex i) const
EntryIndex num_entries() const
void ColumnCopyToDenseColumn(ColIndex col, DenseColumn *dense_column) const
ColIndex num_cols() const
void ColumnAddMultipleToSparseScatteredColumn(ColIndex col, Fractional multiplier, ScatteredColumn *column) const
RowIndex num_rows() const
void PopulateFromTranspose(const CompactSparseMatrix &input)
Fractional ColumnScalarProduct(ColIndex col, const DenseRow &vector) const
void PopulateFromSparseMatrixAndAddSlacks(const SparseMatrix &input)
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn *dense_column) const
void PopulateFromMatrixView(const MatrixView &input)
EntryIndex num_entries() const
ColumnView column(ColIndex col) const
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
const DenseColumn & GetEdgeSquaredNorms()
void ResizeOnNewRows(RowIndex new_size)
void SetParameters(const GlopParameters ¶meters)
bool NeedsBasisRefactorization() const
std::string StatString() const
ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn(bool nothing_to_recompute, const UpdateRow &update_row, Fractional cost_variation, ColIndex *entering_col)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
std::string StatString() const
ABSL_MUST_USE_RESULT Status DualChooseEnteringColumn(bool nothing_to_recompute, const UpdateRow &update_row, Fractional cost_variation, std::vector< ColIndex > *bound_flip_candidates, ColIndex *entering_col)
bool use_dual_simplex() const
double primal_feasibility_tolerance() const
::operations_research::glop::GlopParameters_PricingRule feasibility_rule() const
double refactorization_threshold() const
bool log_search_progress() const
bool allow_simplex_algorithm_change() const
::operations_research::glop::GlopParameters_PricingRule optimization_rule() const
double dual_small_pivot_threshold() const
bool use_dedicated_dual_feasibility_algorithm() const
static constexpr InitialBasisHeuristic TRIANGULAR
double harris_tolerance_ratio() const
double max_number_of_reoptimizations() const
bool change_status_to_imprecise() const
bool use_transposed_matrix() const
double minimum_acceptable_pivot() const
double dual_feasibility_tolerance() const
bool perturb_costs_in_dual_simplex() const
double initial_condition_number_threshold() const
double ratio_test_zero_threshold() const
double solution_feasibility_tolerance() const
static constexpr InitialBasisHeuristic NONE
double crossover_bound_snapping_distance() const
::PROTOBUF_NAMESPACE_ID::int64 max_number_of_iterations() const
double objective_lower_limit() const
double degenerate_ministep_factor() const
double small_pivot_threshold() const
double objective_upper_limit() const
bool exploit_singleton_column_in_initial_basis() const
::operations_research::glop::GlopParameters_InitialBasisHeuristic initial_basis() const
static constexpr InitialBasisHeuristic BIXBY
static constexpr InitialBasisHeuristic MAROS
void set_use_dual_simplex(bool value)
bool push_to_vertex() const
bool IsMaximizationProblem() const
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
void UpdateBeforeBasisPivot(ColIndex entering_col, ColIndex leaving_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
void SetPricingRule(GlopParameters::PricingRule rule)
void TestEnteringEdgeNormPrecision(ColIndex entering_col, const ScatteredColumn &direction)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
bool NeedsBasisRefactorization() const
std::string StatString() const
void ForceRecomputation()
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
void UpdateBeforeBasisPivot(ColIndex entering_col, UpdateRow *update_row)
void RecomputePriceAt(ColIndex col)
ColIndex GetBestEnteringColumn()
void ResetForNewObjective()
Fractional TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction)
void MakeReducedCostsPrecise()
bool AreReducedCostsRecomputed()
bool AreReducedCostsPrecise()
bool IsValidPrimalEnteringCandidate(ColIndex col) const
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
bool HasCostShift() const
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables()
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)
Fractional ComputeSumOfDualInfeasibilities()
const DenseRow & GetFullReducedCosts()
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
const DenseRow & GetReducedCosts()
Fractional GetDualFeasibilityTolerance() const
const DenseColumn & GetDualValues()
void ClearAndRemoveCostShifts()
Fractional ComputeMaximumDualResidual()
void UpdateDataOnBasisPermutation()
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
Fractional ComputeMaximumDualInfeasibility()
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
bool NeedsBasisRefactorization() const
std::string StatString() const
const DenseRow & GetDualRayRowCombination() const
Fractional GetVariableValue(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
const DenseRow & GetPrimalRay() const
Fractional GetConstraintActivity(RowIndex row) const
VariableStatus GetVariableStatus(ColIndex col) const
Fractional GetReducedCost(ColIndex col) const
const DenseColumn & GetDualRay() const
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
Fractional GetObjectiveValue() const
RowMajorSparseMatrix ComputeDictionary(const DenseRow *column_scales)
Fractional GetDualValue(RowIndex row) const
void NotifyThatMatrixIsUnchangedForNextSolve()
void SetStartingVariableValuesForNextSolve(const DenseRow &values)
ConstraintStatus GetConstraintStatus(RowIndex row) const
void ComputeBasicVariablesForState(const LinearProgram &linear_program, const BasisState &state)
ColIndex GetProblemNumCols() const
void LoadStateForNextSolve(const BasisState &state)
RowIndex GetProblemNumRows() const
void ClearStateForNextSolve()
const BasisFactorization & GetBasisFactorization() const
int64_t GetNumberOfIterations() const
const BasisState & GetState() const
ColIndex GetBasis(RowIndex row) const
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
typename Iterator::Entry Entry
void AssignToZero(IntType size)
void resize(IntType size)
const ScatteredRow & GetUnitRowLeftInverse() const
void ComputeUnitRowLeftInverse(RowIndex leaving_row)
void RecomputeFullUpdateRow(RowIndex leaving_row)
const bool IsComputed() const
const Fractional GetCoefficient(ColIndex col) const
void ComputeUpdateRow(RowIndex leaving_row)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
const ColIndexVector & GetNonZeroPositions() const
std::string StatString() const
void Set(ColIndex col, Fractional value)
void SetNonBasicVariableValueFromStatus(ColIndex col)
Fractional ComputeMaximumPrimalInfeasibility() const
Fractional ComputeSumOfPrimalInfeasibilities() const
void UpdateGivenNonBasicVariables(const std::vector< ColIndex > &cols_to_update, bool update_basic_variables)
void ResetAllNonBasicVariableValues(const DenseRow &free_initial_values)
void UpdateDualPrices(const std::vector< RowIndex > &row)
void UpdateOnPivoting(const ScatteredColumn &direction, ColIndex entering_col, Fractional step)
const Fractional Get(ColIndex col) const
void RecomputeBasicVariableValues()
Fractional ComputeMaximumPrimalResidual() const
void RecomputeDualPrices()
bool UpdatePrimalPhaseICosts(const Rows &rows, DenseRow *objective)
const DenseRow & GetDenseRow() const
std::string StatString() const
const DenseBitRow & GetIsBasicBitRow() const
int SnapFreeVariablesToBound(Fractional distance, const DenseRow &starting_values)
const DenseRow & GetVariableLowerBounds() const
int ChangeUnusedBasicVariablesToFree(const RowToColMapping &basis)
const DenseBitRow & GetNonBasicBoxedVariables() const
Fractional GetBoundDifference(ColIndex col) const
const DenseBitRow & GetCanIncreaseBitRow() const
const DenseBitRow & GetCanDecreaseBitRow() const
const VariableTypeRow & GetTypeRow() const
void EndDualPhaseI(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
void MakeBoxedVariableRelevant(bool value)
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)
const DenseBitRow & GetNotBasicBitRow() const
void InitializeToDefaultStatus()
const VariableStatusRow & GetStatusRow() const
void UpdateToBasicStatus(ColIndex col)
const DenseRow & GetVariableUpperBounds() const
const DenseBitRow & GetIsRelevantBitRow() const
void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols, const BasisState &state)
bool LoadBoundsAndReturnTrueIfUnchanged(const DenseRow &new_lower_bounds, const DenseRow &new_upper_bounds)
void TransformToDualPhaseIProblem(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
ModelSharedTimeLimit * time_limit
std::string StringifyMonomial(const Fractional a, const std::string &x, bool fraction)
Fractional Square(Fractional f)
Fractional InfinityNorm(const DenseColumn &v)
const RowIndex kInvalidRow(-1)
std::string Stringify(const Fractional x, bool fraction)
StrictITIVector< ColIndex, VariableType > VariableTypeRow
@ UPPER_AND_LOWER_BOUNDED
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
StrictITIVector< ColIndex, Fractional > DenseRow
std::string GetProblemStatusString(ProblemStatus problem_status)
Index ColToIntIndex(ColIndex col)
Permutation< ColIndex > ColumnPermutation
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
ColIndex RowToColIndex(RowIndex row)
bool IsFinite(Fractional value)
bool AreFirstColumnsAndRowsExactlyEquals(RowIndex num_rows, ColIndex num_cols, const SparseMatrix &matrix_a, const CompactSparseMatrix &matrix_b)
const DenseRow & Transpose(const DenseColumn &col)
Bitset64< ColIndex > DenseBitRow
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
constexpr const uint64_t kDeterministicSeed
StrictITIVector< RowIndex, ColIndex > RowToColMapping
std::string GetVariableTypeString(VariableType variable_type)
void ApplyColumnPermutationToRowIndexedVector(const Permutation< ColIndex > &col_perm, RowIndexedVector *v)
StrictITIVector< RowIndex, Fractional > DenseColumn
StrictITIVector< RowIndex, bool > DenseBooleanColumn
static double DeterministicTimeForFpOperations(int64_t n)
std::string GetVariableStatusString(VariableStatus status)
const ColIndex kInvalidCol(-1)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
DisabledScopedTimeDistributionUpdater ScopedTimeDistributionUpdater
#define RETURN_IF_NULL(x)
Fractional coeff_magnitude
#define DCHECK_ROW_BOUNDS(row)
ABSL_FLAG(bool, simplex_display_numbers_as_fractions, false, "Display numbers as fractions.")
#define DCHECK_COL_BOUNDS(col)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
#define GLOP_RETURN_IF_ERROR(function_call)
#define GLOP_RETURN_ERROR_IF_NULL(arg)
VariableStatusRow statuses
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation)
std::vector< Index > non_zeros
StrictITIVector< Index, Fractional > values
#define VLOG_IS_ON(verboselevel)