24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/str_format.h"
38 ABSL_FLAG(
bool, simplex_display_numbers_as_fractions,
false,
39 "Display numbers as fractions.");
40 ABSL_FLAG(
bool, simplex_stop_after_first_basis,
false,
41 "Stop after first basis has been computed.");
42 ABSL_FLAG(
bool, simplex_stop_after_feasibility,
false,
43 "Stop after first phase has been completed.");
44 ABSL_FLAG(
bool, simplex_display_stats,
false,
"Display algorithm statistics.");
54 explicit Cleanup(std::function<
void()> closure)
55 : closure_(std::move(closure)) {}
56 ~Cleanup() { closure_(); }
59 std::function<void()> closure_;
63 #define DCHECK_COL_BOUNDS(col) \
66 DCHECK_GT(num_cols_, col); \
69 #define DCHECK_ROW_BOUNDS(row) \
72 DCHECK_GT(num_rows_, row); \
89 basis_factorization_(&compact_matrix_, &basis_),
90 variables_info_(compact_matrix_, lower_bound_, upper_bound_),
91 variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
92 basis_factorization_),
93 dual_edge_norms_(basis_factorization_),
94 primal_edge_norms_(compact_matrix_, variables_info_,
95 basis_factorization_),
96 update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
97 basis_factorization_),
98 reduced_costs_(compact_matrix_,
objective_, basis_, variables_info_,
99 basis_factorization_, &random_),
100 entering_variable_(variables_info_, &random_, &reduced_costs_,
101 &primal_edge_norms_),
103 num_feasibility_iterations_(0),
104 num_optimization_iterations_(0),
106 feasibility_time_(0.0),
107 optimization_time_(0.0),
108 last_deterministic_time_update_(0.0),
111 function_stats_(
"SimplexFunctionStats"),
114 feasibility_phase_(true),
126 solution_state_ = state;
127 solution_state_has_been_set_externally_ =
true;
131 notify_that_matrix_is_unchanged_ =
true;
140 "The problem is not in the equations form.");
142 Cleanup update_deterministic_time_on_return(
147 const double start_time =
time_limit->GetElapsedTime();
150 dual_infeasibility_improvement_direction_.
clear();
154 feasibility_phase_ =
true;
156 num_feasibility_iterations_ = 0;
157 num_optimization_iterations_ = 0;
158 feasibility_time_ = 0.0;
159 optimization_time_ = 0.0;
166 solution_state_has_been_set_externally_ =
true;
169 ComputeNumberOfEmptyRows();
170 ComputeNumberOfEmptyColumns();
171 DisplayBasicVariableStatistics();
174 if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
179 const bool use_dual = parameters_.use_dual_simplex();
180 VLOG(1) <<
"------ " << (use_dual ?
"Dual simplex." :
"Primal simplex.");
181 VLOG(1) <<
"The matrix has " << compact_matrix_.
num_rows() <<
" rows, "
182 << compact_matrix_.
num_cols() <<
" columns, "
187 VLOG(1) <<
"------ First phase: feasibility.";
188 entering_variable_.
SetPricingRule(parameters_.feasibility_rule());
190 if (parameters_.perturb_costs_in_dual_simplex()) {
196 DisplayIterationInfo();
198 if (problem_status_ != ProblemStatus::DUAL_INFEASIBLE) {
221 DisplayIterationInfo();
224 if (problem_status_ != ProblemStatus::PRIMAL_INFEASIBLE) {
225 InitializeObjectiveAndTestIfUnchanged(lp);
236 feasibility_phase_ =
false;
237 feasibility_time_ =
time_limit->GetElapsedTime() - start_time;
238 entering_variable_.
SetPricingRule(parameters_.optimization_rule());
239 num_feasibility_iterations_ = num_iterations_;
241 VLOG(1) <<
"------ Second phase: optimization.";
255 for (
int num_optims = 0;
259 num_optims <= parameters_.max_number_of_reoptimizations() &&
260 !objective_limit_reached_ &&
261 (num_iterations_ == 0 ||
262 num_iterations_ < parameters_.max_number_of_iterations()) &&
264 !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
265 (problem_status_ == ProblemStatus::PRIMAL_FEASIBLE ||
266 problem_status_ == ProblemStatus::DUAL_FEASIBLE);
268 if (problem_status_ == ProblemStatus::PRIMAL_FEASIBLE) {
281 DCHECK(problem_status_ == ProblemStatus::PRIMAL_FEASIBLE ||
282 problem_status_ == ProblemStatus::DUAL_FEASIBLE ||
286 if (!integrality_scale_.
empty() &&
306 DisplayIterationInfo();
314 if (problem_status_ == ProblemStatus::DUAL_UNBOUNDED) {
315 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
319 VLOG(1) <<
"DUAL_UNBOUNDED was reported, but the residual and/or "
320 <<
"dual infeasibility is above the tolerance";
329 parameters_.solution_feasibility_tolerance();
331 solution_tolerance ||
333 VLOG(1) <<
"OPTIMAL was reported, yet one of the residuals is "
334 "above the solution feasibility tolerance after the "
335 "shift/perturbation are removed.";
336 if (parameters_.change_status_to_imprecise()) {
343 parameters_.primal_feasibility_tolerance();
345 parameters_.dual_feasibility_tolerance();
350 if (primal_infeasibility > primal_tolerance &&
351 dual_infeasibility > dual_tolerance) {
352 VLOG(1) <<
"OPTIMAL was reported, yet both of the infeasibility "
353 "are above the tolerance after the "
354 "shift/perturbation are removed.";
355 if (parameters_.change_status_to_imprecise()) {
358 }
else if (primal_infeasibility > primal_tolerance) {
359 VLOG(1) <<
"Re-optimizing with dual simplex ... ";
360 problem_status_ = ProblemStatus::DUAL_FEASIBLE;
361 }
else if (dual_infeasibility > dual_tolerance) {
362 VLOG(1) <<
"Re-optimizing with primal simplex ... ";
363 problem_status_ = ProblemStatus::PRIMAL_FEASIBLE;
373 if (parameters_.change_status_to_imprecise() &&
374 problem_status_ != ProblemStatus::DUAL_INFEASIBLE) {
375 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
379 }
else if (problem_status_ == ProblemStatus::DUAL_FEASIBLE ||
380 problem_status_ == ProblemStatus::DUAL_UNBOUNDED ||
381 problem_status_ == ProblemStatus::PRIMAL_INFEASIBLE) {
385 }
else if (problem_status_ == ProblemStatus::PRIMAL_FEASIBLE ||
386 problem_status_ == ProblemStatus::PRIMAL_UNBOUNDED ||
387 problem_status_ == ProblemStatus::DUAL_INFEASIBLE) {
396 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
405 if (problem_status_ == ProblemStatus::DUAL_UNBOUNDED ||
406 problem_status_ == ProblemStatus::PRIMAL_UNBOUNDED) {
407 solution_objective_value_ =
408 (problem_status_ == ProblemStatus::DUAL_UNBOUNDED) ?
kInfinity
411 solution_objective_value_ = -solution_objective_value_;
415 total_time_ =
time_limit->GetElapsedTime() - start_time;
416 optimization_time_ = total_time_ - feasibility_time_;
417 num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
424 return problem_status_;
428 return solution_objective_value_;
438 return variable_values_.
Get(
col);
442 return solution_reduced_costs_[
col];
446 return solution_reduced_costs_;
450 return solution_dual_values_[
row];
462 return -variable_values_.
Get(SlackColIndex(
row));
479 DCHECK_EQ(problem_status_, ProblemStatus::PRIMAL_UNBOUNDED);
480 return solution_primal_ray_;
483 DCHECK_EQ(problem_status_, ProblemStatus::DUAL_UNBOUNDED);
484 return solution_dual_ray_;
488 DCHECK_EQ(problem_status_, ProblemStatus::DUAL_UNBOUNDED);
489 return solution_dual_ray_row_combination_;
496 return basis_factorization_;
499 std::string RevisedSimplex::GetPrettySolverStats()
const {
500 return absl::StrFormat(
501 "Problem status : %s\n"
502 "Solving time : %-6.4g\n"
503 "Number of iterations : %u\n"
504 "Time for solvability (first phase) : %-6.4g\n"
505 "Number of iterations for solvability : %u\n"
506 "Time for optimization : %-6.4g\n"
507 "Number of iterations for optimization : %u\n"
508 "Stop after first basis : %d\n",
510 feasibility_time_, num_feasibility_iterations_, optimization_time_,
511 num_optimization_iterations_,
512 absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
523 void RevisedSimplex::SetVariableNames() {
524 variable_name_.
resize(num_cols_,
"");
525 for (ColIndex
col(0);
col < first_slack_col_; ++
col) {
526 const ColIndex var_index =
col + 1;
529 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
530 const ColIndex var_index =
col - first_slack_col_ + 1;
536 ColIndex
col)
const {
538 if (lower_bound_[
col] == upper_bound_[
col]) {
548 return std::abs(lower_bound_[
col]) <= std::abs(upper_bound_[
col])
553 void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
559 bool RevisedSimplex::BasisIsConsistent()
const {
562 for (RowIndex
row(0);
row < num_rows_; ++
row) {
563 const ColIndex
col = basis_[
row];
564 if (!is_basic.IsSet(
col))
return false;
567 ColIndex cols_in_basis(0);
568 ColIndex cols_not_in_basis(0);
569 for (ColIndex
col(0);
col < num_cols_; ++
col) {
570 cols_in_basis += is_basic.IsSet(
col);
571 cols_not_in_basis += !is_basic.IsSet(
col);
572 if (is_basic.IsSet(
col) !=
578 if (cols_not_in_basis != num_cols_ -
RowToColIndex(num_rows_))
return false;
584 void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
593 DCHECK_NE(basis_[basis_row], entering_col);
596 const ColIndex leaving_col = basis_[basis_row];
602 variables_info_.
Update(leaving_col, leaving_variable_status);
607 basis_[basis_row] = entering_col;
615 class ColumnComparator {
618 bool operator()(ColIndex col_a, ColIndex col_b)
const {
619 return value_[col_a] < value_[col_b];
636 void RevisedSimplex::UseSingletonColumnInInitialBasis(
RowToColMapping* basis) {
643 std::vector<ColIndex> singleton_column;
644 DenseRow cost_variation(num_cols_, 0.0);
645 for (ColIndex
col(0);
col < num_cols_; ++
col) {
647 if (lower_bound_[
col] == upper_bound_[
col])
continue;
649 if (variable_values_.
Get(
col) == lower_bound_[
col]) {
650 cost_variation[
col] = objective_[
col] / std::abs(slope);
652 cost_variation[
col] = -objective_[
col] / std::abs(slope);
654 singleton_column.push_back(
col);
656 if (singleton_column.empty())
return;
663 ColumnComparator comparator(cost_variation);
664 std::sort(singleton_column.begin(), singleton_column.end(), comparator);
665 DCHECK_LE(cost_variation[singleton_column.front()],
666 cost_variation[singleton_column.back()]);
674 for (
const ColIndex
col : singleton_column) {
686 if (error_[
row] == 0.0)
continue;
694 if (new_value >= lower_bound_[
col] && new_value <= upper_bound_[
col]) {
709 if (variable_values[
col] == lower_bound_[
col] && error_sign > 0.0) {
711 error_[
row] -= coeff * box_width;
712 SetNonBasicVariableStatusAndDeriveValue(
col,
716 if (variable_values[
col] == upper_bound_[
col] && error_sign < 0.0) {
718 error_[
row] += coeff * box_width;
719 SetNonBasicVariableStatusAndDeriveValue(
col,
726 bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
727 const LinearProgram& lp,
bool* only_change_is_new_rows,
728 bool* only_change_is_new_cols, ColIndex* num_new_cols) {
730 DCHECK(only_change_is_new_rows !=
nullptr);
731 DCHECK(only_change_is_new_cols !=
nullptr);
732 DCHECK(num_new_cols !=
nullptr);
738 lp.GetFirstSlackVariable() +
RowToColIndex(lp.num_constraints()));
740 const bool old_part_of_matrix_is_unchanged =
742 num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
747 if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
748 lp.num_variables() == num_cols_) {
754 *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
755 lp.num_constraints() > num_rows_ &&
756 lp.GetFirstSlackVariable() == first_slack_col_;
760 *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
761 lp.num_constraints() == num_rows_ &&
762 lp.GetFirstSlackVariable() > first_slack_col_;
764 *only_change_is_new_cols ? lp.num_variables() - num_cols_ : ColIndex(0);
767 first_slack_col_ = lp.GetFirstSlackVariable();
770 num_rows_ = lp.num_constraints();
771 num_cols_ = lp.num_variables();
778 if (parameters_.use_transposed_matrix()) {
784 bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
785 const LinearProgram& lp, ColIndex num_new_cols) {
787 DCHECK_EQ(lp.num_variables(), num_cols_);
788 DCHECK_LE(num_new_cols, first_slack_col_);
789 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
792 for (ColIndex
col(0);
col < first_new_col; ++
col) {
793 if (lower_bound_[
col] != lp.variable_lower_bounds()[
col] ||
794 upper_bound_[
col] != lp.variable_upper_bounds()[
col]) {
799 for (ColIndex
col(first_new_col);
col < first_slack_col_; ++
col) {
800 if (lp.variable_lower_bounds()[
col] != 0.0 &&
801 lp.variable_upper_bounds()[
col] != 0.0) {
806 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
807 if (lower_bound_[
col - num_new_cols] != lp.variable_lower_bounds()[
col] ||
808 upper_bound_[
col - num_new_cols] != lp.variable_upper_bounds()[
col]) {
815 bool RevisedSimplex::InitializeBoundsAndTestIfUnchanged(
816 const LinearProgram& lp) {
818 lower_bound_.
resize(num_cols_, 0.0);
819 upper_bound_.
resize(num_cols_, 0.0);
823 bool bounds_are_unchanged =
true;
824 DCHECK_EQ(lp.num_variables(), num_cols_);
825 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
826 if (lower_bound_[
col] != lp.variable_lower_bounds()[
col] ||
827 upper_bound_[
col] != lp.variable_upper_bounds()[
col]) {
828 bounds_are_unchanged =
false;
832 if (!bounds_are_unchanged) {
833 lower_bound_ = lp.variable_lower_bounds();
834 upper_bound_ = lp.variable_upper_bounds();
836 return bounds_are_unchanged;
839 bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
840 const LinearProgram& lp) {
843 bool objective_is_unchanged =
true;
844 objective_.
resize(num_cols_, 0.0);
845 DCHECK_EQ(num_cols_, lp.num_variables());
846 if (lp.IsMaximizationProblem()) {
848 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
850 if (objective_[
col] != coeff) {
851 objective_is_unchanged =
false;
853 objective_[
col] = coeff;
855 objective_offset_ = -lp.objective_offset();
856 objective_scaling_factor_ = -lp.objective_scaling_factor();
858 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
859 if (objective_[
col] != lp.objective_coefficients()[
col]) {
860 objective_is_unchanged =
false;
864 if (!objective_is_unchanged) {
865 objective_ = lp.objective_coefficients();
867 objective_offset_ = lp.objective_offset();
868 objective_scaling_factor_ = lp.objective_scaling_factor();
870 return objective_is_unchanged;
873 void RevisedSimplex::InitializeObjectiveLimit(
const LinearProgram& lp) {
874 objective_limit_reached_ =
false;
875 DCHECK(std::isfinite(objective_offset_));
876 DCHECK(std::isfinite(objective_scaling_factor_));
877 DCHECK_NE(0.0, objective_scaling_factor_);
880 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
881 for (
const bool set_dual : {
true,
false}) {
893 const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
894 ? parameters_.objective_lower_limit()
895 : parameters_.objective_upper_limit();
897 limit / objective_scaling_factor_ - objective_offset_;
902 dual_objective_limit_ = std::isfinite(shifted_limit)
903 ? shifted_limit * (1.0 + tolerance)
906 primal_objective_limit_ = std::isfinite(shifted_limit)
907 ? shifted_limit * (1.0 - tolerance)
913 void RevisedSimplex::InitializeVariableStatusesForWarmStart(
914 const BasisState& state, ColIndex num_new_cols) {
916 RowIndex num_basic_variables(0);
917 DCHECK_LE(num_new_cols, first_slack_col_);
918 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
921 for (ColIndex
col(0);
col < num_cols_; ++
col) {
926 if (
col < first_new_col &&
col < state.statuses.size()) {
927 status = state.statuses[
col];
928 }
else if (
col >= first_slack_col_ &&
929 col - num_new_cols < state.statuses.size()) {
930 status = state.statuses[
col - num_new_cols];
935 if (num_basic_variables == num_rows_) {
936 VLOG(1) <<
"Too many basic variables in the warm-start basis."
937 <<
"Only keeping the first ones as VariableStatus::BASIC.";
940 ++num_basic_variables;
947 if ((status != default_status) &&
955 status = default_status;
970 Status RevisedSimplex::CreateInitialBasis() {
977 int num_free_variables = 0;
979 for (ColIndex
col(0);
col < num_cols_; ++
col) {
981 SetNonBasicVariableStatusAndDeriveValue(
col, status);
984 VLOG(1) <<
"Number of free variables in the problem: " << num_free_variables;
988 for (RowIndex
row(0);
row < num_rows_; ++
row) {
989 basis[
row] = SlackColIndex(
row);
994 if (!parameters_.use_dual_simplex() &&
995 parameters_.initial_basis() != GlopParameters::MAROS &&
996 parameters_.exploit_singleton_column_in_initial_basis()) {
1000 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1004 if (objective > 0 &&
IsFinite(lower_bound_[
col]) &&
1006 SetNonBasicVariableStatusAndDeriveValue(
col,
1008 }
else if (objective < 0 &&
IsFinite(upper_bound_[
col]) &&
1010 SetNonBasicVariableStatusAndDeriveValue(
col,
1017 ComputeVariableValuesError();
1026 UseSingletonColumnInInitialBasis(&basis);
1029 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1031 basis[
row] = SlackColIndex(
row);
1037 if (parameters_.initial_basis() == GlopParameters::NONE) {
1038 return InitializeFirstBasis(basis);
1040 if (parameters_.initial_basis() == GlopParameters::MAROS) {
1041 InitialBasis initial_basis(compact_matrix_, objective_, lower_bound_,
1043 if (parameters_.use_dual_simplex()) {
1046 initial_basis.GetDualMarosBasis(num_cols_, &basis);
1048 initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1050 int number_changed = 0;
1051 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1052 if (basis[
row] != SlackColIndex(
row)) {
1056 VLOG(1) <<
"Number of Maros basis changes: " << number_changed;
1057 }
else if (parameters_.initial_basis() == GlopParameters::BIXBY ||
1058 parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1060 int num_fixed_variables = 0;
1061 for (RowIndex
row(0);
row < basis.size(); ++
row) {
1062 const ColIndex
col = basis[
row];
1063 if (lower_bound_[
col] == upper_bound_[
col]) {
1065 ++num_fixed_variables;
1069 if (num_fixed_variables == 0) {
1070 VLOG(1) <<
"Crash is set to " << parameters_.initial_basis()
1071 <<
" but there is no equality rows to remove from initial all "
1075 VLOG(1) <<
"Trying to remove " << num_fixed_variables
1076 <<
" fixed variables from the initial basis.";
1077 InitialBasis initial_basis(compact_matrix_, objective_, lower_bound_,
1080 if (parameters_.initial_basis() == GlopParameters::BIXBY) {
1081 if (parameters_.use_scaling()) {
1082 initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1084 VLOG(1) <<
"Bixby initial basis algorithm requires the problem "
1085 <<
"to be scaled. Skipping Bixby's algorithm.";
1087 }
else if (parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1090 if (parameters_.use_dual_simplex()) {
1093 initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1095 initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1098 const Status status = InitializeFirstBasis(basis);
1102 VLOG(1) <<
"Reverting to all slack basis.";
1104 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1105 basis[
row] = SlackColIndex(
row);
1111 LOG(
WARNING) <<
"Unsupported initial_basis parameters: "
1112 << parameters_.initial_basis();
1115 return InitializeFirstBasis(basis);
1118 Status RevisedSimplex::InitializeFirstBasis(
const RowToColMapping& basis) {
1124 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1126 basis_[
row] = SlackColIndex(
row);
1140 if (condition_number_ub > parameters_.initial_condition_number_threshold()) {
1141 const std::string error_message =
1142 absl::StrCat(
"The matrix condition number upper bound is too high: ",
1143 condition_number_ub);
1144 VLOG(1) << error_message;
1149 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1152 DCHECK(BasisIsConsistent());
1159 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1161 VLOG(1) << absl::StrCat(
1162 "The primal residual of the initial basis is above the tolerance, ",
1169 Status RevisedSimplex::Initialize(
const LinearProgram& lp) {
1170 parameters_ = initial_parameters_;
1171 PropagateParameters();
1178 ColIndex num_new_cols(0);
1179 bool only_change_is_new_rows =
false;
1180 bool only_change_is_new_cols =
false;
1181 bool matrix_is_unchanged =
true;
1182 bool only_new_bounds =
false;
1183 if (solution_state_.
IsEmpty() || !notify_that_matrix_is_unchanged_) {
1184 matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1185 lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols);
1186 only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1187 OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1190 CHECK(InitializeMatrixAndTestIfUnchanged(
1191 lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols));
1193 notify_that_matrix_is_unchanged_ =
false;
1194 const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1195 const bool bounds_are_unchanged = InitializeBoundsAndTestIfUnchanged(lp);
1200 if (matrix_is_unchanged && parameters_.allow_simplex_algorithm_change()) {
1201 if (objective_is_unchanged && !bounds_are_unchanged) {
1202 parameters_.set_use_dual_simplex(
true);
1203 PropagateParameters();
1205 if (bounds_are_unchanged && !objective_is_unchanged) {
1206 parameters_.set_use_dual_simplex(
false);
1207 PropagateParameters();
1211 InitializeObjectiveLimit(lp);
1227 bool solve_from_scratch =
true;
1230 if (!solution_state_.
IsEmpty() && !solution_state_has_been_set_externally_) {
1231 if (!parameters_.use_dual_simplex()) {
1236 dual_edge_norms_.
Clear();
1237 dual_pricing_vector_.
clear();
1238 if (matrix_is_unchanged && bounds_are_unchanged) {
1242 solve_from_scratch =
false;
1243 }
else if (only_change_is_new_cols && only_new_bounds) {
1244 InitializeVariableStatusesForWarmStart(solution_state_, num_new_cols);
1245 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1246 for (ColIndex& col_ref : basis_) {
1247 if (col_ref >= first_new_col) {
1248 col_ref += num_new_cols;
1255 primal_edge_norms_.
Clear();
1257 solve_from_scratch =
false;
1263 primal_edge_norms_.
Clear();
1264 if (objective_is_unchanged) {
1265 if (matrix_is_unchanged) {
1266 if (!bounds_are_unchanged) {
1267 InitializeVariableStatusesForWarmStart(solution_state_,
1271 solve_from_scratch =
false;
1272 }
else if (only_change_is_new_rows) {
1275 InitializeVariableStatusesForWarmStart(solution_state_, ColIndex(0));
1282 dual_pricing_vector_.
clear();
1285 if (InitializeFirstBasis(basis_).ok()) {
1286 solve_from_scratch =
false;
1295 if (solve_from_scratch && !solution_state_.
IsEmpty()) {
1298 InitializeVariableStatusesForWarmStart(solution_state_, ColIndex(0));
1306 basis_factorization_.
Clear();
1308 primal_edge_norms_.
Clear();
1309 dual_edge_norms_.
Clear();
1310 dual_pricing_vector_.
clear();
1315 if (InitializeFirstBasis(basis_).ok()) {
1316 solve_from_scratch =
false;
1318 VLOG(1) <<
"RevisedSimplex is not using the warm start "
1319 "basis because it is not factorizable.";
1323 if (solve_from_scratch) {
1324 VLOG(1) <<
"Solve from scratch.";
1325 basis_factorization_.
Clear();
1327 primal_edge_norms_.
Clear();
1328 dual_edge_norms_.
Clear();
1329 dual_pricing_vector_.
clear();
1332 VLOG(1) <<
"Incremental solve.";
1334 DCHECK(BasisIsConsistent());
1338 void RevisedSimplex::DisplayBasicVariableStatistics() {
1341 int num_fixed_variables = 0;
1342 int num_free_variables = 0;
1343 int num_variables_at_bound = 0;
1344 int num_slack_variables = 0;
1345 int num_infeasible_variables = 0;
1349 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1350 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1351 const ColIndex
col = basis_[
row];
1353 if (variable_types[
col] == VariableType::UNCONSTRAINED) {
1354 ++num_free_variables;
1356 if (
value > upper_bound_[
col] + tolerance ||
1357 value < lower_bound_[
col] - tolerance) {
1358 ++num_infeasible_variables;
1360 if (
col >= first_slack_col_) {
1361 ++num_slack_variables;
1363 if (lower_bound_[
col] == upper_bound_[
col]) {
1364 ++num_fixed_variables;
1365 }
else if (variable_values[
col] == lower_bound_[
col] ||
1366 variable_values[
col] == upper_bound_[
col]) {
1367 ++num_variables_at_bound;
1371 VLOG(1) <<
"Basis size: " << num_rows_;
1372 VLOG(1) <<
"Number of basic infeasible variables: "
1373 << num_infeasible_variables;
1374 VLOG(1) <<
"Number of basic slack variables: " << num_slack_variables;
1375 VLOG(1) <<
"Number of basic variables at bound: " << num_variables_at_bound;
1376 VLOG(1) <<
"Number of basic fixed variables: " << num_fixed_variables;
1377 VLOG(1) <<
"Number of basic free variables: " << num_free_variables;
1380 void RevisedSimplex::SaveState() {
1383 solution_state_has_been_set_externally_ =
false;
1386 RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1388 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1390 contains_data[e.row()] =
true;
1393 RowIndex num_empty_rows(0);
1394 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1395 if (!contains_data[
row]) {
1397 VLOG(1) <<
"Row " <<
row <<
" is empty.";
1400 return num_empty_rows;
1403 ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1404 ColIndex num_empty_cols(0);
1405 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1408 VLOG(1) <<
"Column " <<
col <<
" is empty.";
1411 return num_empty_cols;
1414 void RevisedSimplex::CorrectErrorsOnVariableValues() {
1426 if (primal_residual >= parameters_.harris_tolerance_ratio() *
1427 parameters_.primal_feasibility_tolerance()) {
1429 VLOG(1) <<
"Primal infeasibility (bounds error) = "
1431 <<
", Primal residual |A.x - b| = "
1444 (!feasibility_phase_ && num_consecutive_degenerate_iterations_ >= 100)) {
1445 VLOG(1) <<
"Perturbing the problem.";
1446 const Fractional tolerance = parameters_.harris_tolerance_ratio() *
1447 parameters_.primal_feasibility_tolerance();
1448 std::uniform_real_distribution<double> dist(0, tolerance);
1449 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1450 bound_perturbation_[
col] += dist(random_);
1455 void RevisedSimplex::ComputeVariableValuesError() {
1459 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1465 void RevisedSimplex::ComputeDirection(ColIndex
col) {
1469 direction_infinity_norm_ = 0.0;
1472 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1476 direction_infinity_norm_ =
1481 for (
const auto e : direction_) {
1482 direction_infinity_norm_ =
1483 std::max(direction_infinity_norm_, std::abs(e.coefficient()));
1487 num_rows_ == 0 ? 0.0
1488 :
static_cast<double>(direction_.non_zeros.size()) /
1489 static_cast<double>(num_rows_.value())));
1492 Fractional RevisedSimplex::ComputeDirectionError(ColIndex
col) {
1495 for (
const auto e : direction_) {
1502 template <
bool is_entering_reduced_cost_positive>
1504 const ColIndex
col = basis_[
row];
1509 if (is_entering_reduced_cost_positive) {
1510 if (direction > 0.0) {
1511 return (upper_bound_[
col] -
value) / direction;
1513 return (lower_bound_[
col] -
value) / direction;
1516 if (direction > 0.0) {
1517 return (
value - lower_bound_[
col]) / direction;
1519 return (
value - upper_bound_[
col]) / direction;
1524 template <
bool is_entering_reduced_cost_positive>
1525 Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1526 Fractional bound_flip_ratio, SparseColumn* leaving_candidates)
const {
1529 parameters_.harris_tolerance_ratio() *
1530 parameters_.primal_feasibility_tolerance();
1531 const Fractional minimum_delta = parameters_.degenerate_ministep_factor() *
1532 parameters_.primal_feasibility_tolerance();
1538 leaving_candidates->Clear();
1545 ? parameters_.minimum_acceptable_pivot()
1546 : parameters_.ratio_test_zero_threshold();
1548 for (
const auto e : direction_) {
1549 const Fractional magnitude = std::abs(e.coefficient());
1550 if (magnitude <= threshold)
continue;
1551 Fractional ratio = GetRatio<is_entering_reduced_cost_positive>(e.row());
1554 if (
false &&
ratio < 0.0) {
1557 ratio += std::abs(bound_perturbation_[basis_[e.row()]] / e.coefficient());
1559 if (
ratio <= harris_ratio) {
1560 leaving_candidates->SetCoefficient(e.row(),
ratio);
1572 harris_ratio =
std::min(harris_ratio,
1573 std::max(minimum_delta / magnitude,
1574 ratio + harris_tolerance / magnitude));
1577 return harris_ratio;
1590 if (current >= 0.0) {
1591 return candidate >= 0.0 && candidate <= current;
1593 return candidate >= current;
1601 Status RevisedSimplex::ChooseLeavingVariableRow(
1602 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1612 int stats_num_leaving_choices = 0;
1613 equivalent_leaving_choices_.clear();
1615 stats_num_leaving_choices = 0;
1619 const Fractional entering_value = variable_values_.
Get(entering_col);
1621 (reduced_cost > 0.0) ? entering_value - lower_bound_[entering_col]
1622 : upper_bound_[entering_col] - entering_value;
1629 (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1630 current_ratio, &leaving_candidates_)
1631 : ComputeHarrisRatioAndLeavingCandidates<false>(
1632 current_ratio, &leaving_candidates_);
1637 if (current_ratio <= harris_ratio) {
1639 *step_length = current_ratio;
1649 stats_num_leaving_choices = 0;
1651 equivalent_leaving_choices_.clear();
1654 if (
ratio > harris_ratio)
continue;
1655 ++stats_num_leaving_choices;
1656 const RowIndex
row = e.row();
1661 const Fractional candidate_magnitude = std::abs(direction_[
row]);
1662 if (candidate_magnitude < pivot_magnitude)
continue;
1663 if (candidate_magnitude == pivot_magnitude) {
1664 if (!IsRatioMoreOrEquallyStable(
ratio, current_ratio))
continue;
1665 if (
ratio == current_ratio) {
1667 equivalent_leaving_choices_.push_back(
row);
1671 equivalent_leaving_choices_.clear();
1672 current_ratio =
ratio;
1673 pivot_magnitude = candidate_magnitude;
1678 if (!equivalent_leaving_choices_.empty()) {
1679 equivalent_leaving_choices_.push_back(*leaving_row);
1681 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1682 0, equivalent_leaving_choices_.size() - 1)(random_)];
1694 if (current_ratio <= 0.0) {
1698 parameters_.degenerate_ministep_factor() *
1699 parameters_.primal_feasibility_tolerance();
1700 *step_length = minimum_delta / pivot_magnitude;
1702 *step_length = current_ratio;
1709 TestPivot(entering_col, *leaving_row);
1722 if (pivot_magnitude <
1723 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
1728 VLOG(1) <<
"Refactorizing to avoid pivoting by "
1729 << direction_[*leaving_row]
1730 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1731 <<
" reduced cost = " << reduced_cost;
1732 *refactorize =
true;
1742 VLOG(1) <<
"Couldn't avoid pivoting by " << direction_[*leaving_row]
1743 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1744 <<
" reduced cost = " << reduced_cost;
1745 DCHECK_GE(std::abs(direction_[*leaving_row]),
1746 parameters_.minimum_acceptable_pivot());
1754 const bool is_reduced_cost_positive = (reduced_cost > 0.0);
1755 const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
1756 *
target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
1757 ? upper_bound_[basis_[*leaving_row]]
1758 : lower_bound_[basis_[*leaving_row]];
1763 ratio_test_stats_.leaving_choices.Add(stats_num_leaving_choices);
1764 if (!equivalent_leaving_choices_.empty()) {
1765 ratio_test_stats_.num_perfect_ties.Add(
1766 equivalent_leaving_choices_.size());
1769 ratio_test_stats_.abs_used_pivot.Add(std::abs(direction_[*leaving_row]));
1791 bool operator<(
const BreakPoint& other)
const {
1792 if (
ratio == other.ratio) {
1794 return row > other.row;
1798 return ratio > other.ratio;
1809 void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
1810 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1811 RowIndex* leaving_row,
Fractional* step_length,
1822 const Fractional entering_value = variable_values_.
Get(entering_col);
1823 Fractional current_ratio = (reduced_cost > 0.0)
1824 ? entering_value - lower_bound_[entering_col]
1825 : upper_bound_[entering_col] - entering_value;
1828 std::vector<BreakPoint> breakpoints;
1829 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1830 for (
const auto e : direction_) {
1832 reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
1833 const Fractional magnitude = std::abs(direction);
1834 if (magnitude < tolerance)
continue;
1849 const ColIndex
col = basis_[e.row()];
1855 const Fractional to_lower = (lower_bound - tolerance -
value) / direction;
1856 const Fractional to_upper = (upper_bound + tolerance -
value) / direction;
1860 if (to_lower >= 0.0 && to_lower < current_ratio) {
1861 breakpoints.push_back(
1862 BreakPoint(e.row(), to_lower, magnitude, lower_bound));
1864 if (to_upper >= 0.0 && to_upper < current_ratio) {
1865 breakpoints.push_back(
1866 BreakPoint(e.row(), to_upper, magnitude, upper_bound));
1872 std::make_heap(breakpoints.begin(), breakpoints.end());
1876 Fractional improvement = std::abs(reduced_cost);
1879 while (!breakpoints.empty()) {
1880 const BreakPoint top = breakpoints.front();
1888 if (top.coeff_magnitude > best_magnitude) {
1889 *leaving_row = top.row;
1890 current_ratio = top.ratio;
1891 best_magnitude = top.coeff_magnitude;
1897 improvement -= top.coeff_magnitude;
1898 if (improvement <= 0.0)
break;
1899 std::pop_heap(breakpoints.begin(), breakpoints.end());
1900 breakpoints.pop_back();
1906 parameters_.small_pivot_threshold() * direction_infinity_norm_;
1907 if (best_magnitude < threshold && !basis_factorization_.
IsRefactorized()) {
1908 *refactorize =
true;
1912 *step_length = current_ratio;
1916 Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
1931 equivalent_leaving_choices_.clear();
1933 const Fractional scaled_best_price = best_price * squared_norm[
row];
1934 if (squared_infeasibilities[
row] >= scaled_best_price) {
1935 if (squared_infeasibilities[
row] == scaled_best_price) {
1937 equivalent_leaving_choices_.push_back(
row);
1940 equivalent_leaving_choices_.clear();
1941 best_price = squared_infeasibilities[
row] / squared_norm[
row];
1947 if (!equivalent_leaving_choices_.empty()) {
1948 equivalent_leaving_choices_.push_back(*leaving_row);
1950 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1951 0, equivalent_leaving_choices_.size() - 1)(random_)];
1957 const ColIndex leaving_col = basis_[*leaving_row];
1959 if (
value < lower_bound_[leaving_col]) {
1960 *cost_variation = lower_bound_[leaving_col] -
value;
1964 *cost_variation = upper_bound_[leaving_col] -
value;
1978 if (
cost == 0.0)
return false;
1979 return type == VariableType::UPPER_AND_LOWER_BOUNDED ||
1981 (type == VariableType::UPPER_BOUNDED &&
cost < -threshold) ||
1982 (type == VariableType::LOWER_BOUNDED &&
cost > threshold);
1987 void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
1988 ColIndex entering_col) {
1991 const Fractional threshold = parameters_.ratio_test_zero_threshold();
1997 dual_pricing_vector_[leaving_row] / direction_[leaving_row];
1998 for (
const auto e : direction_) {
1999 dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2000 is_dual_entering_candidate_.
Set(
2001 e.row(), IsDualPhaseILeavingCandidate(dual_pricing_vector_[e.row()],
2002 variable_type[basis_[e.row()]],
2005 dual_pricing_vector_[leaving_row] = step;
2009 dual_pricing_vector_[leaving_row] -=
2010 dual_infeasibility_improvement_direction_[entering_col];
2011 if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2012 --num_dual_infeasible_positions_;
2014 dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2017 dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2020 is_dual_entering_candidate_.
Set(
2022 IsDualPhaseILeavingCandidate(dual_pricing_vector_[leaving_row],
2023 variable_type[entering_col], threshold));
2026 template <
typename Cols>
2027 void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2030 bool something_to_do =
false;
2035 for (ColIndex
col : cols) {
2038 (can_increase.IsSet(
col) && reduced_cost < -tolerance) ? 1.0
2039 : (can_decrease.IsSet(
col) && reduced_cost > tolerance) ? -1.0
2041 if (sign != dual_infeasibility_improvement_direction_[
col]) {
2043 --num_dual_infeasible_positions_;
2044 }
else if (dual_infeasibility_improvement_direction_[
col] == 0.0) {
2045 ++num_dual_infeasible_positions_;
2047 if (!something_to_do) {
2048 initially_all_zero_scratchpad_.
values.
resize(num_rows_, 0.0);
2050 initially_all_zero_scratchpad_.
non_zeros.clear();
2051 something_to_do =
true;
2054 col, sign - dual_infeasibility_improvement_direction_[
col],
2055 &initially_all_zero_scratchpad_);
2056 dual_infeasibility_improvement_direction_[
col] = sign;
2059 if (something_to_do) {
2064 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2065 basis_factorization_.
RightSolve(&initially_all_zero_scratchpad_);
2066 if (initially_all_zero_scratchpad_.
non_zeros.empty()) {
2067 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2068 if (initially_all_zero_scratchpad_[
row] == 0.0)
continue;
2069 dual_pricing_vector_[
row] += initially_all_zero_scratchpad_[
row];
2070 is_dual_entering_candidate_.
Set(
2071 row, IsDualPhaseILeavingCandidate(dual_pricing_vector_[
row],
2072 variable_type[basis_[
row]],
2077 for (
const auto e : initially_all_zero_scratchpad_) {
2078 dual_pricing_vector_[e.row()] += e.coefficient();
2079 initially_all_zero_scratchpad_[e.row()] = 0.0;
2080 is_dual_entering_candidate_.
Set(
2081 e.row(), IsDualPhaseILeavingCandidate(
2082 dual_pricing_vector_[e.row()],
2083 variable_type[basis_[e.row()]], threshold));
2086 initially_all_zero_scratchpad_.non_zeros.clear();
2090 Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2091 RowIndex* leaving_row,
Fractional* cost_variation,
2108 dual_pricing_vector_.
empty()) {
2110 num_dual_infeasible_positions_ = 0;
2113 dual_infeasibility_improvement_direction_.
AssignToZero(num_cols_);
2114 DualPhaseIUpdatePriceOnReducedCostChange(
2124 if (num_dual_infeasible_positions_ == 0)
return Status::OK();
2133 equivalent_leaving_choices_.clear();
2134 for (
const RowIndex
row : is_dual_entering_candidate_) {
2136 const Fractional scaled_best_price = best_price * squared_norm[
row];
2137 if (squared_cost >= scaled_best_price) {
2138 if (squared_cost == scaled_best_price) {
2140 equivalent_leaving_choices_.push_back(
row);
2143 equivalent_leaving_choices_.clear();
2144 best_price = squared_cost / squared_norm[
row];
2150 if (!equivalent_leaving_choices_.empty()) {
2151 equivalent_leaving_choices_.push_back(*leaving_row);
2153 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
2154 0, equivalent_leaving_choices_.size() - 1)(random_)];
2160 *cost_variation = dual_pricing_vector_[*leaving_row];
2161 const ColIndex leaving_col = basis_[*leaving_row];
2162 if (*cost_variation < 0.0) {
2171 template <
typename BoxedVariableCols>
2172 void RevisedSimplex::MakeBoxedVariableDualFeasible(
2173 const BoxedVariableCols& cols,
bool update_basic_values) {
2175 std::vector<ColIndex> changed_cols;
2183 const Fractional dual_feasibility_tolerance =
2186 for (
const ColIndex
col : cols) {
2190 VariableType::UPPER_AND_LOWER_BOUNDED);
2193 variable_values[
col] == upper_bound_[
col] ||
2195 if (reduced_cost > dual_feasibility_tolerance &&
2198 changed_cols.push_back(
col);
2199 }
else if (reduced_cost < -dual_feasibility_tolerance &&
2202 changed_cols.push_back(
col);
2206 if (!changed_cols.empty()) {
2208 update_basic_values);
2212 Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2217 const ColIndex leaving_col = basis_[leaving_row];
2218 const Fractional leaving_variable_value = variable_values_.
Get(leaving_col);
2228 return unscaled_step / direction_[leaving_row];
2231 bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2232 VLOG(1) <<
"Test pivot.";
2234 const ColIndex leaving_col = basis_[leaving_row];
2235 basis_[leaving_row] = entering_col;
2239 CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2241 basis_[leaving_row] = leaving_col;
2248 void RevisedSimplex::PermuteBasis() {
2255 if (col_perm.empty())
return;
2261 if (!dual_pricing_vector_.
empty()) {
2278 Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2279 RowIndex leaving_row,
2282 const ColIndex leaving_col = basis_[leaving_row];
2284 lower_bound_[leaving_col] == upper_bound_[leaving_col]
2290 ratio_test_stats_.bound_shift.Add(variable_values_.
Get(leaving_col) -
2293 UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2295 const Fractional pivot_from_direction = direction_[leaving_row];
2299 std::abs(pivot_from_update_row - pivot_from_direction);
2300 if (diff > parameters_.refactorization_threshold() *
2301 (1 + std::abs(pivot_from_direction))) {
2302 VLOG(1) <<
"Refactorizing: imprecise pivot " << pivot_from_direction
2303 <<
" diff = " << diff;
2307 basis_factorization_.
Update(entering_col, leaving_row, direction_));
2315 bool RevisedSimplex::NeedsBasisRefactorization(
bool refactorize) {
2318 const GlopParameters::PricingRule pricing_rule =
2319 feasibility_phase_ ? parameters_.feasibility_rule()
2320 : parameters_.optimization_rule();
2321 if (parameters_.use_dual_simplex()) {
2323 DCHECK_EQ(pricing_rule, GlopParameters::STEEPEST_EDGE);
2326 if (pricing_rule == GlopParameters::STEEPEST_EDGE &&
2334 Status RevisedSimplex::RefactorizeBasisIfNeeded(
bool* refactorize) {
2336 if (NeedsBasisRefactorization(*refactorize)) {
2341 *refactorize =
false;
2346 if (
col >= integrality_scale_.
size()) {
2347 integrality_scale_.
resize(
col + 1, 0.0);
2349 integrality_scale_[
col] = scale;
2354 Cleanup update_deterministic_time_on_return(
2361 std::vector<ColIndex> candidates;
2364 if (std::abs(rc[
col]) < 1e-9) candidates.push_back(
col);
2367 bool refactorize =
false;
2370 for (
int i = 0; i < 10; ++i) {
2373 if (num_pivots >= 5)
break;
2374 if (candidates.empty())
break;
2378 std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2379 const ColIndex entering_col = candidates[
index];
2380 std::swap(candidates[
index], candidates.back());
2381 candidates.pop_back();
2392 ComputeDirection(entering_col);
2394 RowIndex leaving_row;
2396 bool local_refactorize =
false;
2398 ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2401 if (local_refactorize)
continue;
2403 if (std::abs(step_length) <= 1e-6)
continue;
2404 if (leaving_row !=
kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2407 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2413 const auto get_diff = [
this](ColIndex
col,
Fractional old_value,
2415 if (
col >= integrality_scale_.
size() || integrality_scale_[
col] == 0.0) {
2419 return (std::abs(new_value * s - std::round(new_value * s)) -
2420 std::abs(old_value * s - std::round(old_value * s)));
2422 Fractional diff = get_diff(entering_col, variable_values_.
Get(entering_col),
2423 variable_values_.
Get(entering_col) + step);
2424 for (
const auto e : direction_) {
2425 const ColIndex
col = basis_[e.row()];
2427 const Fractional new_value = old_value - e.coefficient() * step;
2428 diff += get_diff(
col, old_value, new_value);
2432 if (diff > -1e-2)
continue;
2442 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2444 }
else if (step < 0.0) {
2445 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2453 const ColIndex leaving_col = basis_[leaving_row];
2456 entering_col, leaving_col, leaving_row, direction_, &update_row_);
2460 const Fractional dir = -direction_[leaving_row] * step;
2461 const bool is_degenerate =
2465 if (!is_degenerate) {
2469 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2472 VLOG(1) <<
"Polish num_pivots: " << num_pivots <<
" gain:" << total_gain;
2491 Status RevisedSimplex::Minimize(TimeLimit*
time_limit) {
2493 Cleanup update_deterministic_time_on_return(
2495 num_consecutive_degenerate_iterations_ = 0;
2496 DisplayIterationInfo();
2497 bool refactorize =
false;
2499 if (feasibility_phase_) {
2515 CorrectErrorsOnVariableValues();
2516 DisplayIterationInfo();
2518 if (feasibility_phase_) {
2530 if (!feasibility_phase_ &&
2531 ComputeObjectiveValue() < primal_objective_limit_) {
2532 VLOG(1) <<
"Stopping the primal simplex because"
2533 <<
" the objective limit " << primal_objective_limit_
2534 <<
" has been reached.";
2535 problem_status_ = ProblemStatus::PRIMAL_FEASIBLE;
2536 objective_limit_reached_ =
true;
2539 }
else if (feasibility_phase_) {
2555 if (feasibility_phase_) {
2558 if (primal_infeasibility <
2559 parameters_.primal_feasibility_tolerance()) {
2560 problem_status_ = ProblemStatus::PRIMAL_FEASIBLE;
2562 VLOG(1) <<
"Infeasible problem! infeasibility = "
2563 << primal_infeasibility;
2564 problem_status_ = ProblemStatus::PRIMAL_INFEASIBLE;
2571 VLOG(1) <<
"Optimal reached, double checking...";
2581 ComputeDirection(entering_col);
2585 entering_col, direction_, &reduced_cost)) {
2586 VLOG(1) <<
"Skipping col #" << entering_col <<
" whose reduced cost is "
2597 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2603 RowIndex leaving_row;
2605 if (feasibility_phase_) {
2606 PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2607 &refactorize, &leaving_row,
2611 ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2614 if (refactorize)
continue;
2619 VLOG(1) <<
"Infinite step length, double checking...";
2623 if (feasibility_phase_) {
2625 VLOG(1) <<
"Unbounded feasibility problem !?";
2628 VLOG(1) <<
"Unbounded problem.";
2629 problem_status_ = ProblemStatus::PRIMAL_UNBOUNDED;
2631 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2632 const ColIndex
col = basis_[
row];
2633 solution_primal_ray_[
col] = -direction_[
row];
2635 solution_primal_ray_[entering_col] = 1.0;
2643 Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2644 if (feasibility_phase_ && leaving_row !=
kInvalidRow) {
2654 step = ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2658 const ColIndex leaving_col =
2664 bool is_degenerate =
false;
2666 Fractional dir = -direction_[leaving_row] * step;
2674 if (!is_degenerate) {
2675 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
2683 entering_col, basis_[leaving_row], leaving_row, direction_,
2686 direction_, &update_row_);
2687 if (!is_degenerate) {
2696 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2698 if (is_degenerate) {
2699 timer.AlsoUpdate(&iteration_stats_.degenerate);
2701 timer.AlsoUpdate(&iteration_stats_.normal);
2707 DCHECK_EQ(VariableType::UPPER_AND_LOWER_BOUNDED,
2710 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2712 }
else if (step < 0.0) {
2713 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2720 if (feasibility_phase_ && leaving_row !=
kInvalidRow) {
2724 &objective_[leaving_col]);
2728 if (step_length == 0.0) {
2729 num_consecutive_degenerate_iterations_++;
2731 if (num_consecutive_degenerate_iterations_ > 0) {
2732 iteration_stats_.degenerate_run_size.Add(
2733 num_consecutive_degenerate_iterations_);
2734 num_consecutive_degenerate_iterations_ = 0;
2739 if (num_consecutive_degenerate_iterations_ > 0) {
2740 iteration_stats_.degenerate_run_size.Add(
2741 num_consecutive_degenerate_iterations_);
2756 Status RevisedSimplex::DualMinimize(TimeLimit*
time_limit) {
2757 Cleanup update_deterministic_time_on_return(
2759 num_consecutive_degenerate_iterations_ = 0;
2760 bool refactorize =
false;
2762 bound_flip_candidates_.clear();
2763 pair_to_ignore_.clear();
2766 RowIndex leaving_row;
2771 ColIndex entering_col;
2780 const bool old_refactorize_value = refactorize;
2797 !old_refactorize_value) {
2800 if (dual_residual_error >
2802 VLOG(1) <<
"Recomputing reduced costs. Dual residual = "
2803 << dual_residual_error;
2818 if (!feasibility_phase_) {
2819 MakeBoxedVariableDualFeasible(
2827 if (ComputeObjectiveValue() > dual_objective_limit_) {
2828 VLOG(1) <<
"Stopping the dual simplex because"
2829 <<
" the objective limit " << dual_objective_limit_
2830 <<
" has been reached.";
2831 problem_status_ = ProblemStatus::DUAL_FEASIBLE;
2832 objective_limit_reached_ =
true;
2838 DisplayIterationInfo();
2842 if (!feasibility_phase_) {
2845 MakeBoxedVariableDualFeasible(bound_flip_candidates_,
2847 bound_flip_candidates_.clear();
2852 direction_.non_zeros);
2856 if (feasibility_phase_) {
2865 VLOG(1) <<
"Optimal reached, double checking.";
2869 if (feasibility_phase_) {
2874 if (num_dual_infeasible_positions_ == 0) {
2875 problem_status_ = ProblemStatus::DUAL_FEASIBLE;
2877 problem_status_ = ProblemStatus::DUAL_INFEASIBLE;
2886 for (std::pair<RowIndex, ColIndex> pair : pair_to_ignore_) {
2887 if (pair.first == leaving_row) {
2891 if (feasibility_phase_) {
2893 update_row_, cost_variation, &entering_col, &
ratio));
2896 update_row_, cost_variation, &bound_flip_candidates_, &entering_col,
2903 VLOG(1) <<
"No entering column. Double checking...";
2908 if (feasibility_phase_) {
2910 VLOG(1) <<
"Unbounded dual feasibility problem !?";
2913 problem_status_ = ProblemStatus::DUAL_UNBOUNDED;
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_);
2932 if (std::abs(entering_coeff) < parameters_.dual_small_pivot_threshold() &&
2934 VLOG(1) <<
"Trying not to pivot by " << entering_coeff;
2943 ComputeDirection(entering_col);
2944 if (std::abs(direction_[leaving_row]) <
2945 parameters_.minimum_acceptable_pivot()) {
2946 VLOG(1) <<
"Do not pivot by " << entering_coeff
2947 <<
" because the direction is " << direction_[leaving_row];
2949 pair_to_ignore_.push_back({leaving_row, entering_col});
2952 pair_to_ignore_.clear();
2959 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2966 timer.AlsoUpdate(&iteration_stats_.degenerate);
2968 timer.AlsoUpdate(&iteration_stats_.normal);
2980 if (feasibility_phase_) {
2981 DualPhaseIUpdatePrice(leaving_row, entering_col);
2984 ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2991 entering_col, leaving_row, direction_,
2995 const ColIndex leaving_col = basis_[leaving_row];
2997 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3007 if (std::abs(primal_step) * parameters_.primal_feasibility_tolerance() >
3016 ColIndex RevisedSimplex::SlackColIndex(RowIndex
row)
const {
3024 result.append(iteration_stats_.StatString());
3025 result.append(ratio_test_stats_.StatString());
3026 result.append(entering_variable_.
StatString());
3028 result.append(variable_values_.
StatString());
3029 result.append(primal_edge_norms_.
StatString());
3030 result.append(dual_edge_norms_.
StatString());
3032 result.append(basis_factorization_.
StatString());
3037 void RevisedSimplex::DisplayAllStats() {
3038 if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3040 absl::FPrintF(stderr,
"%s", GetPrettySolverStats());
3044 Fractional RevisedSimplex::ComputeObjectiveValue()
const {
3050 Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue()
const {
3054 return objective_scaling_factor_ * (sum + objective_offset_);
3062 PropagateParameters();
3065 void RevisedSimplex::PropagateParameters() {
3075 void RevisedSimplex::DisplayIterationInfo()
const {
3077 const int iter = feasibility_phase_
3079 : num_iterations_ - num_feasibility_iterations_;
3086 ? ComputeInitialProblemObjectiveValue()
3087 : (parameters_.use_dual_simplex()
3088 ? reduced_costs_.ComputeSumOfDualInfeasibilities()
3089 : variable_values_.ComputeSumOfPrimalInfeasibilities());
3090 VLOG(1) << (feasibility_phase_ ?
"Feasibility" :
"Optimization")
3091 <<
" phase, iteration # " << iter
3092 <<
", objective = " << absl::StrFormat(
"%.15E", objective);
3096 void RevisedSimplex::DisplayErrors()
const {
3098 VLOG(1) <<
"Primal infeasibility (bounds) = "
3100 VLOG(1) <<
"Primal residual |A.x - b| = "
3102 VLOG(1) <<
"Dual infeasibility (reduced costs) = "
3104 VLOG(1) <<
"Dual residual |c_B - y.B| = "
3111 std::string StringifyMonomialWithFlags(
const Fractional a,
3112 const std::string& x) {
3114 a, x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3120 std::string StringifyWithFlags(
const Fractional x) {
3122 absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3127 std::string RevisedSimplex::SimpleVariableInfo(ColIndex
col)
const {
3131 absl::StrAppendFormat(&output,
"%d (%s) = %s, %s, %s, [%s,%s]",
col.value(),
3132 variable_name_[
col],
3133 StringifyWithFlags(variable_values_.
Get(
col)),
3136 StringifyWithFlags(lower_bound_[
col]),
3137 StringifyWithFlags(upper_bound_[
col]));
3141 void RevisedSimplex::DisplayInfoOnVariables()
const {
3143 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3147 objective_coefficient * variable_value;
3148 VLOG(3) << SimpleVariableInfo(
col) <<
". " << variable_name_[
col] <<
" = "
3149 << StringifyWithFlags(variable_value) <<
" * "
3150 << StringifyWithFlags(objective_coefficient)
3151 <<
"(obj) = " << StringifyWithFlags(objective_contribution);
3153 VLOG(3) <<
"------";
3157 void RevisedSimplex::DisplayVariableBounds() {
3160 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3161 switch (variable_type[
col]) {
3162 case VariableType::UNCONSTRAINED:
3164 case VariableType::LOWER_BOUNDED:
3165 VLOG(3) << variable_name_[
col]
3166 <<
" >= " << StringifyWithFlags(lower_bound_[
col]) <<
";";
3168 case VariableType::UPPER_BOUNDED:
3169 VLOG(3) << variable_name_[
col]
3170 <<
" <= " << StringifyWithFlags(upper_bound_[
col]) <<
";";
3172 case VariableType::UPPER_AND_LOWER_BOUNDED:
3173 VLOG(3) << StringifyWithFlags(lower_bound_[
col])
3174 <<
" <= " << variable_name_[
col]
3175 <<
" <= " << StringifyWithFlags(upper_bound_[
col]) <<
";";
3178 VLOG(3) << variable_name_[
col] <<
" = "
3179 << StringifyWithFlags(lower_bound_[
col]) <<
";";
3182 LOG(DFATAL) <<
"Column " <<
col <<
" has no meaningful status.";
3192 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3193 ComputeDirection(
col);
3194 for (
const auto e : direction_) {
3195 if (column_scales ==
nullptr) {
3196 dictionary[e.row()].SetCoefficient(
col, e.coefficient());
3200 col < column_scales->
size() ? (*column_scales)[
col] : 1.0;
3202 ? (*column_scales)[
GetBasis(e.row())]
3204 dictionary[e.row()].SetCoefficient(
3205 col, direction_[e.row()] * (numerator / denominator));
3214 Status status = Initialize(linear_program);
3218 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3222 void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3225 DisplayInfoOnVariables();
3227 std::string output =
"z = " + StringifyWithFlags(ComputeObjectiveValue());
3230 absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[
col],
3231 variable_name_[
col]));
3233 VLOG(3) << output <<
";";
3235 const RevisedSimplexDictionary dictionary(
nullptr,
this);
3237 for (
const SparseRow&
row : dictionary) {
3239 ColIndex basic_col = basis_[r];
3240 absl::StrAppend(&output, variable_name_[basic_col],
" = ",
3241 StringifyWithFlags(variable_values_.
Get(basic_col)));
3242 for (
const SparseRowEntry e :
row) {
3243 if (e.col() != basic_col) {
3244 absl::StrAppend(&output,
3245 StringifyMonomialWithFlags(e.coefficient(),
3246 variable_name_[e.col()]));
3249 VLOG(3) << output <<
";";
3251 VLOG(3) <<
"------";
3252 DisplayVariableBounds();
3257 void RevisedSimplex::DisplayProblem()
const {
3261 DisplayInfoOnVariables();
3262 std::string output =
"min: ";
3263 bool has_objective =
false;
3264 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3266 has_objective |= (coeff != 0.0);
3267 absl::StrAppend(&output,
3268 StringifyMonomialWithFlags(coeff, variable_name_[
col]));
3270 if (!has_objective) {
3271 absl::StrAppend(&output,
" 0");
3273 VLOG(3) << output <<
";";
3274 for (RowIndex
row(0);
row < num_rows_; ++
row) {
3276 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3277 absl::StrAppend(&output,
3278 StringifyMonomialWithFlags(
3280 variable_name_[
col]));
3282 VLOG(3) << output <<
" = 0;";
3284 VLOG(3) <<
"------";
3288 void RevisedSimplex::AdvanceDeterministicTime(TimeLimit*
time_limit) {
3291 const double deterministic_time_delta =
3292 current_deterministic_time - last_deterministic_time_update_;
3293 time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3294 last_deterministic_time_update_ = current_deterministic_time;
3297 #undef DCHECK_COL_BOUNDS
3298 #undef DCHECK_ROW_BOUNDS