26#include "absl/flags/flag.h"
27#include "absl/log/check.h"
28#include "absl/random/bit_gen_ref.h"
29#include "absl/random/random.h"
30#include "absl/random/seed_sequences.h"
31#include "absl/strings/match.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/str_format.h"
34#include "absl/strings/string_view.h"
39#include "ortools/glop/parameters.pb.h"
57ABSL_FLAG(
bool, simplex_display_numbers_as_fractions,
false,
58 "Display numbers as fractions.");
59ABSL_FLAG(
bool, simplex_stop_after_first_basis,
false,
60 "Stop after first basis has been computed.");
61ABSL_FLAG(
bool, simplex_stop_after_feasibility,
false,
62 "Stop after first phase has been completed.");
63ABSL_FLAG(
bool, simplex_display_stats,
false,
"Display algorithm statistics.");
73 explicit Cleanup(std::function<
void()> closure)
74 : closure_(
std::move(closure)) {}
75 ~Cleanup() { closure_(); }
78 std::function<void()> closure_;
82#define DCHECK_COL_BOUNDS(col) \
85 DCHECK_GT(num_cols_, col); \
89#define DCHECK_ROW_BOUNDS(row) \
92 DCHECK_GT(num_rows_, row); \
99bool UseAbslRandom() {
return false; }
110 random_(UseAbslRandom() ?
absl::BitGenRef(absl_random_)
111 :
absl::BitGenRef(deterministic_random_)),
112 basis_factorization_(&compact_matrix_, &basis_),
113 variables_info_(compact_matrix_),
114 primal_edge_norms_(compact_matrix_, variables_info_,
115 basis_factorization_),
116 dual_edge_norms_(basis_factorization_),
117 dual_prices_(random_),
118 variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
119 basis_factorization_, &dual_edge_norms_, &dual_prices_),
120 update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
121 basis_factorization_),
122 reduced_costs_(compact_matrix_, objective_, basis_, variables_info_,
123 basis_factorization_, random_),
124 entering_variable_(variables_info_, random_, &reduced_costs_),
125 primal_prices_(random_, variables_info_, &primal_edge_norms_,
129 function_stats_(
"SimplexFunctionStats"),
138 variable_starting_values_.clear();
148 solution_state_ = state;
149 solution_state_has_been_set_externally_ =
true;
154 variable_starting_values_ = values;
158 notify_that_matrix_is_unchanged_ =
true;
162 notify_that_matrix_is_unchanged_ =
false;
169 Cleanup update_deterministic_time_on_return(
172 default_logger_.
EnableLogging(parameters_.log_search_progress());
178 const double start_time =
time_limit->GetElapsedTime();
181 DisplayBasicVariableStatistics();
184 dual_infeasibility_improvement_direction_.clear();
188 phase_ = Phase::FEASIBILITY;
190 num_feasibility_iterations_ = 0;
191 num_optimization_iterations_ = 0;
192 num_push_iterations_ = 0;
193 feasibility_time_ = 0.0;
194 optimization_time_ = 0.0;
202 solution_state_has_been_set_externally_ =
true;
205 ComputeNumberOfEmptyRows();
206 ComputeNumberOfEmptyColumns();
209 if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
214 const bool use_dual = parameters_.use_dual_simplex();
219 primal_edge_norms_.
SetPricingRule(parameters_.feasibility_rule());
221 if (parameters_.perturb_costs_in_dual_simplex()) {
225 if (parameters_.use_dedicated_dual_feasibility_algorithm()) {
228 DualMinimize(phase_ == Phase::FEASIBILITY,
time_limit));
245 MakeBoxedVariableDualFeasible(
260 if (initial_infeasibility <
262 SOLVER_LOG(logger_,
"Initial basis is dual feasible.");
264 MakeBoxedVariableDualFeasible(
287 variable_starting_values_);
302 SOLVER_LOG(logger_,
"Infeasible after first phase.");
313 InitializeObjectiveAndTestIfUnchanged(lp);
320 phase_ = Phase::OPTIMIZATION;
321 feasibility_time_ =
time_limit->GetElapsedTime() - start_time;
322 primal_edge_norms_.
SetPricingRule(parameters_.optimization_rule());
323 num_feasibility_iterations_ = num_iterations_;
338 for (
int num_optims = 0;
342 num_optims <= parameters_.max_number_of_reoptimizations() &&
343 !objective_limit_reached_ &&
344 (num_iterations_ == 0 ||
345 num_iterations_ < parameters_.max_number_of_iterations()) &&
347 !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
357 DualMinimize(phase_ == Phase::FEASIBILITY,
time_limit));
368 if (!integrality_scale_.empty() &&
395 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
400 "PRIMAL_UNBOUNDED was reported, but the residual and/or "
401 "dual infeasibility is above the tolerance");
402 if (parameters_.change_status_to_imprecise()) {
423 double max_magnitude = 0.0;
427 double cost_delta = 0.0;
428 for (ColIndex
col(0);
col < num_cols_; ++
col) {
429 cost_delta += solution_primal_ray_[
col] * objective_[
col];
433 solution_primal_ray_[
col];
434 min_distance = std::min(
distance, min_distance);
435 max_magnitude = std::max(solution_primal_ray_[
col], max_magnitude);
440 -solution_primal_ray_[
col];
441 min_distance = std::min(
distance, min_distance);
442 max_magnitude = std::max(-solution_primal_ray_[
col], max_magnitude);
445 SOLVER_LOG(logger_,
"Primal unbounded ray: max blocking magnitude = ",
446 max_magnitude,
", min distance to bound + ", tolerance,
" = ",
447 min_distance,
", ray cost delta = ", cost_delta);
448 if (min_distance * std::abs(cost_delta) < 1 &&
451 "PRIMAL_UNBOUNDED was reported, but the tolerance are good "
452 "and the unbounded ray is not great.");
454 "The difference between unbounded and optimal can depends "
455 "on a slight change of tolerance, trying to see if we are "
456 "at OPTIMAL after postsolve.");
462 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
467 "DUAL_UNBOUNDED was reported, but the residual and/or "
468 "dual infeasibility is above the tolerance");
469 if (parameters_.change_status_to_imprecise()) {
482 for (ColIndex
col(0);
col < num_cols_; ++
col) {
483 const Fractional coeff = solution_dual_ray_row_combination_[
col];
486 error = std::max(error, coeff);
490 }
else if (coeff < 0) {
492 error = std::max(error, -coeff);
499 " infeasibility=", implied_lb);
500 if (implied_lb < tolerance || error > tolerance) {
502 "DUAL_UNBOUNDED was reported, but the dual ray is not "
503 "proving infeasibility with high enough tolerance");
504 if (parameters_.change_status_to_imprecise()) {
515 parameters_.solution_feasibility_tolerance();
520 if (primal_residual > solution_tolerance ||
521 dual_residual > solution_tolerance) {
523 "OPTIMAL was reported, yet one of the residuals is "
524 "above the solution feasibility tolerance after the "
525 "shift/perturbation are removed.");
526 if (parameters_.change_status_to_imprecise()) {
536 primal_residual, parameters_.primal_feasibility_tolerance());
538 std::max(dual_residual, parameters_.dual_feasibility_tolerance());
543 if (primal_infeasibility > primal_tolerance &&
544 dual_infeasibility > dual_tolerance) {
546 "OPTIMAL was reported, yet both of the infeasibility "
547 "are above the tolerance after the "
548 "shift/perturbation are removed.");
549 if (parameters_.change_status_to_imprecise()) {
552 }
else if (primal_infeasibility > primal_tolerance) {
553 if (num_optims == parameters_.max_number_of_reoptimizations()) {
555 "The primal infeasibility is still higher than the "
556 "requested internal tolerance, but the maximum "
557 "number of optimization is reached.");
561 SOLVER_LOG(logger_,
"Re-optimizing with dual simplex ... ");
563 }
else if (dual_infeasibility > dual_tolerance) {
564 if (num_optims == parameters_.max_number_of_reoptimizations()) {
566 "The dual infeasibility is still higher than the "
567 "requested internal tolerance, but the maximum "
568 "number of optimization is reached.");
572 SOLVER_LOG(logger_,
"Re-optimizing with primal simplex ... ");
583 if (parameters_.change_status_to_imprecise() &&
585 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
604 total_time_ =
time_limit->GetElapsedTime() - start_time;
605 optimization_time_ = total_time_ - feasibility_time_;
606 num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
610 if (!variable_starting_values_.empty()) {
611 const int num_super_basic = ComputeNumberOfSuperBasicVariables();
612 if (num_super_basic > 0) {
614 "Num super-basic variables left after optimize phase: ",
616 if (parameters_.push_to_vertex()) {
619 phase_ = Phase::PUSH;
625 "Skipping push phase because optimize didn't succeed.");
631 total_time_ =
time_limit->GetElapsedTime() - start_time;
632 push_time_ = total_time_ - feasibility_time_ - optimization_time_;
633 num_push_iterations_ = num_iterations_ - num_feasibility_iterations_ -
634 num_optimization_iterations_;
637 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
650 solution_objective_value_ =
654 solution_objective_value_ = -solution_objective_value_;
658 variable_starting_values_.clear();
664 return problem_status_;
668 return solution_objective_value_;
672 return num_iterations_;
680 return variable_values_.
Get(
col);
684 return solution_reduced_costs_[
col];
688 return solution_reduced_costs_;
692 return solution_dual_values_[
row];
704 return -variable_values_.
Get(SlackColIndex(
row));
722 return solution_primal_ray_;
726 return solution_dual_ray_;
731 return solution_dual_ray_row_combination_;
738 return basis_factorization_;
741std::string RevisedSimplex::GetPrettySolverStats()
const {
742 return absl::StrFormat(
743 "Problem status : %s\n"
744 "Solving time : %-6.4g\n"
745 "Number of iterations : %u\n"
746 "Time for solvability (first phase) : %-6.4g\n"
747 "Number of iterations for solvability : %u\n"
748 "Time for optimization : %-6.4g\n"
749 "Number of iterations for optimization : %u\n"
750 "Stop after first basis : %d\n",
752 feasibility_time_, num_feasibility_iterations_, optimization_time_,
753 num_optimization_iterations_,
754 absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
767void RevisedSimplex::SetVariableNames() {
768 variable_name_.
resize(num_cols_,
"");
769 for (ColIndex
col(0);
col < first_slack_col_; ++
col) {
773 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
779void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
785bool RevisedSimplex::BasisIsConsistent()
const {
788 for (RowIndex
row(0);
row < num_rows_; ++
row) {
789 const ColIndex
col = basis_[
row];
790 if (!is_basic.IsSet(
col))
return false;
793 ColIndex cols_in_basis(0);
794 ColIndex cols_not_in_basis(0);
795 for (ColIndex
col(0);
col < num_cols_; ++
col) {
796 cols_in_basis += is_basic.IsSet(
col);
797 cols_not_in_basis += !is_basic.IsSet(
col);
798 if (is_basic.IsSet(
col) !=
804 if (cols_not_in_basis != num_cols_ -
RowToColIndex(num_rows_))
return false;
810void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
819 DCHECK_NE(basis_[basis_row], entering_col);
822 const ColIndex leaving_col = basis_[basis_row];
833 basis_[basis_row] = entering_col;
841class ColumnComparator {
844 bool operator()(ColIndex col_a, ColIndex col_b)
const {
845 return value_[col_a] < value_[col_b];
862void RevisedSimplex::UseSingletonColumnInInitialBasis(
RowToColMapping* basis) {
869 std::vector<ColIndex> singleton_column;
870 DenseRow cost_variation(num_cols_, 0.0);
873 for (ColIndex
col(0);
col < num_cols_; ++
col) {
878 cost_variation[
col] = objective_[
col] / std::abs(slope);
880 cost_variation[
col] = -objective_[
col] / std::abs(slope);
882 singleton_column.push_back(
col);
884 if (singleton_column.empty())
return;
891 ColumnComparator comparator(cost_variation);
892 std::sort(singleton_column.begin(), singleton_column.end(), comparator);
893 DCHECK_LE(cost_variation[singleton_column.front()],
894 cost_variation[singleton_column.back()]);
902 for (
const ColIndex
col : singleton_column) {
914 if (error_[
row] == 0.0)
continue;
934 DCHECK_NE(box_width, 0.0);
935 DCHECK_NE(error_[
row], 0.0);
939 error_[
row] -= coeff * box_width;
940 SetNonBasicVariableStatusAndDeriveValue(
col,
946 error_[
row] += coeff * box_width;
947 SetNonBasicVariableStatusAndDeriveValue(
col,
954bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
955 const LinearProgram& lp,
bool lp_is_in_equation_form,
956 bool* only_change_is_new_rows,
bool* only_change_is_new_cols,
957 ColIndex* num_new_cols) {
959 DCHECK(only_change_is_new_rows !=
nullptr);
960 DCHECK(only_change_is_new_cols !=
nullptr);
961 DCHECK(num_new_cols !=
nullptr);
962 DCHECK_EQ(num_cols_, compact_matrix_.
num_cols());
963 DCHECK_EQ(num_rows_, compact_matrix_.
num_rows());
966 const bool old_part_of_matrix_is_unchanged =
968 num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
971 const ColIndex lp_first_slack =
972 lp_is_in_equation_form ? lp.GetFirstSlackVariable() : lp.num_variables();
977 if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
978 lp_first_slack == first_slack_col_) {
983 if (parameters_.use_transposed_matrix()) {
984 if (transposed_matrix_.
IsEmpty()) {
988 transposed_matrix_.
Reset(RowIndex(0));
995 *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
996 lp.num_constraints() > num_rows_ &&
997 lp_first_slack == first_slack_col_;
1001 *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
1002 lp.num_constraints() == num_rows_ &&
1003 lp_first_slack > first_slack_col_;
1004 *num_new_cols = *only_change_is_new_cols ? lp_first_slack - first_slack_col_
1008 first_slack_col_ = lp_first_slack;
1011 num_rows_ = lp.num_constraints();
1012 num_cols_ = lp_first_slack +
RowToColIndex(lp.num_constraints());
1015 if (lp_is_in_equation_form) {
1022 if (parameters_.use_transposed_matrix()) {
1025 transposed_matrix_.
Reset(RowIndex(0));
1032bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1033 const LinearProgram& lp,
bool lp_is_in_equation_form,
1034 ColIndex num_new_cols) {
1036 DCHECK_LE(num_new_cols, first_slack_col_);
1037 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1042 for (ColIndex
col(0);
col < first_new_col; ++
col) {
1050 for (ColIndex
col(first_new_col);
col < first_slack_col_; ++
col) {
1051 if (lp.variable_lower_bounds()[
col] != 0.0 &&
1052 lp.variable_upper_bounds()[
col] != 0.0) {
1058 if (lp_is_in_equation_form) {
1059 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
1066 DCHECK_EQ(num_rows_, lp.num_constraints());
1067 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1070 -lp.constraint_upper_bounds()[
row] ||
1072 -lp.constraint_lower_bounds()[
row]) {
1080bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
1081 const LinearProgram& lp) {
1084 bool objective_is_unchanged =
true;
1085 objective_.
resize(num_cols_, 0.0);
1089 DCHECK_GE(num_cols_, lp.num_variables());
1090 for (ColIndex
col(lp.num_variables());
col < num_cols_; ++
col) {
1091 if (objective_[
col] != 0.0) {
1092 objective_is_unchanged =
false;
1093 objective_[
col] = 0.0;
1097 if (lp.IsMaximizationProblem()) {
1099 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
1101 if (objective_[
col] != coeff) {
1102 objective_is_unchanged =
false;
1103 objective_[
col] = coeff;
1106 objective_offset_ = -lp.objective_offset();
1107 objective_scaling_factor_ = -lp.objective_scaling_factor();
1109 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
1111 if (objective_[
col] != coeff) {
1112 objective_is_unchanged =
false;
1113 objective_[
col] = coeff;
1116 objective_offset_ = lp.objective_offset();
1117 objective_scaling_factor_ = lp.objective_scaling_factor();
1120 return objective_is_unchanged;
1123void RevisedSimplex::InitializeObjectiveLimit(
const LinearProgram& lp) {
1124 objective_limit_reached_ =
false;
1125 DCHECK(std::isfinite(objective_offset_));
1126 DCHECK(std::isfinite(objective_scaling_factor_));
1127 DCHECK_NE(0.0, objective_scaling_factor_);
1130 for (
const bool set_dual : {
true,
false}) {
1142 const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
1143 ? parameters_.objective_lower_limit()
1144 : parameters_.objective_upper_limit();
1146 limit / objective_scaling_factor_ - objective_offset_;
1148 dual_objective_limit_ = shifted_limit;
1150 primal_objective_limit_ = shifted_limit;
1160Status RevisedSimplex::CreateInitialBasis() {
1172 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1173 basis[
row] = SlackColIndex(
row);
1180 if (!parameters_.use_dual_simplex() &&
1181 parameters_.initial_basis() != GlopParameters::MAROS &&
1182 parameters_.exploit_singleton_column_in_initial_basis()) {
1186 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1192 SetNonBasicVariableStatusAndDeriveValue(
col,
1196 SetNonBasicVariableStatusAndDeriveValue(
col,
1203 ComputeVariableValuesError();
1212 UseSingletonColumnInInitialBasis(&basis);
1215 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1217 basis[
row] = SlackColIndex(
row);
1223 if (parameters_.initial_basis() == GlopParameters::NONE) {
1224 return InitializeFirstBasis(basis);
1226 if (parameters_.initial_basis() == GlopParameters::MAROS) {
1227 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1229 if (parameters_.use_dual_simplex()) {
1232 initial_basis.GetDualMarosBasis(num_cols_, &basis);
1234 initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1236 int number_changed = 0;
1237 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1238 if (basis[
row] != SlackColIndex(
row)) {
1242 VLOG(1) <<
"Number of Maros basis changes: " << number_changed;
1243 }
else if (parameters_.initial_basis() == GlopParameters::BIXBY ||
1244 parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1246 int num_fixed_variables = 0;
1247 for (RowIndex
row(0);
row < basis.size(); ++
row) {
1248 const ColIndex
col = basis[
row];
1251 ++num_fixed_variables;
1255 if (num_fixed_variables == 0) {
1256 SOLVER_LOG(logger_,
"Crash is set to ", parameters_.initial_basis(),
1257 " but there is no equality rows to remove from initial all "
1258 "slack basis. Starting from there.");
1261 SOLVER_LOG(logger_,
"Trying to remove ", num_fixed_variables,
1262 " fixed variables from the initial basis.");
1263 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1266 if (parameters_.initial_basis() == GlopParameters::BIXBY) {
1267 if (parameters_.use_scaling()) {
1268 initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1270 VLOG(1) <<
"Bixby initial basis algorithm requires the problem "
1271 <<
"to be scaled. Skipping Bixby's algorithm.";
1273 }
else if (parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1276 if (parameters_.use_dual_simplex()) {
1279 initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1281 initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1284 const Status
status = InitializeFirstBasis(basis);
1290 "Advanced basis algo failed, Reverting to all slack basis.");
1292 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1293 basis[
row] = SlackColIndex(
row);
1299 LOG(WARNING) <<
"Unsupported initial_basis parameters: "
1300 << parameters_.initial_basis();
1303 return InitializeFirstBasis(basis);
1306Status RevisedSimplex::InitializeFirstBasis(
const RowToColMapping& basis) {
1312 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1314 basis_[
row] = SlackColIndex(
row);
1328 if (condition_number_ub > parameters_.initial_condition_number_threshold()) {
1329 const std::string error_message =
1330 absl::StrCat(
"The matrix condition number upper bound is too high: ",
1331 condition_number_ub);
1337 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1340 DCHECK(BasisIsConsistent());
1349 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1353 "The primal residual of the initial basis is above the tolerance, ",
1360Status RevisedSimplex::Initialize(
const LinearProgram& lp) {
1361 parameters_ = initial_parameters_;
1362 PropagateParameters();
1370 const bool lp_is_in_equation_form = lp.IsInEquationForm();
1377 ColIndex num_new_cols(0);
1378 bool only_change_is_new_rows =
false;
1379 bool only_change_is_new_cols =
false;
1380 bool matrix_is_unchanged =
true;
1381 bool only_new_bounds =
false;
1382 if (solution_state_.
IsEmpty() || !notify_that_matrix_is_unchanged_) {
1383 matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1384 lp, lp_is_in_equation_form, &only_change_is_new_rows,
1385 &only_change_is_new_cols, &num_new_cols);
1386 only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1387 OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1388 lp, lp_is_in_equation_form, num_new_cols);
1390 CHECK(InitializeMatrixAndTestIfUnchanged(
1391 lp, lp_is_in_equation_form, &only_change_is_new_rows,
1392 &only_change_is_new_cols, &num_new_cols));
1394 notify_that_matrix_is_unchanged_ =
false;
1397 const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1399 const bool bounds_are_unchanged =
1400 lp_is_in_equation_form
1402 lp.variable_lower_bounds(), lp.variable_upper_bounds())
1404 lp.variable_lower_bounds(), lp.variable_upper_bounds(),
1405 lp.constraint_lower_bounds(), lp.constraint_upper_bounds());
1410 if (matrix_is_unchanged && parameters_.allow_simplex_algorithm_change()) {
1411 if (objective_is_unchanged && !bounds_are_unchanged) {
1412 parameters_.set_use_dual_simplex(
true);
1413 PropagateParameters();
1415 if (bounds_are_unchanged && !objective_is_unchanged) {
1416 parameters_.set_use_dual_simplex(
false);
1417 PropagateParameters();
1421 InitializeObjectiveLimit(lp);
1426 if (VLOG_IS_ON(2)) {
1437 bool solve_from_scratch =
true;
1440 if (!solution_state_.
IsEmpty() && !solution_state_has_been_set_externally_) {
1441 if (!parameters_.use_dual_simplex()) {
1446 dual_edge_norms_.
Clear();
1447 dual_pricing_vector_.clear();
1448 if (matrix_is_unchanged && bounds_are_unchanged) {
1452 solve_from_scratch =
false;
1453 }
else if (only_change_is_new_cols && only_new_bounds) {
1457 variable_starting_values_);
1459 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1460 for (ColIndex& col_ref : basis_) {
1461 if (col_ref >= first_new_col) {
1462 col_ref += num_new_cols;
1469 primal_edge_norms_.
Clear();
1471 solve_from_scratch =
false;
1477 primal_edge_norms_.
Clear();
1478 if (objective_is_unchanged) {
1479 if (matrix_is_unchanged) {
1480 if (!bounds_are_unchanged) {
1482 first_slack_col_, ColIndex(0), solution_state_);
1484 variable_starting_values_);
1487 solve_from_scratch =
false;
1488 }
else if (only_change_is_new_rows) {
1492 first_slack_col_, ColIndex(0), solution_state_);
1499 dual_pricing_vector_.clear();
1502 if (InitializeFirstBasis(basis_).ok()) {
1503 solve_from_scratch =
false;
1512 if (solve_from_scratch && !solution_state_.
IsEmpty()) {
1513 basis_factorization_.
Clear();
1515 primal_edge_norms_.
Clear();
1516 dual_edge_norms_.
Clear();
1517 dual_pricing_vector_.clear();
1525 std::vector<ColIndex> candidates;
1527 candidates.push_back(
col);
1529 SOLVER_LOG(logger_,
"The warm-start state contains ", candidates.size(),
1530 " candidates for the basis (num_rows = ", num_rows_.value(),
1536 if (candidates.size() == num_rows_) {
1538 for (
const ColIndex
col : candidates) {
1539 basis_.push_back(
col);
1545 if (InitializeFirstBasis(basis_).ok()) {
1546 solve_from_scratch =
false;
1550 if (solve_from_scratch) {
1552 const int num_super_basic =
1555 parameters_.crossover_bound_snapping_distance(),
1556 variable_starting_values_);
1558 SOLVER_LOG(logger_,
"The initial basis did not use ",
1559 " BASIC columns from the initial state and used ",
1560 (num_rows_ - (candidates.size() - num_super_basic)).value(),
1561 " slack variables that were not marked BASIC.");
1562 if (num_snapped > 0) {
1564 " of the FREE variables where moved to their bound.");
1568 if (InitializeFirstBasis(basis_).ok()) {
1569 solve_from_scratch =
false;
1572 "RevisedSimplex is not using the warm start "
1573 "basis because it is not factorizable.");
1578 if (solve_from_scratch) {
1579 SOLVER_LOG(logger_,
"Starting basis: create from scratch.");
1580 basis_factorization_.
Clear();
1582 primal_edge_norms_.
Clear();
1583 dual_edge_norms_.
Clear();
1584 dual_pricing_vector_.clear();
1587 SOLVER_LOG(logger_,
"Starting basis: incremental solve.");
1589 DCHECK(BasisIsConsistent());
1593void RevisedSimplex::DisplayBasicVariableStatistics() {
1596 int num_fixed_variables = 0;
1597 int num_free_variables = 0;
1598 int num_variables_at_bound = 0;
1599 int num_slack_variables = 0;
1600 int num_infeasible_variables = 0;
1606 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1607 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1608 const ColIndex
col = basis_[
row];
1611 ++num_free_variables;
1615 ++num_infeasible_variables;
1617 if (
col >= first_slack_col_) {
1618 ++num_slack_variables;
1621 ++num_fixed_variables;
1624 ++num_variables_at_bound;
1628 SOLVER_LOG(logger_,
"The matrix with slacks has ",
1629 compact_matrix_.
num_rows().value(),
" rows, ",
1630 compact_matrix_.
num_cols().value(),
" columns, ",
1631 compact_matrix_.
num_entries().value(),
" entries.");
1632 SOLVER_LOG(logger_,
"Number of basic infeasible variables: ",
1633 num_infeasible_variables);
1634 SOLVER_LOG(logger_,
"Number of basic slack variables: ", num_slack_variables);
1636 "Number of basic variables at bound: ", num_variables_at_bound);
1637 SOLVER_LOG(logger_,
"Number of basic fixed variables: ", num_fixed_variables);
1638 SOLVER_LOG(logger_,
"Number of basic free variables: ", num_free_variables);
1639 SOLVER_LOG(logger_,
"Number of super-basic variables: ",
1640 ComputeNumberOfSuperBasicVariables());
1643void RevisedSimplex::SaveState() {
1646 solution_state_has_been_set_externally_ =
false;
1649RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1651 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1652 for (
const SparseColumn::Entry e : compact_matrix_.
column(
col)) {
1653 contains_data[e.row()] =
true;
1656 RowIndex num_empty_rows(0);
1657 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1658 if (!contains_data[
row]) {
1660 VLOG(2) <<
"Row " <<
row <<
" is empty.";
1663 return num_empty_rows;
1666ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1667 ColIndex num_empty_cols(0);
1668 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1671 VLOG(2) <<
"Column " <<
col <<
" is empty.";
1674 return num_empty_cols;
1677int RevisedSimplex::ComputeNumberOfSuperBasicVariables()
const {
1679 int num_super_basic = 0;
1680 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1682 variable_values_.
Get(
col) != 0.0) {
1686 return num_super_basic;
1689void RevisedSimplex::CorrectErrorsOnVariableValues() {
1701 if (primal_residual >= parameters_.harris_tolerance_ratio() *
1702 parameters_.primal_feasibility_tolerance()) {
1704 VLOG(1) <<
"Primal infeasibility (bounds error) = "
1706 <<
", Primal residual |A.x - b| = "
1711void RevisedSimplex::ComputeVariableValuesError() {
1715 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1721void RevisedSimplex::ComputeDirection(ColIndex
col) {
1728 const RowIndex num_rows = num_rows_;
1730 for (RowIndex
row(0);
row < num_rows; ++
row) {
1734 norm = std::max(norm, std::abs(
value));
1738 for (
const auto e : direction_) {
1739 norm = std::max(norm, std::abs(e.coefficient()));
1742 direction_infinity_norm_ = norm;
1744 num_rows_ == 0 ? 0.0
1745 :
static_cast<double>(direction_.non_zeros.size()) /
1746 static_cast<double>(num_rows_.value())));
1749Fractional RevisedSimplex::ComputeDirectionError(ColIndex
col) {
1752 for (
const auto e : direction_) {
1759template <
bool is_entering_reduced_cost_positive>
1762 RowIndex
row)
const {
1763 const ColIndex
col = basis_[
row];
1767 DCHECK_NE(direction, 0.0);
1768 if (is_entering_reduced_cost_positive) {
1769 if (direction > 0.0) {
1775 if (direction > 0.0) {
1783template <
bool is_entering_reduced_cost_positive>
1784Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1788 parameters_.harris_tolerance_ratio() *
1789 parameters_.primal_feasibility_tolerance();
1790 const Fractional minimum_delta = parameters_.degenerate_ministep_factor() *
1791 parameters_.primal_feasibility_tolerance();
1797 leaving_candidates->Clear();
1804 ? parameters_.minimum_acceptable_pivot()
1805 : parameters_.ratio_test_zero_threshold();
1809 for (
const auto e : direction_) {
1810 const Fractional magnitude = std::abs(e.coefficient());
1811 if (magnitude <= threshold)
continue;
1814 if (
ratio <= harris_ratio) {
1815 leaving_candidates->SetCoefficient(e.row(),
ratio);
1827 harris_ratio = std::min(harris_ratio,
1828 std::max(minimum_delta / magnitude,
1829 ratio + harris_tolerance / magnitude));
1832 return harris_ratio;
1845 if (current >= 0.0) {
1846 return candidate >= 0.0 && candidate <= current;
1848 return candidate >= current;
1856Status RevisedSimplex::ChooseLeavingVariableRow(
1857 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1864 DCHECK_NE(0.0, reduced_cost);
1867 int stats_num_leaving_choices = 0;
1868 equivalent_leaving_choices_.clear();
1872 stats_num_leaving_choices = 0;
1876 const Fractional entering_value = variable_values_.
Get(entering_col);
1878 (reduced_cost > 0.0) ? entering_value -
lower_bounds[entering_col]
1880 DCHECK_GT(current_ratio, 0.0);
1886 (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1887 current_ratio, &leaving_candidates_)
1888 : ComputeHarrisRatioAndLeavingCandidates<false>(
1889 current_ratio, &leaving_candidates_);
1894 if (current_ratio <= harris_ratio) {
1896 *step_length = current_ratio;
1906 stats_num_leaving_choices = 0;
1908 equivalent_leaving_choices_.clear();
1909 for (
const SparseColumn::Entry e : leaving_candidates_) {
1911 if (
ratio > harris_ratio)
continue;
1912 ++stats_num_leaving_choices;
1913 const RowIndex
row = e.row();
1918 const Fractional candidate_magnitude = std::abs(direction_[
row]);
1919 if (candidate_magnitude < pivot_magnitude)
continue;
1920 if (candidate_magnitude == pivot_magnitude) {
1921 if (!IsRatioMoreOrEquallyStable(
ratio, current_ratio))
continue;
1922 if (
ratio == current_ratio) {
1924 equivalent_leaving_choices_.push_back(
row);
1928 equivalent_leaving_choices_.clear();
1929 current_ratio =
ratio;
1930 pivot_magnitude = candidate_magnitude;
1935 if (!equivalent_leaving_choices_.empty()) {
1936 equivalent_leaving_choices_.push_back(*leaving_row);
1938 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1939 0, equivalent_leaving_choices_.size() - 1)(random_)];
1951 if (current_ratio <= 0.0) {
1955 parameters_.degenerate_ministep_factor() *
1956 parameters_.primal_feasibility_tolerance();
1957 *step_length = minimum_delta / pivot_magnitude;
1959 *step_length = current_ratio;
1966 TestPivot(entering_col, *leaving_row);
1979 if (pivot_magnitude <
1980 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
1985 VLOG(1) <<
"Refactorizing to avoid pivoting by "
1986 << direction_[*leaving_row]
1987 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1988 <<
" reduced cost = " << reduced_cost;
1989 *refactorize =
true;
1999 VLOG(1) <<
"Couldn't avoid pivoting by " << direction_[*leaving_row]
2000 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
2001 <<
" reduced cost = " << reduced_cost;
2002 DCHECK_GE(std::abs(direction_[*leaving_row]),
2003 parameters_.minimum_acceptable_pivot());
2011 const bool is_reduced_cost_positive = (reduced_cost > 0.0);
2012 const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
2013 *
target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
2020 ratio_test_stats_.leaving_choices.
Add(stats_num_leaving_choices);
2021 if (!equivalent_leaving_choices_.empty()) {
2022 ratio_test_stats_.num_perfect_ties.
Add(
2023 equivalent_leaving_choices_.size());
2026 ratio_test_stats_.abs_used_pivot.
Add(std::abs(direction_[*leaving_row]));
2048 bool operator<(
const BreakPoint& other)
const {
2049 if (ratio == other.ratio) {
2050 if (coeff_magnitude == other.coeff_magnitude) {
2051 return row > other.row;
2055 return ratio > other.ratio;
2066void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
2067 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
2068 RowIndex* leaving_row,
Fractional* step_length,
2075 DCHECK_NE(0.0, reduced_cost);
2081 const Fractional entering_value = variable_values_.
Get(entering_col);
2082 Fractional current_ratio = (reduced_cost > 0.0)
2085 DCHECK_GT(current_ratio, 0.0);
2087 std::vector<BreakPoint> breakpoints;
2088 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
2089 for (
const auto e : direction_) {
2091 reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
2092 const Fractional magnitude = std::abs(direction);
2093 if (magnitude < tolerance)
continue;
2108 const ColIndex
col = basis_[e.row()];
2119 if (to_lower >= 0.0 && to_lower < current_ratio) {
2120 breakpoints.push_back(
2121 BreakPoint(e.row(), to_lower, magnitude,
lower_bound));
2123 if (to_upper >= 0.0 && to_upper < current_ratio) {
2124 breakpoints.push_back(
2125 BreakPoint(e.row(), to_upper, magnitude,
upper_bound));
2131 std::make_heap(breakpoints.begin(), breakpoints.end());
2135 Fractional improvement = std::abs(reduced_cost);
2138 while (!breakpoints.empty()) {
2139 const BreakPoint top = breakpoints.front();
2147 if (top.coeff_magnitude > best_magnitude) {
2148 *leaving_row = top.row;
2149 current_ratio = top.ratio;
2150 best_magnitude = top.coeff_magnitude;
2156 improvement -= top.coeff_magnitude;
2157 if (improvement <= 0.0)
break;
2158 std::pop_heap(breakpoints.begin(), breakpoints.end());
2159 breakpoints.pop_back();
2165 parameters_.small_pivot_threshold() * direction_infinity_norm_;
2166 if (best_magnitude < threshold && !basis_factorization_.
IsRefactorized()) {
2167 *refactorize =
true;
2171 *step_length = current_ratio;
2175Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
2184 if (dual_prices_.
Size() == 0) {
2186 parameters_.dual_price_prioritize_norm());
2196 const ColIndex leaving_col = basis_[*leaving_row];
2201 DCHECK_GT(*cost_variation, 0.0);
2205 DCHECK_LT(*cost_variation, 0.0);
2216 if (cost == 0.0)
return false;
2226template <
bool use_dense_update>
2231 const bool is_candidate =
2232 IsDualPhaseILeavingCandidate(price, type, threshold);
2234 if (use_dense_update) {
2244void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
2245 ColIndex entering_col) {
2256 dual_pricing_vector_.empty()) {
2261 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2272 dual_pricing_vector_[leaving_row] / direction_[leaving_row];
2273 for (
const auto e : direction_) {
2274 dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2275 OnDualPriceChange(squared_norms, e.row(), variable_type[basis_[e.row()]],
2278 dual_pricing_vector_[leaving_row] = step;
2282 dual_pricing_vector_[leaving_row] -=
2283 dual_infeasibility_improvement_direction_[entering_col];
2284 if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2285 --num_dual_infeasible_positions_;
2287 dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2290 dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2293 OnDualPriceChange(squared_norms, leaving_row, variable_type[entering_col],
2297template <
typename Cols>
2298void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2301 bool something_to_do =
false;
2302 const DenseBitRow::ConstView can_decrease =
2304 const DenseBitRow::ConstView can_increase =
2308 auto improvement_direction = dual_infeasibility_improvement_direction_.
view();
2309 for (
const ColIndex
col : cols) {
2312 (can_increase[
col] && reduced_cost < -tolerance) ? 1.0
2313 : (can_decrease[
col] && reduced_cost > tolerance) ? -1.0
2315 if (sign != improvement_direction[
col]) {
2317 --num_dual_infeasible_positions_;
2318 }
else if (improvement_direction[
col] == 0.0) {
2319 ++num_dual_infeasible_positions_;
2321 if (!something_to_do) {
2322 initially_all_zero_scratchpad_.
values.
resize(num_rows_, 0.0);
2324 initially_all_zero_scratchpad_.
non_zeros.clear();
2325 something_to_do =
true;
2329 num_update_price_operations_ +=
2332 col, sign - improvement_direction[
col],
2333 &initially_all_zero_scratchpad_);
2334 improvement_direction[
col] = sign;
2337 if (something_to_do) {
2344 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2345 basis_factorization_.
RightSolve(&initially_all_zero_scratchpad_);
2346 if (initially_all_zero_scratchpad_.
non_zeros.empty()) {
2348 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2349 if (initially_all_zero_scratchpad_[
row] == 0.0)
continue;
2350 dual_pricing_vector_[
row] += initially_all_zero_scratchpad_[
row];
2351 OnDualPriceChange<
true>(
2352 squared_norms,
row, variable_type[basis_[
row]], threshold);
2356 for (
const auto e : initially_all_zero_scratchpad_) {
2357 dual_pricing_vector_[e.row()] += e.coefficient();
2358 OnDualPriceChange(squared_norms, e.row(),
2359 variable_type[basis_[e.row()]], threshold);
2360 initially_all_zero_scratchpad_[e.row()] = 0.0;
2363 initially_all_zero_scratchpad_.non_zeros.clear();
2367Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2368 RowIndex* leaving_row,
Fractional* cost_variation,
2388 dual_pricing_vector_.empty()) {
2390 num_dual_infeasible_positions_ = 0;
2393 dual_infeasibility_improvement_direction_.
AssignToZero(num_cols_);
2394 DualPhaseIUpdatePriceOnReducedCostChange(
2404 if (num_dual_infeasible_positions_ == 0)
return Status::OK();
2411 *cost_variation = dual_pricing_vector_[*leaving_row];
2412 const ColIndex leaving_col = basis_[*leaving_row];
2413 if (*cost_variation < 0.0) {
2422template <
typename BoxedVariableCols>
2423void RevisedSimplex::MakeBoxedVariableDualFeasible(
2424 const BoxedVariableCols& cols,
bool update_basic_values) {
2426 std::vector<ColIndex> changed_cols;
2443 for (
const ColIndex
col : cols) {
2455 changed_cols.push_back(
col);
2456 }
else if (reduced_cost < -threshold &&
2460 changed_cols.push_back(
col);
2464 if (!changed_cols.empty()) {
2465 iteration_stats_.num_dual_flips.
Add(changed_cols.size());
2467 update_basic_values);
2471Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2476 const ColIndex leaving_col = basis_[leaving_row];
2477 const Fractional leaving_variable_value = variable_values_.
Get(leaving_col);
2487 return unscaled_step / direction_[leaving_row];
2490bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2491 VLOG(1) <<
"Test pivot.";
2493 const ColIndex leaving_col = basis_[leaving_row];
2494 basis_[leaving_row] = entering_col;
2498 CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2500 basis_[leaving_row] = leaving_col;
2507void RevisedSimplex::PermuteBasis() {
2514 if (col_perm.empty())
return;
2520 if (!dual_pricing_vector_.empty()) {
2524 &tmp_dual_pricing_vector_);
2536Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2537 RowIndex leaving_row,
2554 pivot_from_update_row = update_row_.
GetCoefficient(entering_col);
2565 const ColIndex leaving_col = basis_[leaving_row];
2573 ratio_test_stats_.bound_shift.
Add(variable_values_.
Get(leaving_col) -
2576 UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2579 const Fractional pivot_from_direction = direction_[leaving_row];
2581 std::abs(pivot_from_update_row - pivot_from_direction);
2582 if (diff > parameters_.refactorization_threshold() *
2583 (1.0 + std::min(std::abs(pivot_from_update_row),
2584 std::abs(pivot_from_direction)))) {
2585 VLOG(1) <<
"Refactorizing: imprecise pivot " << pivot_from_direction
2586 <<
" diff = " << diff;
2589 if (basis_factorization_.
NumUpdates() < 10) {
2590 Fractional threshold = parameters_.lu_factorization_pivot_threshold();
2591 threshold = std::min(threshold * 1.5, 0.9);
2592 VLOG(1) <<
"Increasing LU pivot threshold " << threshold;
2593 parameters_.set_lu_factorization_pivot_threshold(threshold);
2597 last_refactorization_reason_ = RefactorizationReason::IMPRECISE_PIVOT;
2601 basis_factorization_.
Update(entering_col, leaving_row, direction_));
2609Status RevisedSimplex::RefactorizeBasisIfNeeded(
bool* refactorize) {
2616 *refactorize =
false;
2621 if (
col >= integrality_scale_.
size()) {
2622 integrality_scale_.
resize(
col + 1, 0.0);
2624 integrality_scale_[
col] = scale;
2629 Cleanup update_deterministic_time_on_return(
2636 std::vector<ColIndex> candidates;
2639 if (std::abs(rc[
col]) < 1e-9) candidates.push_back(
col);
2642 bool refactorize =
false;
2645 for (
int i = 0; i < 10; ++i) {
2648 if (num_pivots >= 5)
break;
2649 if (candidates.empty())
break;
2653 std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2654 const ColIndex entering_col = candidates[
index];
2655 std::swap(candidates[
index], candidates.back());
2656 candidates.pop_back();
2670 ComputeDirection(entering_col);
2672 RowIndex leaving_row;
2674 bool local_refactorize =
false;
2676 ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2679 if (local_refactorize)
continue;
2680 if (step_length == kInfinity || step_length == -kInfinity)
continue;
2681 if (std::abs(step_length) <= 1e-6)
continue;
2682 if (leaving_row !=
kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2685 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2691 const auto get_diff = [
this](ColIndex
col,
Fractional old_value,
2693 if (
col >= integrality_scale_.
size() || integrality_scale_[
col] == 0.0) {
2697 return (std::abs(new_value * s - std::round(new_value * s)) -
2698 std::abs(old_value * s - std::round(old_value * s)));
2700 Fractional diff = get_diff(entering_col, variable_values_.
Get(entering_col),
2701 variable_values_.
Get(entering_col) + step);
2702 for (
const auto e : direction_) {
2703 const ColIndex
col = basis_[e.row()];
2705 const Fractional new_value = old_value - e.coefficient() * step;
2706 diff += get_diff(
col, old_value, new_value);
2710 if (diff > -1e-2)
continue;
2720 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2722 }
else if (step < 0.0) {
2723 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2730 const ColIndex leaving_col = basis_[leaving_row];
2741 entering_col, leaving_col, leaving_row, direction_, &update_row_);
2743 entering_col, leaving_row, direction_,
2752 const Fractional dir = -direction_[leaving_row] * step;
2753 const bool is_degenerate =
2757 if (!is_degenerate) {
2761 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2764 VLOG(1) <<
"Polish num_pivots: " << num_pivots <<
" gain:" << total_gain;
2783Status RevisedSimplex::PrimalMinimize(TimeLimit*
time_limit) {
2785 Cleanup update_deterministic_time_on_return(
2787 num_consecutive_degenerate_iterations_ = 0;
2788 bool refactorize =
false;
2789 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
2795 if (phase_ == Phase::FEASIBILITY) {
2815 last_refactorization_reason_ = RefactorizationReason::RC;
2819 last_refactorization_reason_ = RefactorizationReason::NORM;
2825 CorrectErrorsOnVariableValues();
2826 DisplayIterationInfo(
true, last_refactorization_reason_);
2827 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
2829 if (phase_ == Phase::FEASIBILITY) {
2841 if (phase_ == Phase::OPTIMIZATION &&
2842 ComputeObjectiveValue() < primal_objective_limit_) {
2843 VLOG(1) <<
"Stopping the primal simplex because"
2844 <<
" the objective limit " << primal_objective_limit_
2845 <<
" has been reached.";
2847 objective_limit_reached_ =
true;
2850 }
else if (phase_ == Phase::FEASIBILITY) {
2863 if (phase_ == Phase::FEASIBILITY) {
2866 if (primal_infeasibility <
2867 parameters_.primal_feasibility_tolerance()) {
2870 VLOG(1) <<
"Infeasible problem! infeasibility = "
2871 << primal_infeasibility;
2880 VLOG(1) <<
"Optimal reached, double checking...";
2883 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
2890 ComputeDirection(entering_col);
2909 VLOG(1) <<
"Skipping col #" << entering_col
2910 <<
" whose reduced cost is no longer valid under precise reduced "
2920 if (num_iterations_ == parameters_.max_number_of_iterations())
break;
2923 RowIndex leaving_row;
2925 if (phase_ == Phase::FEASIBILITY) {
2926 PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2927 &refactorize, &leaving_row,
2931 ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2935 last_refactorization_reason_ = RefactorizationReason::SMALL_PIVOT;
2939 if (step_length == kInfinity || step_length == -kInfinity) {
2942 DCHECK_NE(step_length, -kInfinity);
2945 VLOG(1) <<
"Infinite step length, double checking...";
2948 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
2951 if (phase_ == Phase::FEASIBILITY) {
2953 VLOG(1) <<
"Unbounded feasibility problem !?";
2958 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2959 const ColIndex
col = basis_[
row];
2960 solution_primal_ray_[
col] = -direction_[
row];
2962 solution_primal_ray_[entering_col] = 1.0;
2963 if (reduced_cost > 0.0) {
2970 Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2971 if (phase_ == Phase::FEASIBILITY && leaving_row !=
kInvalidRow) {
2981 step = ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2985 const ColIndex leaving_col =
2991 bool is_degenerate =
false;
2993 Fractional dir = -direction_[leaving_row] * step;
3001 if (!is_degenerate) {
3002 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
3011 entering_col, basis_[leaving_row], leaving_row, direction_,
3014 direction_, &update_row_);
3016 if (!is_degenerate) {
3025 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3027 if (is_degenerate) {
3028 timer.AlsoUpdate(&iteration_stats_.degenerate);
3030 timer.AlsoUpdate(&iteration_stats_.normal);
3039 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3041 }
else if (step < 0.0) {
3042 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3049 if (phase_ == Phase::FEASIBILITY && leaving_row !=
kInvalidRow) {
3055 &objective_[leaving_col]);
3060 if (step_length == 0.0) {
3061 num_consecutive_degenerate_iterations_++;
3063 if (num_consecutive_degenerate_iterations_ > 0) {
3064 iteration_stats_.degenerate_run_size.
Add(
3065 num_consecutive_degenerate_iterations_);
3066 num_consecutive_degenerate_iterations_ = 0;
3071 if (num_consecutive_degenerate_iterations_ > 0) {
3072 iteration_stats_.degenerate_run_size.
Add(
3073 num_consecutive_degenerate_iterations_);
3089Status RevisedSimplex::DualMinimize(
bool feasibility_phase,
3091 Cleanup update_deterministic_time_on_return(
3093 num_consecutive_degenerate_iterations_ = 0;
3094 bool refactorize =
false;
3095 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
3097 bound_flip_candidates_.clear();
3100 RowIndex leaving_row;
3105 ColIndex entering_col;
3119 const bool old_refactorize_value = refactorize;
3121 last_refactorization_reason_ = RefactorizationReason::RC;
3125 last_refactorization_reason_ = RefactorizationReason::NORM;
3146 if (feasibility_phase || old_refactorize_value) {
3158 if (!feasibility_phase) {
3159 MakeBoxedVariableDualFeasible(
3164 parameters_.dual_price_prioritize_norm());
3173 if (phase_ == Phase::OPTIMIZATION &&
3174 dual_objective_limit_ != kInfinity &&
3175 ComputeObjectiveValue() > dual_objective_limit_) {
3177 "Stopping the dual simplex because"
3178 " the objective limit ",
3179 dual_objective_limit_,
" has been reached.");
3181 objective_limit_reached_ =
true;
3186 DisplayIterationInfo(
false, last_refactorization_reason_);
3187 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
3191 if (!feasibility_phase) {
3194 MakeBoxedVariableDualFeasible(bound_flip_candidates_,
3196 bound_flip_candidates_.clear();
3204 if (feasibility_phase) {
3216 VLOG(1) <<
"Optimal reached, double checking.";
3220 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
3223 if (feasibility_phase) {
3228 if (num_dual_infeasible_positions_ == 0) {
3231 VLOG(1) <<
"DUAL infeasible in dual phase I.";
3247 if (feasibility_phase) {
3248 const Fractional price = dual_pricing_vector_[leaving_row];
3252 Square(price) / squared_norms[leaving_row]);
3260 if (feasibility_phase) {
3267 &bound_flip_candidates_, &entering_col));
3273 VLOG(1) <<
"No entering column. Double checking...";
3276 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
3280 if (feasibility_phase) {
3282 VLOG(1) <<
"Unbounded dual feasibility problem !?";
3286 solution_dual_ray_ =
3289 &solution_dual_ray_row_combination_);
3290 if (cost_variation < 0) {
3292 ChangeSign(&solution_dual_ray_row_combination_);
3303 if (std::abs(entering_coeff) < parameters_.dual_small_pivot_threshold() &&
3305 VLOG(1) <<
"Trying not to pivot by " << entering_coeff;
3308 last_refactorization_reason_ = RefactorizationReason::SMALL_PIVOT;
3312 ComputeDirection(entering_col);
3318 if (std::abs(direction_[leaving_row]) <
3319 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
3321 VLOG(1) <<
"Trying not pivot by " << entering_coeff <<
" ("
3322 << direction_[leaving_row]
3323 <<
") because the direction has a norm of "
3324 << direction_infinity_norm_;
3327 last_refactorization_reason_ = RefactorizationReason::SMALL_PIVOT;
3336 if (std::abs(direction_[leaving_row]) <= 1e-20) {
3337 const std::string error_message = absl::StrCat(
3338 "trying to pivot with number too small: ", direction_[leaving_row]);
3347 if (num_iterations_ == parameters_.max_number_of_iterations()) {
3361 const bool increasing_rc_is_needed =
3362 (cost_variation > 0.0) == (entering_coeff > 0.0);
3368 timer.AlsoUpdate(&iteration_stats_.degenerate);
3370 timer.AlsoUpdate(&iteration_stats_.normal);
3382 entering_col, leaving_row, direction_,
3388 if (feasibility_phase) {
3389 DualPhaseIUpdatePrice(leaving_row, entering_col);
3392 ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
3397 const ColIndex leaving_col = basis_[leaving_row];
3399 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3412Status RevisedSimplex::PrimalPush(TimeLimit*
time_limit) {
3414 Cleanup update_deterministic_time_on_return(
3416 bool refactorize =
false;
3420 primal_edge_norms_.
Clear();
3421 dual_edge_norms_.
Clear();
3425 std::vector<ColIndex> super_basic_cols;
3428 variable_values_.
Get(
col) != 0) {
3429 super_basic_cols.push_back(
col);
3433 while (!super_basic_cols.empty()) {
3441 CorrectErrorsOnVariableValues();
3442 DisplayIterationInfo(
true);
3446 ColIndex entering_col = super_basic_cols.back();
3460 const Fractional entering_value = variable_values_.
Get(entering_col);
3461 if (variables_info_.
GetTypeRow()[entering_col] ==
3463 if (entering_value > 0) {
3475 if (diff_lb <= diff_ub) {
3483 ComputeDirection(entering_col);
3486 RowIndex leaving_row;
3490 &refactorize, &leaving_row,
3493 if (refactorize)
continue;
3496 super_basic_cols.pop_back();
3498 if (step_length == kInfinity || step_length == -kInfinity) {
3499 if (variables_info_.
GetTypeRow()[entering_col] ==
3501 step_length = std::fabs(entering_value);
3503 VLOG(1) <<
"Infinite step for bounded variable ?!";
3509 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
3512 const ColIndex leaving_col =
3521 bool is_degenerate =
false;
3523 Fractional dir = -direction_[leaving_row] * step;
3531 if (!is_degenerate) {
3532 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
3539 if (!is_degenerate) {
3548 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3550 if (is_degenerate) {
3551 timer.AlsoUpdate(&iteration_stats_.degenerate);
3553 timer.AlsoUpdate(&iteration_stats_.normal);
3560 if (variables_info_.
GetTypeRow()[entering_col] ==
3562 variable_values_.
Set(entering_col, 0.0);
3563 }
else if (step > 0.0) {
3564 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3566 }
else if (step < 0.0) {
3567 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3576 if (!super_basic_cols.empty()) {
3577 SOLVER_LOG(logger_,
"Push terminated early with ", super_basic_cols.size(),
3578 " super-basic variables remaining.");
3588ColIndex RevisedSimplex::SlackColIndex(RowIndex
row)
const {
3595 result.append(iteration_stats_.
StatString());
3596 result.append(ratio_test_stats_.
StatString());
3597 result.append(entering_variable_.
StatString());
3600 result.append(variable_values_.
StatString());
3601 result.append(primal_edge_norms_.
StatString());
3602 result.append(dual_edge_norms_.
StatString());
3604 result.append(basis_factorization_.
StatString());
3609void RevisedSimplex::DisplayAllStats() {
3610 if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3612 absl::FPrintF(stderr,
"%s", GetPrettySolverStats());
3616Fractional RevisedSimplex::ComputeObjectiveValue()
const {
3622Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue()
const {
3626 return objective_scaling_factor_ * (sum + objective_offset_);
3631 deterministic_random_.seed(
parameters.random_seed());
3632 absl_random_ = absl::BitGen(absl::SeedSeq({
parameters.random_seed()}));
3635 PropagateParameters();
3638void RevisedSimplex::PropagateParameters() {
3648void RevisedSimplex::DisplayIterationInfo(
bool primal,
3649 RefactorizationReason reason) {
3651 const std::string first_word = primal ?
"Primal " :
"Dual ";
3658 if (reason != RefactorizationReason::DEFAULT) {
3660 case RefactorizationReason::DEFAULT:
3661 info =
" [default]";
3663 case RefactorizationReason::SMALL_PIVOT:
3664 info =
" [small pivot]";
3666 case RefactorizationReason::IMPRECISE_PIVOT:
3667 info =
" [imprecise pivot]";
3669 case RefactorizationReason::NORM:
3672 case RefactorizationReason::RC:
3673 info =
" [reduced costs]";
3675 case RefactorizationReason::VAR_VALUES:
3676 info =
" [var values]";
3678 case RefactorizationReason::FINAL_CHECK:
3685 case Phase::FEASIBILITY: {
3686 const int64_t iter = num_iterations_;
3689 if (parameters_.use_dual_simplex()) {
3690 if (parameters_.use_dedicated_dual_feasibility_algorithm()) {
3698 name =
"sum_dual_infeasibilities";
3701 name =
"sum_primal_infeasibilities";
3704 SOLVER_LOG(logger_, first_word,
"feasibility phase, iteration # ", iter,
3705 ", ",
name,
" = ", absl::StrFormat(
"%.15E", objective), info);
3708 case Phase::OPTIMIZATION: {
3709 const int64_t iter = num_iterations_ - num_feasibility_iterations_;
3715 const Fractional objective = ComputeInitialProblemObjectiveValue();
3716 SOLVER_LOG(logger_, first_word,
"optimization phase, iteration # ", iter,
3717 ", objective = ", absl::StrFormat(
"%.15E", objective), info);
3721 const int64_t iter = num_iterations_ - num_feasibility_iterations_ -
3722 num_optimization_iterations_;
3723 SOLVER_LOG(logger_, first_word,
"push phase, iteration # ", iter,
3724 ", remaining_variables_to_push = ",
3725 ComputeNumberOfSuperBasicVariables(), info);
3730void RevisedSimplex::DisplayErrors() {
3734 SOLVER_LOG(logger_,
"Primal infeasibility (bounds) = ",
3736 SOLVER_LOG(logger_,
"Primal residual |A.x - b| = ",
3738 SOLVER_LOG(logger_,
"Dual infeasibility (reduced costs) = ",
3740 SOLVER_LOG(logger_,
"Dual residual |c_B - y.B| = ",
3746std::string StringifyMonomialWithFlags(
const Fractional a,
3747 absl::string_view
x) {
3749 a,
x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3755std::string StringifyWithFlags(
const Fractional x) {
3757 absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3762std::string RevisedSimplex::SimpleVariableInfo(ColIndex
col)
const {
3768 absl::StrAppendFormat(&output,
"%d (%s) = %s, %s, %s, [%s,%s]",
col.value(),
3769 variable_name_[
col],
3770 StringifyWithFlags(variable_values_.
Get(
col)),
3778void RevisedSimplex::DisplayInfoOnVariables()
const {
3779 if (VLOG_IS_ON(3)) {
3780 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3784 objective_coefficient * variable_value;
3785 VLOG(3) << SimpleVariableInfo(
col) <<
". " << variable_name_[
col] <<
" = "
3786 << StringifyWithFlags(variable_value) <<
" * "
3787 << StringifyWithFlags(objective_coefficient)
3788 <<
"(obj) = " << StringifyWithFlags(objective_contribution);
3790 VLOG(3) <<
"------";
3794void RevisedSimplex::DisplayVariableBounds() {
3795 if (VLOG_IS_ON(3)) {
3799 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3800 switch (variable_type[
col]) {
3804 VLOG(3) << variable_name_[
col]
3808 VLOG(3) << variable_name_[
col]
3813 <<
" <= " << variable_name_[
col]
3817 VLOG(3) << variable_name_[
col] <<
" = "
3821 LOG(DFATAL) <<
"Column " <<
col <<
" has no meaningful status.";
3831 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3832 ComputeDirection(
col);
3833 for (
const auto e : direction_) {
3834 if (column_scales ==
nullptr) {
3835 dictionary[e.row()].SetCoefficient(
col, e.coefficient());
3839 col < column_scales->
size() ? (*column_scales)[
col] : 1.0;
3841 ? (*column_scales)[
GetBasis(e.row())]
3843 dictionary[e.row()].SetCoefficient(
3844 col, direction_[e.row()] * (numerator / denominator));
3856 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3860void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3861 if (VLOG_IS_ON(3)) {
3863 DisplayInfoOnVariables();
3865 std::string output =
"z = " + StringifyWithFlags(ComputeObjectiveValue());
3868 absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[
col],
3869 variable_name_[
col]));
3871 VLOG(3) << output <<
";";
3873 const RevisedSimplexDictionary dictionary(
nullptr,
this);
3877 ColIndex basic_col = basis_[r];
3878 absl::StrAppend(&output, variable_name_[basic_col],
" = ",
3879 StringifyWithFlags(variable_values_.
Get(basic_col)));
3880 for (
const SparseRowEntry e :
row) {
3881 if (e.col() != basic_col) {
3882 absl::StrAppend(&output,
3883 StringifyMonomialWithFlags(e.coefficient(),
3884 variable_name_[e.col()]));
3887 VLOG(3) << output <<
";";
3889 VLOG(3) <<
"------";
3890 DisplayVariableBounds();
3895void RevisedSimplex::DisplayProblem()
const {
3898 if (VLOG_IS_ON(3)) {
3899 DisplayInfoOnVariables();
3900 std::string output =
"min: ";
3901 bool has_objective =
false;
3902 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3904 has_objective |= (coeff != 0.0);
3905 absl::StrAppend(&output,
3906 StringifyMonomialWithFlags(coeff, variable_name_[
col]));
3908 if (!has_objective) {
3909 absl::StrAppend(&output,
" 0");
3911 VLOG(3) << output <<
";";
3912 for (RowIndex
row(0);
row < num_rows_; ++
row) {
3914 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3915 absl::StrAppend(&output,
3916 StringifyMonomialWithFlags(
3918 variable_name_[
col]));
3920 VLOG(3) << output <<
" = 0;";
3922 VLOG(3) <<
"------";
3926void RevisedSimplex::AdvanceDeterministicTime(TimeLimit*
time_limit) {
3929 const double deterministic_time_delta =
3930 current_deterministic_time - last_deterministic_time_update_;
3931 time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3932 last_deterministic_time_update_ = current_deterministic_time;
3935#undef DCHECK_COL_BOUNDS
3936#undef DCHECK_ROW_BOUNDS
bool IsSet(IndexType i) const
Returns true if the bit at position i is set.
ConstView const_view() const
void EnableLogging(bool enable)
void SetLogToStdOut(bool enable)
Should all messages be displayed on stdout ?
bool LoggingIsEnabled() const
Returns true iff logging is enabled.
std::string StatString() const
std::string StatString() const
double DeterministicTime() const
ABSL_MUST_USE_RESULT Status ForceRefactorization()
ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
void SetParameters(const GlopParameters ¶meters)
Sets the parameters for this component.
const ColumnPermutation & GetColumnPermutation() const
int NumUpdates() const
Returns the number of updates since last refactorization.
void RightSolve(ScatteredColumn *d) const
Right solves the system B.d = a where the input is the initial value of d.
RowToColMapping ComputeInitialBasis(const std::vector< ColIndex > &candidates)
ABSL_MUST_USE_RESULT Status Initialize()
bool IsRefactorized() const
Returns true if the factorization was just recomputed.
Fractional ComputeInfinityNormConditionNumberUpperBound() const
void RightSolveForProblemColumn(ColIndex col, ScatteredColumn *d) const
ABSL_MUST_USE_RESULT Status Refactorize()
void SetColumnPermutationToIdentity()
Fractional LookUpCoefficient(RowIndex index) const
RowIndex EntryRow(EntryIndex i) const
EntryIndex num_entries() const
Fractional GetFirstCoefficient() const
Fractional EntryCoefficient(EntryIndex i) const
void ColumnCopyToDenseColumn(ColIndex col, DenseColumn *dense_column) const
void Reset(RowIndex num_rows)
void PopulateFromMatrixView(const MatrixView &input)
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn::View dense_column) const
void PopulateFromSparseMatrixAndAddSlacks(const SparseMatrix &input)
EntryIndex num_entries() const
Returns the matrix dimensions. See same functions in SparseMatrix.
RowIndex num_rows() const
bool IsEmpty() const
Returns whether or not this matrix contains any non-zero entries.
ColIndex num_cols() const
Fractional ColumnScalarProduct(ColIndex col, const DenseRow &vector) const
void PopulateFromTranspose(const CompactSparseMatrix &input)
ColumnView column(ColIndex col) const
void ColumnAddMultipleToSparseScatteredColumn(ColIndex col, Fractional multiplier, ScatteredColumn *column) const
DenseColumn::ConstView GetEdgeSquaredNorms()
void ResizeOnNewRows(RowIndex new_size)
std::string StatString() const
Stats related functions.
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
Updates the norms if the columns of the basis where permuted.
bool NeedsBasisRefactorization() const
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
void SetParameters(const GlopParameters ¶meters)
Sets the algorithm parameters.
bool TestPrecision(RowIndex leaving_row, const ScatteredRow &unit_row_left_inverse)
void ClearAndResize(Index n)
std::string StatString() const
Returns some stats about this class if they are enabled.
void DenseAddOrUpdate(Index position, Fractional value)
void Remove(Index position)
Removes the given index from the set of candidates.
void AddOrUpdate(Index position, Fractional value)
void SetParameters(const GlopParameters ¶meters)
Sets the parameters.
double DeterministicTime() const
std::string StatString() const
Stats related functions.
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)
ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn(bool nothing_to_recompute, const UpdateRow &update_row, Fractional cost_variation, ColIndex *entering_col)
bool IsMaximizationProblem() const
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
std::string StatString() const
Returns a string with statistics about this class.
void UpdateBeforeBasisPivot(ColIndex entering_col, ColIndex leaving_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
double DeterministicTime() const
Deterministic time used by the scalar product computation of this class.
void SetPricingRule(GlopParameters::PricingRule rule)
This changes what GetSquaredNorms() returns.
bool TestEnteringEdgeNormPrecision(ColIndex entering_col, const ScatteredColumn &direction)
bool NeedsBasisRefactorization() const
void SetParameters(const GlopParameters ¶meters)
Sets the algorithm parameters.
void RecomputePriceAt(ColIndex col)
Triggers a recomputation of the price at the given column only.
void ForceRecomputation()
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
void UpdateBeforeBasisPivot(ColIndex entering_col, UpdateRow *update_row)
ColIndex GetBestEnteringColumn()
std::string StatString() const
Stats related functions.
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables()
Fractional ComputeMaximumDualInfeasibility()
Fractional GetDualFeasibilityTolerance() const
Returns the current dual feasibility tolerance.
void SetParameters(const GlopParameters ¶meters)
Sets the pricing parameters. This does not change the pricing rule.
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)
bool NeedsBasisRefactorization() const
Fractional ComputeMaximumDualResidual()
Fractional TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction)
bool HasCostShift() const
bool IsValidPrimalEnteringCandidate(ColIndex col) const
Does basic checking of an entering candidate.
void UpdateDataOnBasisPermutation()
Invalidates the data that depends on the order of the column in basis_.
bool AreReducedCostsPrecise()
void ClearAndRemoveCostShifts()
const DenseColumn & GetDualValues()
Returns the dual values associated to the current basis.
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
bool AreReducedCostsRecomputed()
double DeterministicTime() const
The deterministic time used by this class.
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
DenseRow::ConstView GetFullReducedCosts()
DenseRow::ConstView GetReducedCosts()
void MakeReducedCostsPrecise()
Fractional ComputeSumOfDualInfeasibilities()
void ResetForNewObjective()
Invalidates all internal structure that depends on the objective function.
Fractional GetVariableValue(ColIndex col) const
const DenseColumn & GetDualRay() const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
ColIndex GetBasis(RowIndex row) const
ProblemStatus GetProblemStatus() const
ConstraintStatus GetConstraintStatus(RowIndex row) const
void NotifyThatMatrixIsUnchangedForNextSolve()
Fractional GetObjectiveValue() const
const BasisState & GetState() const
void LoadStateForNextSolve(const BasisState &state)
Uses the given state as a warm-start for the next Solve() call.
Fractional GetDualValue(RowIndex row) const
Fractional GetReducedCost(ColIndex col) const
ColIndex GetProblemNumCols() const
void ClearStateForNextSolve()
Fractional GetConstraintActivity(RowIndex row) const
RowMajorSparseMatrix ComputeDictionary(const DenseRow *column_scales)
const DenseRow & GetDualRayRowCombination() const
This is the "dual ray" linear combination of the matrix rows.
RowIndex GetProblemNumRows() const
Getters to retrieve all the information computed by the last Solve().
void ComputeBasicVariablesForState(const LinearProgram &linear_program, const BasisState &state)
const DenseRow & GetPrimalRay() const
std::string StatString()
Returns statistics about this class as a string.
const BasisFactorization & GetBasisFactorization() const
VariableStatus GetVariableStatus(ColIndex col) const
void SetParameters(const GlopParameters ¶meters)
Sets or gets the algorithm parameters to be used on the next Solve().
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
double DeterministicTime() const
int64_t GetNumberOfIterations() const
void SetStartingVariableValuesForNextSolve(const DenseRow &values)
void NotifyThatMatrixIsChangedForNextSolve()
static Status OK()
Improves readability but identical to 0-arg constructor.
@ ERROR_LU
The LU factorization of the current basis couldn't be computed.
void AssignToZero(IntType size)
StrictITISpan< RowIndex, const Fractional > ConstView
ConstView const_view() const
void resize(IntType size)
void SetParameters(const GlopParameters ¶meters)
Sets the algorithm parameters.
void ComputeFullUpdateRow(RowIndex leaving_row, DenseRow *output) const
std::string StatString() const
Returns statistics about this class as a string.
const ScatteredRow & GetUnitRowLeftInverse() const
void ComputeUnitRowLeftInverse(RowIndex leaving_row)
void ComputeUpdateRow(RowIndex leaving_row)
bool IsComputedFor(RowIndex leaving_row) const
Returns true if ComputeUpdateRow() was called since the last Invalidate().
double DeterministicTime() const
Deterministic time used by the scalar product computation of this class.
Fractional GetCoefficient(ColIndex col) const
absl::Span< const ColIndex > GetNonZeroPositions() const
Fractional ComputeSumOfPrimalInfeasibilities() const
bool UpdatePrimalPhaseICosts(const Rows &rows, DenseRow *objective)
void Set(ColIndex col, Fractional value)
Sets the variable value of a given column.
std::string StatString() const
Parameters and stats functions.
void UpdateOnPivoting(const ScatteredColumn &direction, ColIndex entering_col, Fractional step)
void UpdateGivenNonBasicVariables(absl::Span< const ColIndex > cols_to_update, bool update_basic_variables)
Fractional ComputeMaximumPrimalInfeasibility() const
void RecomputeBasicVariableValues()
const DenseRow & GetDenseRow() const
void ResetAllNonBasicVariableValues(const DenseRow &free_initial_values)
Fractional ComputeMaximumPrimalResidual() const
void RecomputeDualPrices(bool put_more_importance_on_norm=false)
void UpdateDualPrices(absl::Span< const RowIndex > row)
Fractional Get(ColIndex col) const
Getters for the variable values.
void SetNonBasicVariableValueFromStatus(ColIndex col)
void TransformToDualPhaseIProblem(Fractional dual_feasibility_tolerance, DenseRow::ConstView reduced_costs)
const DenseBitRow & GetCanDecreaseBitRow() const
const DenseBitRow & GetCanIncreaseBitRow() const
const VariableStatusRow & GetStatusRow() const
const DenseRow & GetVariableLowerBounds() const
Returns the variable bounds.
const VariableTypeRow & GetTypeRow() const
const DenseRow & GetVariableUpperBounds() const
void MakeBoxedVariableRelevant(bool value)
const DenseBitRow & GetNotBasicBitRow() const
Fractional GetBoundDifference(ColIndex col) const
Returns the distance between the upper and lower bound of the given column.
const DenseBitRow & GetIsRelevantBitRow() const
int ChangeUnusedBasicVariablesToFree(const RowToColMapping &basis)
void UpdateToBasicStatus(ColIndex col)
const DenseBitRow & GetNonBasicBoxedVariables() const
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)
void EndDualPhaseI(Fractional dual_feasibility_tolerance, DenseRow::ConstView reduced_costs)
bool LoadBoundsAndReturnTrueIfUnchanged(const DenseRow &new_lower_bounds, const DenseRow &new_upper_bounds)
void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols, const BasisState &state)
void InitializeToDefaultStatus()
const DenseBitRow & GetIsBasicBitRow() const
int SnapFreeVariablesToBound(Fractional distance, const DenseRow &starting_values)
const std::string name
A name for logging purposes.
constexpr ColIndex kInvalidCol(-1)
constexpr double kInfinity
Infinity for type Fractional.
std::string GetVariableTypeString(VariableType variable_type)
Returns the string representation of the VariableType enum.
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Fractional Square(Fractional f)
StrictITIVector< RowIndex, bool > DenseBooleanColumn
Column of booleans.
Permutation< ColIndex > ColumnPermutation
std::string Stringify(const Fractional x, bool fraction)
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
Bitset64< ColIndex > DenseBitRow
Row of bits.
const DenseRow & Transpose(const DenseColumn &col)
Similar comment as the other Transpose() implementation above.
std::string GetVariableStatusString(VariableStatus status)
Returns the string representation of the VariableStatus enum.
Index ColToIntIndex(ColIndex col)
Get the integer index corresponding to the col.
constexpr const uint64_t kDeterministicSeed
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
bool IsFinite(Fractional value)
constexpr RowIndex kInvalidRow(-1)
VariableType
Different types of variables.
@ UPPER_AND_LOWER_BOUNDED
bool AreFirstColumnsAndRowsExactlyEquals(RowIndex num_rows, ColIndex num_cols, const SparseMatrix &matrix_a, const CompactSparseMatrix &matrix_b)
StrictITIVector< ColIndex, VariableType > VariableTypeRow
Row of variable types.
Fractional InfinityNorm(const DenseColumn &v)
Returns the maximum of the coefficients of 'v'.
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
Changes the sign of all the entries in the given vector.
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
Row of variable statuses.
std::string StringifyMonomial(const Fractional a, absl::string_view x, bool fraction)
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
void ApplyColumnPermutationToRowIndexedVector(const Permutation< ColIndex > &col_perm, RowIndexedVector *v)
ProblemStatus
Different statuses for a given problem.
@ ABNORMAL
An error occurred during the solving process.
@ INIT
The solver didn't had a chance to prove anything.
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
Returns the ConstraintStatus corresponding to a given VariableStatus.
static double DeterministicTimeForFpOperations(int64_t n)
std::string GetProblemStatusString(ProblemStatus problem_status)
Returns the string representation of the ProblemStatus enum.
In SWIG mode, we don't want anything besides these top-level includes.
util_intops::StrongVector< ColumnEntryIndex, ElementIndex, ElementAllocator > SparseColumn
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)
Macro to simplify error propagation between function returning Status.
#define GLOP_RETURN_ERROR_IF_NULL(arg)
Macro to check that a pointer argument is not null.
bool IsEmpty() const
Returns true if this state is empty.
VariableStatusRow statuses
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation)
void ClearSparseMask()
Efficiently clears the is_non_zero vector.
StrictITIVector< Index, Fractional > values
std::vector< Index > non_zeros
#define SOLVER_LOG(logger,...)