25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_format.h"
40 ABSL_FLAG(
bool, simplex_display_numbers_as_fractions,
false,
41 "Display numbers as fractions.");
42 ABSL_FLAG(
bool, simplex_stop_after_first_basis,
false,
43 "Stop after first basis has been computed.");
44 ABSL_FLAG(
bool, simplex_stop_after_feasibility,
false,
45 "Stop after first phase has been completed.");
46 ABSL_FLAG(
bool, simplex_display_stats,
false,
"Display algorithm statistics.");
56 explicit Cleanup(std::function<
void()> closure)
57 : closure_(std::move(closure)) {}
58 ~Cleanup() { closure_(); }
61 std::function<void()> closure_;
65 #define DCHECK_COL_BOUNDS(col) \
68 DCHECK_GT(num_cols_, col); \
71 #define DCHECK_ROW_BOUNDS(row) \
74 DCHECK_GT(num_rows_, row); \
90 random_(deterministic_random_),
91 basis_factorization_(&compact_matrix_, &basis_),
92 variables_info_(compact_matrix_),
93 primal_edge_norms_(compact_matrix_, variables_info_,
94 basis_factorization_),
95 dual_edge_norms_(basis_factorization_),
96 dual_prices_(random_),
97 variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
98 basis_factorization_, &dual_edge_norms_, &dual_prices_),
99 update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
100 basis_factorization_),
101 reduced_costs_(compact_matrix_,
objective_, basis_, variables_info_,
102 basis_factorization_, random_),
103 entering_variable_(variables_info_, random_, &reduced_costs_),
104 primal_prices_(random_, variables_info_, &primal_edge_norms_,
107 num_feasibility_iterations_(0),
108 num_optimization_iterations_(0),
110 feasibility_time_(0.0),
111 optimization_time_(0.0),
112 last_deterministic_time_update_(0.0),
115 function_stats_(
"SimplexFunctionStats"),
118 feasibility_phase_(true) {
129 solution_state_ = state;
130 solution_state_has_been_set_externally_ =
true;
134 notify_that_matrix_is_unchanged_ =
true;
143 "The problem is not in the equations form.");
145 Cleanup update_deterministic_time_on_return(
150 const double start_time =
time_limit->GetElapsedTime();
153 dual_infeasibility_improvement_direction_.
clear();
157 feasibility_phase_ =
true;
159 num_feasibility_iterations_ = 0;
160 num_optimization_iterations_ = 0;
161 feasibility_time_ = 0.0;
162 optimization_time_ = 0.0;
169 solution_state_has_been_set_externally_ =
true;
172 ComputeNumberOfEmptyRows();
173 ComputeNumberOfEmptyColumns();
174 DisplayBasicVariableStatistics();
177 if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
182 const bool use_dual = parameters_.use_dual_simplex();
183 const bool log_info = parameters_.log_search_progress() ||
VLOG_IS_ON(1);
185 LOG(
INFO) <<
"------ " << (use_dual ?
"Dual simplex." :
"Primal simplex.");
186 LOG(
INFO) <<
"The matrix has " << compact_matrix_.
num_rows() <<
" rows, "
187 << compact_matrix_.
num_cols() <<
" columns, "
193 if (log_info)
LOG(
INFO) <<
"------ First phase: feasibility.";
194 primal_edge_norms_.
SetPricingRule(parameters_.feasibility_rule());
196 if (parameters_.perturb_costs_in_dual_simplex()) {
200 if (parameters_.use_dedicated_dual_feasibility_algorithm()) {
203 DisplayIterationInfo();
220 MakeBoxedVariableDualFeasible(
231 RefactorizeBasisIfNeeded(&unused);
236 if (initial_infeasibility <
238 if (log_info)
LOG(
INFO) <<
"Initial basis is dual feasible.";
240 MakeBoxedVariableDualFeasible(
255 DisplayIterationInfo();
277 if (log_info)
LOG(
INFO) <<
"Infeasible after first phase.";
285 DisplayIterationInfo();
289 InitializeObjectiveAndTestIfUnchanged(lp);
300 feasibility_phase_ =
false;
301 feasibility_time_ =
time_limit->GetElapsedTime() - start_time;
302 primal_edge_norms_.
SetPricingRule(parameters_.optimization_rule());
303 num_feasibility_iterations_ = num_iterations_;
305 if (log_info)
LOG(
INFO) <<
"------ Second phase: optimization.";
319 for (
int num_optims = 0;
323 num_optims <= parameters_.max_number_of_reoptimizations() &&
324 !objective_limit_reached_ &&
325 (num_iterations_ == 0 ||
326 num_iterations_ < parameters_.max_number_of_iterations()) &&
328 !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
348 if (!integrality_scale_.
empty() &&
367 DisplayIterationInfo();
380 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
385 LOG(
INFO) <<
"DUAL_UNBOUNDED was reported, but the residual and/or "
386 <<
"dual infeasibility is above the tolerance";
396 parameters_.solution_feasibility_tolerance();
401 if (primal_residual > solution_tolerance ||
402 dual_residual > solution_tolerance) {
404 LOG(
INFO) <<
"OPTIMAL was reported, yet one of the residuals is "
405 "above the solution feasibility tolerance after the "
406 "shift/perturbation are removed.";
408 if (parameters_.change_status_to_imprecise()) {
418 primal_residual, parameters_.primal_feasibility_tolerance());
420 std::max(dual_residual, parameters_.dual_feasibility_tolerance());
425 if (primal_infeasibility > primal_tolerance &&
426 dual_infeasibility > dual_tolerance) {
428 LOG(
INFO) <<
"OPTIMAL was reported, yet both of the infeasibility "
429 "are above the tolerance after the "
430 "shift/perturbation are removed.";
432 if (parameters_.change_status_to_imprecise()) {
435 }
else if (primal_infeasibility > primal_tolerance) {
436 if (num_optims == parameters_.max_number_of_reoptimizations()) {
438 LOG(
INFO) <<
"The primal infeasibility is still higher than the "
439 "requested internal tolerance, but the maximum "
440 "number of optimization is reached.";
444 if (log_info)
LOG(
INFO) <<
"Re-optimizing with dual simplex ... ";
446 }
else if (dual_infeasibility > dual_tolerance) {
447 if (num_optims == parameters_.max_number_of_reoptimizations()) {
449 LOG(
INFO) <<
"The dual infeasibility is still higher than the "
450 "requested internal tolerance, but the maximum "
451 "number of optimization is reached.";
455 if (log_info)
LOG(
INFO) <<
"Re-optimizing with primal simplex ... ";
466 if (parameters_.change_status_to_imprecise() &&
468 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
488 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
501 solution_objective_value_ =
505 solution_objective_value_ = -solution_objective_value_;
509 total_time_ =
time_limit->GetElapsedTime() - start_time;
510 optimization_time_ = total_time_ - feasibility_time_;
511 num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
518 return problem_status_;
522 return solution_objective_value_;
526 return num_iterations_;
534 return variable_values_.
Get(
col);
538 return solution_reduced_costs_[
col];
542 return solution_reduced_costs_;
546 return solution_dual_values_[
row];
558 return -variable_values_.
Get(SlackColIndex(
row));
576 return solution_primal_ray_;
580 return solution_dual_ray_;
585 return solution_dual_ray_row_combination_;
592 return basis_factorization_;
595 std::string RevisedSimplex::GetPrettySolverStats()
const {
596 return absl::StrFormat(
597 "Problem status : %s\n"
598 "Solving time : %-6.4g\n"
599 "Number of iterations : %u\n"
600 "Time for solvability (first phase) : %-6.4g\n"
601 "Number of iterations for solvability : %u\n"
602 "Time for optimization : %-6.4g\n"
603 "Number of iterations for optimization : %u\n"
604 "Stop after first basis : %d\n",
606 feasibility_time_, num_feasibility_iterations_, optimization_time_,
607 num_optimization_iterations_,
608 absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
621 void RevisedSimplex::SetVariableNames() {
622 variable_name_.
resize(num_cols_,
"");
623 for (ColIndex
col(0);
col < first_slack_col_; ++
col) {
624 const ColIndex var_index =
col + 1;
627 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
628 const ColIndex var_index =
col - first_slack_col_ + 1;
633 void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
639 bool RevisedSimplex::BasisIsConsistent()
const {
642 for (RowIndex
row(0);
row < num_rows_; ++
row) {
643 const ColIndex
col = basis_[
row];
644 if (!is_basic.IsSet(
col))
return false;
647 ColIndex cols_in_basis(0);
648 ColIndex cols_not_in_basis(0);
649 for (ColIndex
col(0);
col < num_cols_; ++
col) {
650 cols_in_basis += is_basic.IsSet(
col);
651 cols_not_in_basis += !is_basic.IsSet(
col);
652 if (is_basic.IsSet(
col) !=
658 if (cols_not_in_basis != num_cols_ -
RowToColIndex(num_rows_))
return false;
664 void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
673 DCHECK_NE(basis_[basis_row], entering_col);
676 const ColIndex leaving_col = basis_[basis_row];
687 basis_[basis_row] = entering_col;
695 class ColumnComparator {
698 bool operator()(ColIndex col_a, ColIndex col_b)
const {
699 return value_[col_a] < value_[col_b];
716 void RevisedSimplex::UseSingletonColumnInInitialBasis(
RowToColMapping* basis) {
723 std::vector<ColIndex> singleton_column;
724 DenseRow cost_variation(num_cols_, 0.0);
727 for (ColIndex
col(0);
col < num_cols_; ++
col) {
732 cost_variation[
col] = objective_[
col] / std::abs(slope);
734 cost_variation[
col] = -objective_[
col] / std::abs(slope);
736 singleton_column.push_back(
col);
738 if (singleton_column.empty())
return;
745 ColumnComparator comparator(cost_variation);
746 std::sort(singleton_column.begin(), singleton_column.end(), comparator);
747 DCHECK_LE(cost_variation[singleton_column.front()],
748 cost_variation[singleton_column.back()]);
756 for (
const ColIndex
col : singleton_column) {
768 if (error_[
row] == 0.0)
continue;
793 error_[
row] -= coeff * box_width;
794 SetNonBasicVariableStatusAndDeriveValue(
col,
800 error_[
row] += coeff * box_width;
801 SetNonBasicVariableStatusAndDeriveValue(
col,
808 bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
809 const LinearProgram& lp,
bool* only_change_is_new_rows,
810 bool* only_change_is_new_cols, ColIndex* num_new_cols) {
812 DCHECK(only_change_is_new_rows !=
nullptr);
813 DCHECK(only_change_is_new_cols !=
nullptr);
814 DCHECK(num_new_cols !=
nullptr);
820 lp.GetFirstSlackVariable() +
RowToColIndex(lp.num_constraints()));
822 const bool old_part_of_matrix_is_unchanged =
824 num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
829 if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
830 lp.num_variables() == num_cols_) {
836 *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
837 lp.num_constraints() > num_rows_ &&
838 lp.GetFirstSlackVariable() == first_slack_col_;
842 *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
843 lp.num_constraints() == num_rows_ &&
844 lp.GetFirstSlackVariable() > first_slack_col_;
846 *only_change_is_new_cols ? lp.num_variables() - num_cols_ : ColIndex(0);
849 first_slack_col_ = lp.GetFirstSlackVariable();
852 num_rows_ = lp.num_constraints();
853 num_cols_ = lp.num_variables();
860 if (parameters_.use_transposed_matrix()) {
866 bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
867 const LinearProgram& lp, ColIndex num_new_cols) {
869 DCHECK_EQ(lp.num_variables(), num_cols_);
870 DCHECK_LE(num_new_cols, first_slack_col_);
871 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
876 for (ColIndex
col(0);
col < first_new_col; ++
col) {
883 for (ColIndex
col(first_new_col);
col < first_slack_col_; ++
col) {
884 if (lp.variable_lower_bounds()[
col] != 0.0 &&
885 lp.variable_upper_bounds()[
col] != 0.0) {
890 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
899 bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
900 const LinearProgram& lp) {
903 bool objective_is_unchanged =
true;
904 objective_.
resize(num_cols_, 0.0);
905 DCHECK_EQ(num_cols_, lp.num_variables());
906 if (lp.IsMaximizationProblem()) {
908 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
910 if (objective_[
col] != coeff) {
911 objective_is_unchanged =
false;
913 objective_[
col] = coeff;
915 objective_offset_ = -lp.objective_offset();
916 objective_scaling_factor_ = -lp.objective_scaling_factor();
918 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
919 if (objective_[
col] != lp.objective_coefficients()[
col]) {
920 objective_is_unchanged =
false;
924 if (!objective_is_unchanged) {
925 objective_ = lp.objective_coefficients();
927 objective_offset_ = lp.objective_offset();
928 objective_scaling_factor_ = lp.objective_scaling_factor();
930 return objective_is_unchanged;
933 void RevisedSimplex::InitializeObjectiveLimit(
const LinearProgram& lp) {
934 objective_limit_reached_ =
false;
935 DCHECK(std::isfinite(objective_offset_));
936 DCHECK(std::isfinite(objective_scaling_factor_));
937 DCHECK_NE(0.0, objective_scaling_factor_);
940 for (
const bool set_dual : {
true,
false}) {
952 const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
953 ? parameters_.objective_lower_limit()
954 : parameters_.objective_upper_limit();
956 limit / objective_scaling_factor_ - objective_offset_;
958 dual_objective_limit_ = shifted_limit;
960 primal_objective_limit_ = shifted_limit;
970 Status RevisedSimplex::CreateInitialBasis() {
981 int num_free_variables = 0;
985 VLOG(1) <<
"Number of free variables in the problem: "
986 << num_free_variables;
991 for (RowIndex
row(0);
row < num_rows_; ++
row) {
992 basis[
row] = SlackColIndex(
row);
999 if (!parameters_.use_dual_simplex() &&
1000 parameters_.initial_basis() != GlopParameters::MAROS &&
1001 parameters_.exploit_singleton_column_in_initial_basis()) {
1005 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1011 SetNonBasicVariableStatusAndDeriveValue(
col,
1015 SetNonBasicVariableStatusAndDeriveValue(
col,
1022 ComputeVariableValuesError();
1031 UseSingletonColumnInInitialBasis(&basis);
1034 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1036 basis[
row] = SlackColIndex(
row);
1042 if (parameters_.initial_basis() == GlopParameters::NONE) {
1043 return InitializeFirstBasis(basis);
1045 if (parameters_.initial_basis() == GlopParameters::MAROS) {
1046 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1048 if (parameters_.use_dual_simplex()) {
1051 initial_basis.GetDualMarosBasis(num_cols_, &basis);
1053 initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1055 int number_changed = 0;
1056 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1057 if (basis[
row] != SlackColIndex(
row)) {
1061 VLOG(1) <<
"Number of Maros basis changes: " << number_changed;
1062 }
else if (parameters_.initial_basis() == GlopParameters::BIXBY ||
1063 parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1065 int num_fixed_variables = 0;
1066 for (RowIndex
row(0);
row < basis.size(); ++
row) {
1067 const ColIndex
col = basis[
row];
1070 ++num_fixed_variables;
1074 if (num_fixed_variables == 0) {
1075 VLOG(1) <<
"Crash is set to " << parameters_.initial_basis()
1076 <<
" but there is no equality rows to remove from initial all "
1080 VLOG(1) <<
"Trying to remove " << num_fixed_variables
1081 <<
" fixed variables from the initial basis.";
1082 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1085 if (parameters_.initial_basis() == GlopParameters::BIXBY) {
1086 if (parameters_.use_scaling()) {
1087 initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1089 VLOG(1) <<
"Bixby initial basis algorithm requires the problem "
1090 <<
"to be scaled. Skipping Bixby's algorithm.";
1092 }
else if (parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1095 if (parameters_.use_dual_simplex()) {
1098 initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1100 initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1103 const Status status = InitializeFirstBasis(basis);
1107 VLOG(1) <<
"Reverting to all slack basis.";
1109 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1110 basis[
row] = SlackColIndex(
row);
1116 LOG(
WARNING) <<
"Unsupported initial_basis parameters: "
1117 << parameters_.initial_basis();
1120 return InitializeFirstBasis(basis);
1123 Status RevisedSimplex::InitializeFirstBasis(
const RowToColMapping& basis) {
1129 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1131 basis_[
row] = SlackColIndex(
row);
1145 if (condition_number_ub > parameters_.initial_condition_number_threshold()) {
1146 const std::string error_message =
1147 absl::StrCat(
"The matrix condition number upper bound is too high: ",
1148 condition_number_ub);
1149 VLOG(1) << error_message;
1154 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1157 DCHECK(BasisIsConsistent());
1165 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1167 VLOG(1) << absl::StrCat(
1168 "The primal residual of the initial basis is above the tolerance, ",
1175 Status RevisedSimplex::Initialize(
const LinearProgram& lp) {
1176 parameters_ = initial_parameters_;
1177 PropagateParameters();
1184 ColIndex num_new_cols(0);
1185 bool only_change_is_new_rows =
false;
1186 bool only_change_is_new_cols =
false;
1187 bool matrix_is_unchanged =
true;
1188 bool only_new_bounds =
false;
1189 if (solution_state_.
IsEmpty() || !notify_that_matrix_is_unchanged_) {
1190 matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1191 lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols);
1192 only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1193 OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1196 CHECK(InitializeMatrixAndTestIfUnchanged(
1197 lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols));
1199 notify_that_matrix_is_unchanged_ =
false;
1202 const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1204 const bool bounds_are_unchanged =
1206 lp.variable_lower_bounds(), lp.variable_upper_bounds());
1211 if (matrix_is_unchanged && parameters_.allow_simplex_algorithm_change()) {
1212 if (objective_is_unchanged && !bounds_are_unchanged) {
1213 parameters_.set_use_dual_simplex(
true);
1214 PropagateParameters();
1216 if (bounds_are_unchanged && !objective_is_unchanged) {
1217 parameters_.set_use_dual_simplex(
false);
1218 PropagateParameters();
1222 InitializeObjectiveLimit(lp);
1238 bool solve_from_scratch =
true;
1241 if (!solution_state_.
IsEmpty() && !solution_state_has_been_set_externally_) {
1242 if (!parameters_.use_dual_simplex()) {
1247 dual_edge_norms_.
Clear();
1248 dual_pricing_vector_.
clear();
1249 if (matrix_is_unchanged && bounds_are_unchanged) {
1253 solve_from_scratch =
false;
1254 }
else if (only_change_is_new_cols && only_new_bounds) {
1259 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1260 for (ColIndex& col_ref : basis_) {
1261 if (col_ref >= first_new_col) {
1262 col_ref += num_new_cols;
1269 primal_edge_norms_.
Clear();
1271 solve_from_scratch =
false;
1277 primal_edge_norms_.
Clear();
1278 if (objective_is_unchanged) {
1279 if (matrix_is_unchanged) {
1280 if (!bounds_are_unchanged) {
1282 first_slack_col_, ColIndex(0), solution_state_);
1286 solve_from_scratch =
false;
1287 }
else if (only_change_is_new_rows) {
1291 first_slack_col_, ColIndex(0), solution_state_);
1298 dual_pricing_vector_.
clear();
1301 if (InitializeFirstBasis(basis_).ok()) {
1302 solve_from_scratch =
false;
1311 const bool log_info = parameters_.log_search_progress() ||
VLOG_IS_ON(1);
1312 if (solve_from_scratch && !solution_state_.
IsEmpty()) {
1324 basis_factorization_.
Clear();
1326 primal_edge_norms_.
Clear();
1327 dual_edge_norms_.
Clear();
1328 dual_pricing_vector_.
clear();
1333 if (InitializeFirstBasis(basis_).ok()) {
1334 solve_from_scratch =
false;
1337 LOG(
INFO) <<
"RevisedSimplex is not using the warm start "
1338 "basis because it is not factorizable.";
1343 if (solve_from_scratch) {
1344 if (log_info)
LOG(
INFO) <<
"Solve from scratch.";
1345 basis_factorization_.
Clear();
1347 primal_edge_norms_.
Clear();
1348 dual_edge_norms_.
Clear();
1349 dual_pricing_vector_.
clear();
1352 if (log_info)
LOG(
INFO) <<
"Incremental solve.";
1354 DCHECK(BasisIsConsistent());
1358 void RevisedSimplex::DisplayBasicVariableStatistics() {
1361 int num_fixed_variables = 0;
1362 int num_free_variables = 0;
1363 int num_variables_at_bound = 0;
1364 int num_slack_variables = 0;
1365 int num_infeasible_variables = 0;
1371 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1372 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1373 const ColIndex
col = basis_[
row];
1376 ++num_free_variables;
1380 ++num_infeasible_variables;
1382 if (
col >= first_slack_col_) {
1383 ++num_slack_variables;
1386 ++num_fixed_variables;
1389 ++num_variables_at_bound;
1393 VLOG(1) <<
"Basis size: " << num_rows_;
1394 VLOG(1) <<
"Number of basic infeasible variables: "
1395 << num_infeasible_variables;
1396 VLOG(1) <<
"Number of basic slack variables: " << num_slack_variables;
1397 VLOG(1) <<
"Number of basic variables at bound: " << num_variables_at_bound;
1398 VLOG(1) <<
"Number of basic fixed variables: " << num_fixed_variables;
1399 VLOG(1) <<
"Number of basic free variables: " << num_free_variables;
1402 void RevisedSimplex::SaveState() {
1405 solution_state_has_been_set_externally_ =
false;
1408 RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1410 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1412 contains_data[e.row()] =
true;
1415 RowIndex num_empty_rows(0);
1416 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1417 if (!contains_data[
row]) {
1419 VLOG(1) <<
"Row " <<
row <<
" is empty.";
1422 return num_empty_rows;
1425 ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1426 ColIndex num_empty_cols(0);
1427 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1430 VLOG(1) <<
"Column " <<
col <<
" is empty.";
1433 return num_empty_cols;
1436 void RevisedSimplex::CorrectErrorsOnVariableValues() {
1448 if (primal_residual >= parameters_.harris_tolerance_ratio() *
1449 parameters_.primal_feasibility_tolerance()) {
1451 VLOG(1) <<
"Primal infeasibility (bounds error) = "
1453 <<
", Primal residual |A.x - b| = "
1458 void RevisedSimplex::ComputeVariableValuesError() {
1462 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1468 void RevisedSimplex::ComputeDirection(ColIndex
col) {
1472 direction_infinity_norm_ = 0.0;
1475 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1479 direction_infinity_norm_ =
1484 for (
const auto e : direction_) {
1485 direction_infinity_norm_ =
1486 std::max(direction_infinity_norm_, std::abs(e.coefficient()));
1490 num_rows_ == 0 ? 0.0
1491 :
static_cast<double>(direction_.non_zeros.size()) /
1492 static_cast<double>(num_rows_.value())));
1495 Fractional RevisedSimplex::ComputeDirectionError(ColIndex
col) {
1498 for (
const auto e : direction_) {
1505 template <
bool is_entering_reduced_cost_positive>
1508 RowIndex
row)
const {
1509 const ColIndex
col = basis_[
row];
1514 if (is_entering_reduced_cost_positive) {
1515 if (direction > 0.0) {
1521 if (direction > 0.0) {
1529 template <
bool is_entering_reduced_cost_positive>
1530 Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1531 Fractional bound_flip_ratio, SparseColumn* leaving_candidates)
const {
1534 parameters_.harris_tolerance_ratio() *
1535 parameters_.primal_feasibility_tolerance();
1536 const Fractional minimum_delta = parameters_.degenerate_ministep_factor() *
1537 parameters_.primal_feasibility_tolerance();
1543 leaving_candidates->Clear();
1550 ? parameters_.minimum_acceptable_pivot()
1551 : parameters_.ratio_test_zero_threshold();
1555 for (
const auto e : direction_) {
1556 const Fractional magnitude = std::abs(e.coefficient());
1557 if (magnitude <= threshold)
continue;
1560 if (
ratio <= harris_ratio) {
1561 leaving_candidates->SetCoefficient(e.row(),
ratio);
1573 harris_ratio =
std::min(harris_ratio,
1574 std::max(minimum_delta / magnitude,
1575 ratio + harris_tolerance / magnitude));
1578 return harris_ratio;
1591 if (current >= 0.0) {
1592 return candidate >= 0.0 && candidate <= current;
1594 return candidate >= current;
1602 Status RevisedSimplex::ChooseLeavingVariableRow(
1603 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1613 int stats_num_leaving_choices = 0;
1614 equivalent_leaving_choices_.clear();
1618 stats_num_leaving_choices = 0;
1622 const Fractional entering_value = variable_values_.
Get(entering_col);
1624 (reduced_cost > 0.0) ? entering_value -
lower_bounds[entering_col]
1632 (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1633 current_ratio, &leaving_candidates_)
1634 : ComputeHarrisRatioAndLeavingCandidates<false>(
1635 current_ratio, &leaving_candidates_);
1640 if (current_ratio <= harris_ratio) {
1642 *step_length = current_ratio;
1652 stats_num_leaving_choices = 0;
1654 equivalent_leaving_choices_.clear();
1657 if (
ratio > harris_ratio)
continue;
1658 ++stats_num_leaving_choices;
1659 const RowIndex
row = e.row();
1664 const Fractional candidate_magnitude = std::abs(direction_[
row]);
1665 if (candidate_magnitude < pivot_magnitude)
continue;
1666 if (candidate_magnitude == pivot_magnitude) {
1667 if (!IsRatioMoreOrEquallyStable(
ratio, current_ratio))
continue;
1668 if (
ratio == current_ratio) {
1670 equivalent_leaving_choices_.push_back(
row);
1674 equivalent_leaving_choices_.clear();
1675 current_ratio =
ratio;
1676 pivot_magnitude = candidate_magnitude;
1681 if (!equivalent_leaving_choices_.empty()) {
1682 equivalent_leaving_choices_.push_back(*leaving_row);
1684 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1685 0, equivalent_leaving_choices_.size() - 1)(random_)];
1697 if (current_ratio <= 0.0) {
1701 parameters_.degenerate_ministep_factor() *
1702 parameters_.primal_feasibility_tolerance();
1703 *step_length = minimum_delta / pivot_magnitude;
1705 *step_length = current_ratio;
1712 TestPivot(entering_col, *leaving_row);
1725 if (pivot_magnitude <
1726 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
1731 VLOG(1) <<
"Refactorizing to avoid pivoting by "
1732 << direction_[*leaving_row]
1733 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1734 <<
" reduced cost = " << reduced_cost;
1735 *refactorize =
true;
1745 VLOG(1) <<
"Couldn't avoid pivoting by " << direction_[*leaving_row]
1746 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1747 <<
" reduced cost = " << reduced_cost;
1748 DCHECK_GE(std::abs(direction_[*leaving_row]),
1749 parameters_.minimum_acceptable_pivot());
1757 const bool is_reduced_cost_positive = (reduced_cost > 0.0);
1758 const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
1759 *
target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
1766 ratio_test_stats_.leaving_choices.Add(stats_num_leaving_choices);
1767 if (!equivalent_leaving_choices_.empty()) {
1768 ratio_test_stats_.num_perfect_ties.Add(
1769 equivalent_leaving_choices_.size());
1772 ratio_test_stats_.abs_used_pivot.Add(std::abs(direction_[*leaving_row]));
1794 bool operator<(
const BreakPoint& other)
const {
1795 if (
ratio == other.ratio) {
1797 return row > other.row;
1801 return ratio > other.ratio;
1812 void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
1813 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1814 RowIndex* leaving_row,
Fractional* step_length,
1827 const Fractional entering_value = variable_values_.
Get(entering_col);
1828 Fractional current_ratio = (reduced_cost > 0.0)
1833 std::vector<BreakPoint> breakpoints;
1834 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1835 for (
const auto e : direction_) {
1837 reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
1838 const Fractional magnitude = std::abs(direction);
1839 if (magnitude < tolerance)
continue;
1854 const ColIndex
col = basis_[e.row()];
1865 if (to_lower >= 0.0 && to_lower < current_ratio) {
1866 breakpoints.push_back(
1867 BreakPoint(e.row(), to_lower, magnitude,
lower_bound));
1869 if (to_upper >= 0.0 && to_upper < current_ratio) {
1870 breakpoints.push_back(
1871 BreakPoint(e.row(), to_upper, magnitude,
upper_bound));
1877 std::make_heap(breakpoints.begin(), breakpoints.end());
1881 Fractional improvement = std::abs(reduced_cost);
1884 while (!breakpoints.empty()) {
1885 const BreakPoint top = breakpoints.front();
1893 if (top.coeff_magnitude > best_magnitude) {
1894 *leaving_row = top.row;
1895 current_ratio = top.ratio;
1896 best_magnitude = top.coeff_magnitude;
1902 improvement -= top.coeff_magnitude;
1903 if (improvement <= 0.0)
break;
1904 std::pop_heap(breakpoints.begin(), breakpoints.end());
1905 breakpoints.pop_back();
1911 parameters_.small_pivot_threshold() * direction_infinity_norm_;
1912 if (best_magnitude < threshold && !basis_factorization_.
IsRefactorized()) {
1913 *refactorize =
true;
1917 *step_length = current_ratio;
1921 Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
1930 if (dual_prices_.
Size() == 0) {
1941 const ColIndex leaving_col = basis_[*leaving_row];
1961 if (
cost == 0.0)
return false;
1971 template <
bool use_dense_update>
1972 void RevisedSimplex::OnDualPriceChange(
const DenseColumn& squared_norm,
1976 const bool is_candidate =
1977 IsDualPhaseILeavingCandidate(price, type, threshold);
1979 if (use_dense_update) {
1989 void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
1990 ColIndex entering_col) {
1993 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2003 dual_pricing_vector_[leaving_row] / direction_[leaving_row];
2004 for (
const auto e : direction_) {
2005 dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2006 OnDualPriceChange(squared_norms, e.row(), variable_type[basis_[e.row()]],
2009 dual_pricing_vector_[leaving_row] = step;
2013 dual_pricing_vector_[leaving_row] -=
2014 dual_infeasibility_improvement_direction_[entering_col];
2015 if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2016 --num_dual_infeasible_positions_;
2018 dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2021 dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2024 OnDualPriceChange(squared_norms, leaving_row, variable_type[entering_col],
2028 template <
typename Cols>
2029 void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2032 bool something_to_do =
false;
2037 for (ColIndex
col : cols) {
2040 (can_increase.IsSet(
col) && reduced_cost < -tolerance) ? 1.0
2041 : (can_decrease.IsSet(
col) && reduced_cost > tolerance) ? -1.0
2043 if (sign != dual_infeasibility_improvement_direction_[
col]) {
2045 --num_dual_infeasible_positions_;
2046 }
else if (dual_infeasibility_improvement_direction_[
col] == 0.0) {
2047 ++num_dual_infeasible_positions_;
2049 if (!something_to_do) {
2050 initially_all_zero_scratchpad_.
values.
resize(num_rows_, 0.0);
2052 initially_all_zero_scratchpad_.
non_zeros.clear();
2053 something_to_do =
true;
2057 num_update_price_operations_ +=
2060 col, sign - dual_infeasibility_improvement_direction_[
col],
2061 &initially_all_zero_scratchpad_);
2062 dual_infeasibility_improvement_direction_[
col] = sign;
2065 if (something_to_do) {
2071 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2072 basis_factorization_.
RightSolve(&initially_all_zero_scratchpad_);
2073 if (initially_all_zero_scratchpad_.
non_zeros.empty()) {
2075 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2076 if (initially_all_zero_scratchpad_[
row] == 0.0)
continue;
2077 dual_pricing_vector_[
row] += initially_all_zero_scratchpad_[
row];
2078 OnDualPriceChange<
true>(
2079 squared_norms,
row, variable_type[basis_[
row]], threshold);
2083 for (
const auto e : initially_all_zero_scratchpad_) {
2084 dual_pricing_vector_[e.row()] += e.coefficient();
2085 OnDualPriceChange(squared_norms, e.row(),
2086 variable_type[basis_[e.row()]], threshold);
2087 initially_all_zero_scratchpad_[e.row()] = 0.0;
2090 initially_all_zero_scratchpad_.non_zeros.clear();
2094 Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2095 RowIndex* leaving_row,
Fractional* cost_variation,
2115 dual_pricing_vector_.
empty()) {
2117 num_dual_infeasible_positions_ = 0;
2120 dual_infeasibility_improvement_direction_.
AssignToZero(num_cols_);
2121 DualPhaseIUpdatePriceOnReducedCostChange(
2131 if (num_dual_infeasible_positions_ == 0)
return Status::OK();
2138 *cost_variation = dual_pricing_vector_[*leaving_row];
2139 const ColIndex leaving_col = basis_[*leaving_row];
2140 if (*cost_variation < 0.0) {
2149 template <
typename BoxedVariableCols>
2150 void RevisedSimplex::MakeBoxedVariableDualFeasible(
2151 const BoxedVariableCols& cols,
bool update_basic_values) {
2153 std::vector<ColIndex> changed_cols;
2163 const Fractional dual_feasibility_tolerance =
2166 for (
const ColIndex
col : cols) {
2175 if (reduced_cost > dual_feasibility_tolerance &&
2179 changed_cols.push_back(
col);
2180 }
else if (reduced_cost < -dual_feasibility_tolerance &&
2184 changed_cols.push_back(
col);
2188 if (!changed_cols.empty()) {
2189 iteration_stats_.num_dual_flips.Add(changed_cols.size());
2191 update_basic_values);
2195 Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2200 const ColIndex leaving_col = basis_[leaving_row];
2201 const Fractional leaving_variable_value = variable_values_.
Get(leaving_col);
2211 return unscaled_step / direction_[leaving_row];
2214 bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2215 VLOG(1) <<
"Test pivot.";
2217 const ColIndex leaving_col = basis_[leaving_row];
2218 basis_[leaving_row] = entering_col;
2222 CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2224 basis_[leaving_row] = leaving_col;
2231 void RevisedSimplex::PermuteBasis() {
2238 if (col_perm.empty())
return;
2244 if (!dual_pricing_vector_.
empty()) {
2259 Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2260 RowIndex leaving_row,
2265 const ColIndex leaving_col = basis_[leaving_row];
2273 ratio_test_stats_.bound_shift.Add(variable_values_.
Get(leaving_col) -
2276 UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2278 const Fractional pivot_from_direction = direction_[leaving_row];
2282 std::abs(pivot_from_update_row - pivot_from_direction);
2283 if (diff > parameters_.refactorization_threshold() *
2284 (1 + std::abs(pivot_from_direction))) {
2285 VLOG(1) <<
"Refactorizing: imprecise pivot " << pivot_from_direction
2286 <<
" diff = " << diff;
2290 basis_factorization_.
Update(entering_col, leaving_row, direction_));
2298 bool RevisedSimplex::NeedsBasisRefactorization(
bool refactorize) {
2301 const GlopParameters::PricingRule pricing_rule =
2302 feasibility_phase_ ? parameters_.feasibility_rule()
2303 : parameters_.optimization_rule();
2304 if (parameters_.use_dual_simplex()) {
2306 DCHECK_EQ(pricing_rule, GlopParameters::STEEPEST_EDGE);
2309 if (pricing_rule == GlopParameters::STEEPEST_EDGE &&
2317 Status RevisedSimplex::RefactorizeBasisIfNeeded(
bool* refactorize) {
2319 if (NeedsBasisRefactorization(*refactorize)) {
2324 *refactorize =
false;
2329 if (
col >= integrality_scale_.
size()) {
2330 integrality_scale_.
resize(
col + 1, 0.0);
2332 integrality_scale_[
col] = scale;
2337 Cleanup update_deterministic_time_on_return(
2344 std::vector<ColIndex> candidates;
2350 bool refactorize =
false;
2353 for (
int i = 0; i < 10; ++i) {
2356 if (num_pivots >= 5)
break;
2357 if (candidates.empty())
break;
2361 std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2362 const ColIndex entering_col = candidates[
index];
2364 candidates.pop_back();
2375 ComputeDirection(entering_col);
2377 RowIndex leaving_row;
2379 bool local_refactorize =
false;
2381 ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2384 if (local_refactorize)
continue;
2386 if (std::abs(step_length) <= 1e-6)
continue;
2387 if (leaving_row !=
kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2390 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2396 const auto get_diff = [
this](ColIndex
col,
Fractional old_value,
2398 if (
col >= integrality_scale_.
size() || integrality_scale_[
col] == 0.0) {
2402 return (std::abs(new_value * s - std::round(new_value * s)) -
2403 std::abs(old_value * s - std::round(old_value * s)));
2405 Fractional diff = get_diff(entering_col, variable_values_.
Get(entering_col),
2406 variable_values_.
Get(entering_col) + step);
2407 for (
const auto e : direction_) {
2408 const ColIndex
col = basis_[e.row()];
2410 const Fractional new_value = old_value - e.coefficient() * step;
2411 diff += get_diff(
col, old_value, new_value);
2415 if (diff > -1e-2)
continue;
2425 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2427 }
else if (step < 0.0) {
2428 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2435 const ColIndex leaving_col = basis_[leaving_row];
2438 entering_col, leaving_col, leaving_row, direction_, &update_row_);
2442 const Fractional dir = -direction_[leaving_row] * step;
2443 const bool is_degenerate =
2447 if (!is_degenerate) {
2451 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2454 VLOG(1) <<
"Polish num_pivots: " << num_pivots <<
" gain:" << total_gain;
2473 Status RevisedSimplex::Minimize(TimeLimit*
time_limit) {
2475 Cleanup update_deterministic_time_on_return(
2477 num_consecutive_degenerate_iterations_ = 0;
2478 DisplayIterationInfo();
2479 bool refactorize =
false;
2485 if (feasibility_phase_) {
2501 CorrectErrorsOnVariableValues();
2502 DisplayIterationInfo();
2504 if (feasibility_phase_) {
2516 if (!feasibility_phase_ &&
2517 ComputeObjectiveValue() < primal_objective_limit_) {
2518 VLOG(1) <<
"Stopping the primal simplex because"
2519 <<
" the objective limit " << primal_objective_limit_
2520 <<
" has been reached.";
2522 objective_limit_reached_ =
true;
2525 }
else if (feasibility_phase_) {
2539 if (feasibility_phase_) {
2542 if (primal_infeasibility <
2543 parameters_.primal_feasibility_tolerance()) {
2546 VLOG(1) <<
"Infeasible problem! infeasibility = "
2547 << primal_infeasibility;
2555 VLOG(1) <<
"Optimal reached, double checking...";
2565 ComputeDirection(entering_col);
2569 entering_col, direction_, &reduced_cost)) {
2571 VLOG(1) <<
"Skipping col #" << entering_col <<
" whose reduced cost is "
2583 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2589 RowIndex leaving_row;
2591 if (feasibility_phase_) {
2592 PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2593 &refactorize, &leaving_row,
2597 ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2600 if (refactorize)
continue;
2605 VLOG(1) <<
"Infinite step length, double checking...";
2609 if (feasibility_phase_) {
2611 VLOG(1) <<
"Unbounded feasibility problem !?";
2614 VLOG(1) <<
"Unbounded problem.";
2617 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2618 const ColIndex
col = basis_[
row];
2619 solution_primal_ray_[
col] = -direction_[
row];
2621 solution_primal_ray_[entering_col] = 1.0;
2629 Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2630 if (feasibility_phase_ && leaving_row !=
kInvalidRow) {
2640 step = ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2644 const ColIndex leaving_col =
2650 bool is_degenerate =
false;
2652 Fractional dir = -direction_[leaving_row] * step;
2660 if (!is_degenerate) {
2661 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
2670 entering_col, basis_[leaving_row], leaving_row, direction_,
2673 direction_, &update_row_);
2675 if (!is_degenerate) {
2684 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2686 if (is_degenerate) {
2687 timer.AlsoUpdate(&iteration_stats_.degenerate);
2689 timer.AlsoUpdate(&iteration_stats_.normal);
2698 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2700 }
else if (step < 0.0) {
2701 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2708 if (feasibility_phase_ && leaving_row !=
kInvalidRow) {
2714 &objective_[leaving_col]);
2719 if (step_length == 0.0) {
2720 num_consecutive_degenerate_iterations_++;
2722 if (num_consecutive_degenerate_iterations_ > 0) {
2723 iteration_stats_.degenerate_run_size.Add(
2724 num_consecutive_degenerate_iterations_);
2725 num_consecutive_degenerate_iterations_ = 0;
2730 if (num_consecutive_degenerate_iterations_ > 0) {
2731 iteration_stats_.degenerate_run_size.Add(
2732 num_consecutive_degenerate_iterations_);
2748 Status RevisedSimplex::DualMinimize(
bool feasibility_phase,
2750 Cleanup update_deterministic_time_on_return(
2752 num_consecutive_degenerate_iterations_ = 0;
2753 bool refactorize =
false;
2755 bound_flip_candidates_.clear();
2758 RowIndex leaving_row;
2763 ColIndex entering_col;
2771 const bool old_refactorize_value = refactorize;
2788 !old_refactorize_value) {
2791 if (dual_residual_error >
2793 VLOG(1) <<
"Recomputing reduced costs. Dual residual = "
2794 << dual_residual_error;
2809 if (!feasibility_phase) {
2810 MakeBoxedVariableDualFeasible(
2823 if (!feasibility_phase_ && dual_objective_limit_ !=
kInfinity &&
2824 ComputeObjectiveValue() > dual_objective_limit_) {
2825 VLOG(1) <<
"Stopping the dual simplex because"
2826 <<
" the objective limit " << dual_objective_limit_
2827 <<
" has been reached.";
2829 objective_limit_reached_ =
true;
2835 DisplayIterationInfo();
2839 if (!feasibility_phase) {
2842 MakeBoxedVariableDualFeasible(bound_flip_candidates_,
2844 bound_flip_candidates_.clear();
2852 if (feasibility_phase) {
2864 VLOG(1) <<
"Optimal reached, double checking.";
2870 if (feasibility_phase) {
2875 if (num_dual_infeasible_positions_ == 0) {
2878 VLOG(1) <<
"DUAL infeasible in dual phase I.";
2889 if (feasibility_phase) {
2896 &bound_flip_candidates_, &entering_col));
2902 VLOG(1) <<
"No entering column. Double checking...";
2908 if (feasibility_phase) {
2910 VLOG(1) <<
"Unbounded dual feasibility problem !?";
2914 solution_dual_ray_ =
2917 solution_dual_ray_row_combination_.
AssignToZero(num_cols_);
2919 solution_dual_ray_row_combination_[
col] =
2922 if (cost_variation < 0) {
2924 ChangeSign(&solution_dual_ray_row_combination_);
2935 if (std::abs(entering_coeff) < parameters_.dual_small_pivot_threshold() &&
2937 VLOG(1) <<
"Trying not to pivot by " << entering_coeff;
2943 ComputeDirection(entering_col);
2949 if (std::abs(direction_[leaving_row]) <
2950 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
2952 VLOG(1) <<
"Trying not pivot by " << entering_coeff <<
" ("
2953 << direction_[leaving_row]
2954 <<
") because the direction has a norm of "
2955 << direction_infinity_norm_;
2967 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2982 const bool increasing_rc_is_needed =
2983 (cost_variation > 0.0) == (entering_coeff > 0.0);
2989 timer.AlsoUpdate(&iteration_stats_.degenerate);
2991 timer.AlsoUpdate(&iteration_stats_.normal);
3003 entering_col, leaving_row, direction_,
3009 if (feasibility_phase) {
3010 DualPhaseIUpdatePrice(leaving_row, entering_col);
3013 ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
3018 const ColIndex leaving_col = basis_[leaving_row];
3020 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3030 if (std::abs(primal_step) * parameters_.primal_feasibility_tolerance() >
3039 ColIndex RevisedSimplex::SlackColIndex(RowIndex
row)
const {
3047 result.append(iteration_stats_.StatString());
3048 result.append(ratio_test_stats_.StatString());
3049 result.append(entering_variable_.
StatString());
3052 result.append(variable_values_.
StatString());
3053 result.append(primal_edge_norms_.
StatString());
3054 result.append(dual_edge_norms_.
StatString());
3056 result.append(basis_factorization_.
StatString());
3061 void RevisedSimplex::DisplayAllStats() {
3062 if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3064 absl::FPrintF(stderr,
"%s", GetPrettySolverStats());
3068 Fractional RevisedSimplex::ComputeObjectiveValue()
const {
3074 Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue()
const {
3078 return objective_scaling_factor_ * (sum + objective_offset_);
3083 deterministic_random_.seed(
parameters.random_seed());
3087 PropagateParameters();
3090 void RevisedSimplex::PropagateParameters() {
3100 void RevisedSimplex::DisplayIterationInfo()
const {
3101 if (parameters_.log_search_progress() ||
VLOG_IS_ON(1)) {
3104 if (!feasibility_phase_) {
3109 objective = ComputeInitialProblemObjectiveValue();
3111 }
else if (parameters_.use_dual_simplex()) {
3112 if (parameters_.use_dedicated_dual_feasibility_algorithm()) {
3120 name =
"sum_dual_infeasibilities";
3123 name =
"sum_primal_infeasibilities";
3126 const int iter = feasibility_phase_
3128 : num_iterations_ - num_feasibility_iterations_;
3129 LOG(
INFO) << (feasibility_phase_ ?
"Feasibility" :
"Optimization")
3130 <<
" phase, iteration # " << iter <<
", " <<
name <<
" = "
3131 << absl::StrFormat(
"%.15E", objective);
3135 void RevisedSimplex::DisplayErrors()
const {
3136 if (parameters_.log_search_progress() ||
VLOG_IS_ON(1)) {
3137 LOG(
INFO) <<
"Primal infeasibility (bounds) = "
3139 LOG(
INFO) <<
"Primal residual |A.x - b| = "
3141 LOG(
INFO) <<
"Dual infeasibility (reduced costs) = "
3143 LOG(
INFO) <<
"Dual residual |c_B - y.B| = "
3150 std::string StringifyMonomialWithFlags(
const Fractional a,
3151 const std::string& x) {
3153 a, x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3159 std::string StringifyWithFlags(
const Fractional x) {
3161 absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3166 std::string RevisedSimplex::SimpleVariableInfo(ColIndex
col)
const {
3172 absl::StrAppendFormat(&output,
"%d (%s) = %s, %s, %s, [%s,%s]",
col.value(),
3173 variable_name_[
col],
3174 StringifyWithFlags(variable_values_.
Get(
col)),
3182 void RevisedSimplex::DisplayInfoOnVariables()
const {
3184 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3188 objective_coefficient * variable_value;
3189 VLOG(3) << SimpleVariableInfo(
col) <<
". " << variable_name_[
col] <<
" = "
3190 << StringifyWithFlags(variable_value) <<
" * "
3191 << StringifyWithFlags(objective_coefficient)
3192 <<
"(obj) = " << StringifyWithFlags(objective_contribution);
3194 VLOG(3) <<
"------";
3198 void RevisedSimplex::DisplayVariableBounds() {
3203 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3204 switch (variable_type[
col]) {
3208 VLOG(3) << variable_name_[
col]
3212 VLOG(3) << variable_name_[
col]
3217 <<
" <= " << variable_name_[
col]
3221 VLOG(3) << variable_name_[
col] <<
" = "
3225 LOG(DFATAL) <<
"Column " <<
col <<
" has no meaningful status.";
3235 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3236 ComputeDirection(
col);
3237 for (
const auto e : direction_) {
3238 if (column_scales ==
nullptr) {
3239 dictionary[e.row()].SetCoefficient(
col, e.coefficient());
3243 col < column_scales->
size() ? (*column_scales)[
col] : 1.0;
3245 ? (*column_scales)[
GetBasis(e.row())]
3247 dictionary[e.row()].SetCoefficient(
3248 col, direction_[e.row()] * (numerator / denominator));
3257 Status status = Initialize(linear_program);
3260 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3264 void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3267 DisplayInfoOnVariables();
3269 std::string output =
"z = " + StringifyWithFlags(ComputeObjectiveValue());
3272 absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[
col],
3273 variable_name_[
col]));
3275 VLOG(3) << output <<
";";
3277 const RevisedSimplexDictionary dictionary(
nullptr,
this);
3279 for (
const SparseRow&
row : dictionary) {
3281 ColIndex basic_col = basis_[r];
3282 absl::StrAppend(&output, variable_name_[basic_col],
" = ",
3283 StringifyWithFlags(variable_values_.
Get(basic_col)));
3284 for (
const SparseRowEntry e :
row) {
3285 if (e.col() != basic_col) {
3286 absl::StrAppend(&output,
3287 StringifyMonomialWithFlags(e.coefficient(),
3288 variable_name_[e.col()]));
3291 VLOG(3) << output <<
";";
3293 VLOG(3) <<
"------";
3294 DisplayVariableBounds();
3299 void RevisedSimplex::DisplayProblem()
const {
3303 DisplayInfoOnVariables();
3304 std::string output =
"min: ";
3305 bool has_objective =
false;
3306 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3308 has_objective |= (coeff != 0.0);
3309 absl::StrAppend(&output,
3310 StringifyMonomialWithFlags(coeff, variable_name_[
col]));
3312 if (!has_objective) {
3313 absl::StrAppend(&output,
" 0");
3315 VLOG(3) << output <<
";";
3316 for (RowIndex
row(0);
row < num_rows_; ++
row) {
3318 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3319 absl::StrAppend(&output,
3320 StringifyMonomialWithFlags(
3322 variable_name_[
col]));
3324 VLOG(3) << output <<
" = 0;";
3326 VLOG(3) <<
"------";
3330 void RevisedSimplex::AdvanceDeterministicTime(TimeLimit*
time_limit) {
3333 const double deterministic_time_delta =
3334 current_deterministic_time - last_deterministic_time_update_;
3335 time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3336 last_deterministic_time_update_ = current_deterministic_time;
3339 #undef DCHECK_COL_BOUNDS
3340 #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)
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)
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()
bool NeedsBasisRefactorization()
void ResizeOnNewRows(RowIndex new_size)
void SetParameters(const GlopParameters ¶meters)
std::string StatString() const
void AddOrUpdate(Index position, Fractional value)
void Remove(Index position)
void DenseAddOrUpdate(Index position, Fractional value)
void ClearAndResize(Index n)
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 IsInEquationForm() 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 ComputeMaximumDualResidual() const
void MakeReducedCostsPrecise()
bool AreReducedCostsRecomputed()
bool TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction, Fractional *reduced_cost)
bool AreReducedCostsPrecise()
bool IsValidPrimalEnteringCandidate(ColIndex col) const
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
bool HasCostShift() const
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables() const
const DenseRow & GetFullReducedCosts()
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
const DenseRow & GetReducedCosts()
Fractional GetDualFeasibilityTolerance() const
Fractional ComputeMaximumDualInfeasibility() const
const DenseColumn & GetDualValues()
void ClearAndRemoveCostShifts()
void UpdateDataOnBasisPermutation()
Fractional ComputeSumOfDualInfeasibilities() const
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
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()
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 RecomputeFullUpdateRow(RowIndex leaving_row)
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)
const DenseRow & GetDenseRow() const
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)
void ResetAllNonBasicVariableValues()
std::string StatString() const
const DenseBitRow & GetIsBasicBitRow() const
const DenseRow & GetVariableUpperBounds() const
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 DenseRow & GetVariableLowerBounds() const
const DenseBitRow & GetNotBasicBitRow() const
void InitializeToDefaultStatus()
const VariableStatusRow & GetStatusRow() const
void UpdateToBasicStatus(ColIndex col)
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)
SharedTimeLimit * time_limit
std::string StringifyMonomial(const Fractional a, const std::string &x, bool fraction)
bool IsRightMostSquareMatrixIdentity(const SparseMatrix &matrix)
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)