25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_format.h"
32 #include "ortools/glop/parameters.pb.h"
41 ABSL_FLAG(
bool, simplex_display_numbers_as_fractions,
false,
42 "Display numbers as fractions.");
43 ABSL_FLAG(
bool, simplex_stop_after_first_basis,
false,
44 "Stop after first basis has been computed.");
45 ABSL_FLAG(
bool, simplex_stop_after_feasibility,
false,
46 "Stop after first phase has been completed.");
47 ABSL_FLAG(
bool, simplex_display_stats,
false,
"Display algorithm statistics.");
57 explicit Cleanup(std::function<
void()> closure)
58 : closure_(std::move(closure)) {}
59 ~Cleanup() { closure_(); }
62 std::function<void()> closure_;
66 #define DCHECK_COL_BOUNDS(col) \
69 DCHECK_GT(num_cols_, col); \
73 #define DCHECK_ROW_BOUNDS(row) \
76 DCHECK_GT(num_rows_, row); \
89 random_(deterministic_random_),
90 basis_factorization_(&compact_matrix_, &basis_),
91 variables_info_(compact_matrix_),
92 primal_edge_norms_(compact_matrix_, variables_info_,
93 basis_factorization_),
94 dual_edge_norms_(basis_factorization_),
95 dual_prices_(random_),
96 variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
97 basis_factorization_, &dual_edge_norms_, &dual_prices_),
98 update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
99 basis_factorization_),
100 reduced_costs_(compact_matrix_,
objective_, basis_, variables_info_,
101 basis_factorization_, random_),
102 entering_variable_(variables_info_, random_, &reduced_costs_),
103 primal_prices_(random_, variables_info_, &primal_edge_norms_,
107 function_stats_(
"SimplexFunctionStats"),
116 variable_starting_values_.
clear();
121 solution_state_ = state;
122 solution_state_has_been_set_externally_ =
true;
127 variable_starting_values_ = values;
131 notify_that_matrix_is_unchanged_ =
true;
135 notify_that_matrix_is_unchanged_ =
false;
142 Cleanup update_deterministic_time_on_return(
145 default_logger_.
EnableLogging(parameters_.log_search_progress());
151 const double start_time =
time_limit->GetElapsedTime();
154 DisplayBasicVariableStatistics();
157 dual_infeasibility_improvement_direction_.
clear();
161 phase_ = Phase::FEASIBILITY;
163 num_feasibility_iterations_ = 0;
164 num_optimization_iterations_ = 0;
165 num_push_iterations_ = 0;
166 feasibility_time_ = 0.0;
167 optimization_time_ = 0.0;
175 solution_state_has_been_set_externally_ =
true;
178 ComputeNumberOfEmptyRows();
179 ComputeNumberOfEmptyColumns();
182 if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
187 const bool use_dual = parameters_.use_dual_simplex();
192 primal_edge_norms_.
SetPricingRule(parameters_.feasibility_rule());
194 if (parameters_.perturb_costs_in_dual_simplex()) {
198 if (parameters_.use_dedicated_dual_feasibility_algorithm()) {
201 DualMinimize(phase_ == Phase::FEASIBILITY,
time_limit));
218 MakeBoxedVariableDualFeasible(
233 if (initial_infeasibility <
235 SOLVER_LOG(logger_,
"Initial basis is dual feasible.");
237 MakeBoxedVariableDualFeasible(
260 variable_starting_values_);
275 SOLVER_LOG(logger_,
"Infeasible after first phase.");
286 InitializeObjectiveAndTestIfUnchanged(lp);
293 phase_ = Phase::OPTIMIZATION;
294 feasibility_time_ =
time_limit->GetElapsedTime() - start_time;
295 primal_edge_norms_.
SetPricingRule(parameters_.optimization_rule());
296 num_feasibility_iterations_ = num_iterations_;
311 for (
int num_optims = 0;
315 num_optims <= parameters_.max_number_of_reoptimizations() &&
316 !objective_limit_reached_ &&
317 (num_iterations_ == 0 ||
318 num_iterations_ < parameters_.max_number_of_iterations()) &&
320 !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
330 DualMinimize(phase_ == Phase::FEASIBILITY,
time_limit));
341 if (!integrality_scale_.
empty() &&
368 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
373 "PRIMAL_UNBOUNDED was reported, but the residual and/or "
374 "dual infeasibility is above the tolerance");
375 if (parameters_.change_status_to_imprecise()) {
396 double max_magnitude = 0.0;
400 double cost_delta = 0.0;
401 for (ColIndex
col(0);
col < num_cols_; ++
col) {
402 cost_delta += solution_primal_ray_[
col] * objective_[
col];
406 solution_primal_ray_[
col];
408 max_magnitude =
std::max(solution_primal_ray_[
col], max_magnitude);
413 -solution_primal_ray_[
col];
415 max_magnitude =
std::max(-solution_primal_ray_[
col], max_magnitude);
418 SOLVER_LOG(logger_,
"Primal unbounded ray: max blocking magnitude = ",
419 max_magnitude,
", min distance to bound + ", tolerance,
" = ",
420 min_distance,
", ray cost delta = ", cost_delta);
421 if (min_distance * std::abs(cost_delta) < 1 &&
424 "PRIMAL_UNBOUNDED was reported, but the tolerance are good "
425 "and the unbounded ray is not great.");
427 "The difference between unbounded and optimal can depends "
428 "on a slight change of tolerance, trying to see if we are "
429 "at OPTIMAL after postsolve.");
435 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
440 "DUAL_UNBOUNDED was reported, but the residual and/or "
441 "dual infeasibility is above the tolerance");
442 if (parameters_.change_status_to_imprecise()) {
455 for (ColIndex
col(0);
col < num_cols_; ++
col) {
456 const Fractional coeff = solution_dual_ray_row_combination_[
col];
463 }
else if (coeff < 0) {
472 " infeasibility=", implied_lb);
473 if (implied_lb < tolerance || error > tolerance) {
475 "DUAL_UNBOUNDED was reported, but the dual ray is not "
476 "proving infeasibility with high enough tolerance");
477 if (parameters_.change_status_to_imprecise()) {
488 parameters_.solution_feasibility_tolerance();
493 if (primal_residual > solution_tolerance ||
494 dual_residual > solution_tolerance) {
496 "OPTIMAL was reported, yet one of the residuals is "
497 "above the solution feasibility tolerance after the "
498 "shift/perturbation are removed.");
499 if (parameters_.change_status_to_imprecise()) {
509 primal_residual, parameters_.primal_feasibility_tolerance());
511 std::max(dual_residual, parameters_.dual_feasibility_tolerance());
516 if (primal_infeasibility > primal_tolerance &&
517 dual_infeasibility > dual_tolerance) {
519 "OPTIMAL was reported, yet both of the infeasibility "
520 "are above the tolerance after the "
521 "shift/perturbation are removed.");
522 if (parameters_.change_status_to_imprecise()) {
525 }
else if (primal_infeasibility > primal_tolerance) {
526 if (num_optims == parameters_.max_number_of_reoptimizations()) {
528 "The primal infeasibility is still higher than the "
529 "requested internal tolerance, but the maximum "
530 "number of optimization is reached.");
534 SOLVER_LOG(logger_,
"Re-optimizing with dual simplex ... ");
536 }
else if (dual_infeasibility > dual_tolerance) {
537 if (num_optims == parameters_.max_number_of_reoptimizations()) {
539 "The dual infeasibility is still higher than the "
540 "requested internal tolerance, but the maximum "
541 "number of optimization is reached.");
545 SOLVER_LOG(logger_,
"Re-optimizing with primal simplex ... ");
556 if (parameters_.change_status_to_imprecise() &&
558 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
577 total_time_ =
time_limit->GetElapsedTime() - start_time;
578 optimization_time_ = total_time_ - feasibility_time_;
579 num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
583 if (!variable_starting_values_.
empty()) {
584 const int num_super_basic = ComputeNumberOfSuperBasicVariables();
585 if (num_super_basic > 0) {
587 "Num super-basic variables left after optimize phase: ",
589 if (parameters_.push_to_vertex()) {
592 phase_ = Phase::PUSH;
598 "Skipping push phase because optimize didn't succeed.");
604 total_time_ =
time_limit->GetElapsedTime() - start_time;
605 push_time_ = total_time_ - feasibility_time_ - optimization_time_;
606 num_push_iterations_ = num_iterations_ - num_feasibility_iterations_ -
607 num_optimization_iterations_;
610 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
623 solution_objective_value_ =
627 solution_objective_value_ = -solution_objective_value_;
631 variable_starting_values_.
clear();
637 return problem_status_;
641 return solution_objective_value_;
645 return num_iterations_;
653 return variable_values_.
Get(
col);
657 return solution_reduced_costs_[
col];
661 return solution_reduced_costs_;
665 return solution_dual_values_[
row];
677 return -variable_values_.
Get(SlackColIndex(
row));
695 return solution_primal_ray_;
699 return solution_dual_ray_;
704 return solution_dual_ray_row_combination_;
711 return basis_factorization_;
714 std::string RevisedSimplex::GetPrettySolverStats()
const {
715 return absl::StrFormat(
716 "Problem status : %s\n"
717 "Solving time : %-6.4g\n"
718 "Number of iterations : %u\n"
719 "Time for solvability (first phase) : %-6.4g\n"
720 "Number of iterations for solvability : %u\n"
721 "Time for optimization : %-6.4g\n"
722 "Number of iterations for optimization : %u\n"
723 "Stop after first basis : %d\n",
725 feasibility_time_, num_feasibility_iterations_, optimization_time_,
726 num_optimization_iterations_,
727 absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
740 void RevisedSimplex::SetVariableNames() {
741 variable_name_.
resize(num_cols_,
"");
742 for (ColIndex
col(0);
col < first_slack_col_; ++
col) {
743 const ColIndex var_index =
col + 1;
746 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
747 const ColIndex var_index =
col - first_slack_col_ + 1;
752 void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
758 bool RevisedSimplex::BasisIsConsistent()
const {
761 for (RowIndex
row(0);
row < num_rows_; ++
row) {
762 const ColIndex
col = basis_[
row];
763 if (!is_basic.IsSet(
col))
return false;
766 ColIndex cols_in_basis(0);
767 ColIndex cols_not_in_basis(0);
768 for (ColIndex
col(0);
col < num_cols_; ++
col) {
769 cols_in_basis += is_basic.IsSet(
col);
770 cols_not_in_basis += !is_basic.IsSet(
col);
771 if (is_basic.IsSet(
col) !=
777 if (cols_not_in_basis != num_cols_ -
RowToColIndex(num_rows_))
return false;
783 void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
792 DCHECK_NE(basis_[basis_row], entering_col);
795 const ColIndex leaving_col = basis_[basis_row];
806 basis_[basis_row] = entering_col;
814 class ColumnComparator {
817 bool operator()(ColIndex col_a, ColIndex col_b)
const {
818 return value_[col_a] < value_[col_b];
835 void RevisedSimplex::UseSingletonColumnInInitialBasis(
RowToColMapping* basis) {
842 std::vector<ColIndex> singleton_column;
843 DenseRow cost_variation(num_cols_, 0.0);
846 for (ColIndex
col(0);
col < num_cols_; ++
col) {
851 cost_variation[
col] = objective_[
col] / std::abs(slope);
853 cost_variation[
col] = -objective_[
col] / std::abs(slope);
855 singleton_column.push_back(
col);
857 if (singleton_column.empty())
return;
864 ColumnComparator comparator(cost_variation);
865 std::sort(singleton_column.begin(), singleton_column.end(), comparator);
866 DCHECK_LE(cost_variation[singleton_column.front()],
867 cost_variation[singleton_column.back()]);
875 for (
const ColIndex
col : singleton_column) {
887 if (error_[
row] == 0.0)
continue;
907 DCHECK_NE(box_width, 0.0);
908 DCHECK_NE(error_[
row], 0.0);
912 error_[
row] -= coeff * box_width;
913 SetNonBasicVariableStatusAndDeriveValue(
col,
919 error_[
row] += coeff * box_width;
920 SetNonBasicVariableStatusAndDeriveValue(
col,
927 bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
928 const LinearProgram& lp,
bool lp_is_in_equation_form,
929 bool* only_change_is_new_rows,
bool* only_change_is_new_cols,
930 ColIndex* num_new_cols) {
932 DCHECK(only_change_is_new_rows !=
nullptr);
933 DCHECK(only_change_is_new_cols !=
nullptr);
934 DCHECK(num_new_cols !=
nullptr);
935 DCHECK_EQ(num_cols_, compact_matrix_.
num_cols());
936 DCHECK_EQ(num_rows_, compact_matrix_.
num_rows());
939 const bool old_part_of_matrix_is_unchanged =
941 num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
944 const ColIndex lp_first_slack =
945 lp_is_in_equation_form ? lp.GetFirstSlackVariable() : lp.num_variables();
950 if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
951 lp_first_slack == first_slack_col_) {
956 if (parameters_.use_transposed_matrix()) {
957 if (transposed_matrix_.
IsEmpty()) {
961 transposed_matrix_.
Reset(RowIndex(0));
968 *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
969 lp.num_constraints() > num_rows_ &&
970 lp_first_slack == first_slack_col_;
974 *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
975 lp.num_constraints() == num_rows_ &&
976 lp_first_slack > first_slack_col_;
977 *num_new_cols = *only_change_is_new_cols ? lp_first_slack - first_slack_col_
981 first_slack_col_ = lp_first_slack;
984 num_rows_ = lp.num_constraints();
985 num_cols_ = lp_first_slack +
RowToColIndex(lp.num_constraints());
988 if (lp_is_in_equation_form) {
995 if (parameters_.use_transposed_matrix()) {
998 transposed_matrix_.
Reset(RowIndex(0));
1005 bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1006 const LinearProgram& lp,
bool lp_is_in_equation_form,
1007 ColIndex num_new_cols) {
1009 DCHECK_LE(num_new_cols, first_slack_col_);
1010 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1015 for (ColIndex
col(0);
col < first_new_col; ++
col) {
1023 for (ColIndex
col(first_new_col);
col < first_slack_col_; ++
col) {
1024 if (lp.variable_lower_bounds()[
col] != 0.0 &&
1025 lp.variable_upper_bounds()[
col] != 0.0) {
1031 if (lp_is_in_equation_form) {
1032 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
1039 DCHECK_EQ(num_rows_, lp.num_constraints());
1040 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1043 -lp.constraint_upper_bounds()[
row] ||
1045 -lp.constraint_lower_bounds()[
row]) {
1053 bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
1054 const LinearProgram& lp) {
1057 bool objective_is_unchanged =
true;
1058 objective_.
resize(num_cols_, 0.0);
1062 DCHECK_GE(num_cols_, lp.num_variables());
1063 for (ColIndex
col(lp.num_variables());
col < num_cols_; ++
col) {
1064 if (objective_[
col] != 0.0) {
1065 objective_is_unchanged =
false;
1066 objective_[
col] = 0.0;
1070 if (lp.IsMaximizationProblem()) {
1072 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
1074 if (objective_[
col] != coeff) {
1075 objective_is_unchanged =
false;
1076 objective_[
col] = coeff;
1079 objective_offset_ = -lp.objective_offset();
1080 objective_scaling_factor_ = -lp.objective_scaling_factor();
1082 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
1084 if (objective_[
col] != coeff) {
1085 objective_is_unchanged =
false;
1086 objective_[
col] = coeff;
1089 objective_offset_ = lp.objective_offset();
1090 objective_scaling_factor_ = lp.objective_scaling_factor();
1093 return objective_is_unchanged;
1096 void RevisedSimplex::InitializeObjectiveLimit(
const LinearProgram& lp) {
1097 objective_limit_reached_ =
false;
1098 DCHECK(std::isfinite(objective_offset_));
1099 DCHECK(std::isfinite(objective_scaling_factor_));
1100 DCHECK_NE(0.0, objective_scaling_factor_);
1103 for (
const bool set_dual : {
true,
false}) {
1115 const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
1116 ? parameters_.objective_lower_limit()
1117 : parameters_.objective_upper_limit();
1119 limit / objective_scaling_factor_ - objective_offset_;
1121 dual_objective_limit_ = shifted_limit;
1123 primal_objective_limit_ = shifted_limit;
1133 Status RevisedSimplex::CreateInitialBasis() {
1145 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1146 basis[
row] = SlackColIndex(
row);
1153 if (!parameters_.use_dual_simplex() &&
1154 parameters_.initial_basis() != GlopParameters::MAROS &&
1155 parameters_.exploit_singleton_column_in_initial_basis()) {
1159 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1165 SetNonBasicVariableStatusAndDeriveValue(
col,
1169 SetNonBasicVariableStatusAndDeriveValue(
col,
1176 ComputeVariableValuesError();
1185 UseSingletonColumnInInitialBasis(&basis);
1188 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1190 basis[
row] = SlackColIndex(
row);
1196 if (parameters_.initial_basis() == GlopParameters::NONE) {
1197 return InitializeFirstBasis(basis);
1199 if (parameters_.initial_basis() == GlopParameters::MAROS) {
1200 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1202 if (parameters_.use_dual_simplex()) {
1205 initial_basis.GetDualMarosBasis(num_cols_, &basis);
1207 initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1209 int number_changed = 0;
1210 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1211 if (basis[
row] != SlackColIndex(
row)) {
1215 VLOG(1) <<
"Number of Maros basis changes: " << number_changed;
1216 }
else if (parameters_.initial_basis() == GlopParameters::BIXBY ||
1217 parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1219 int num_fixed_variables = 0;
1220 for (RowIndex
row(0);
row < basis.size(); ++
row) {
1221 const ColIndex
col = basis[
row];
1224 ++num_fixed_variables;
1228 if (num_fixed_variables == 0) {
1229 SOLVER_LOG(logger_,
"Crash is set to ", parameters_.initial_basis(),
1230 " but there is no equality rows to remove from initial all "
1231 "slack basis. Starting from there.");
1234 SOLVER_LOG(logger_,
"Trying to remove ", num_fixed_variables,
1235 " fixed variables from the initial basis.");
1236 InitialBasis initial_basis(compact_matrix_, objective_,
lower_bounds,
1239 if (parameters_.initial_basis() == GlopParameters::BIXBY) {
1240 if (parameters_.use_scaling()) {
1241 initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1243 VLOG(1) <<
"Bixby initial basis algorithm requires the problem "
1244 <<
"to be scaled. Skipping Bixby's algorithm.";
1246 }
else if (parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1249 if (parameters_.use_dual_simplex()) {
1252 initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1254 initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1257 const Status
status = InitializeFirstBasis(basis);
1263 "Advanced basis algo failed, Reverting to all slack basis.");
1265 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1266 basis[
row] = SlackColIndex(
row);
1272 LOG(WARNING) <<
"Unsupported initial_basis parameters: "
1273 << parameters_.initial_basis();
1276 return InitializeFirstBasis(basis);
1279 Status RevisedSimplex::InitializeFirstBasis(
const RowToColMapping& basis) {
1285 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1287 basis_[
row] = SlackColIndex(
row);
1301 if (condition_number_ub > parameters_.initial_condition_number_threshold()) {
1302 const std::string error_message =
1303 absl::StrCat(
"The matrix condition number upper bound is too high: ",
1304 condition_number_ub);
1310 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1313 DCHECK(BasisIsConsistent());
1322 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1326 "The primal residual of the initial basis is above the tolerance, ",
1333 Status RevisedSimplex::Initialize(
const LinearProgram& lp) {
1334 parameters_ = initial_parameters_;
1335 PropagateParameters();
1343 const bool lp_is_in_equation_form = lp.IsInEquationForm();
1350 ColIndex num_new_cols(0);
1351 bool only_change_is_new_rows =
false;
1352 bool only_change_is_new_cols =
false;
1353 bool matrix_is_unchanged =
true;
1354 bool only_new_bounds =
false;
1355 if (solution_state_.
IsEmpty() || !notify_that_matrix_is_unchanged_) {
1356 matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1357 lp, lp_is_in_equation_form, &only_change_is_new_rows,
1358 &only_change_is_new_cols, &num_new_cols);
1359 only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1360 OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1361 lp, lp_is_in_equation_form, num_new_cols);
1363 CHECK(InitializeMatrixAndTestIfUnchanged(
1364 lp, lp_is_in_equation_form, &only_change_is_new_rows,
1365 &only_change_is_new_cols, &num_new_cols));
1367 notify_that_matrix_is_unchanged_ =
false;
1370 const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1372 const bool bounds_are_unchanged =
1373 lp_is_in_equation_form
1375 lp.variable_lower_bounds(), lp.variable_upper_bounds())
1377 lp.variable_lower_bounds(), lp.variable_upper_bounds(),
1378 lp.constraint_lower_bounds(), lp.constraint_upper_bounds());
1383 if (matrix_is_unchanged && parameters_.allow_simplex_algorithm_change()) {
1384 if (objective_is_unchanged && !bounds_are_unchanged) {
1385 parameters_.set_use_dual_simplex(
true);
1386 PropagateParameters();
1388 if (bounds_are_unchanged && !objective_is_unchanged) {
1389 parameters_.set_use_dual_simplex(
false);
1390 PropagateParameters();
1394 InitializeObjectiveLimit(lp);
1410 bool solve_from_scratch =
true;
1413 if (!solution_state_.
IsEmpty() && !solution_state_has_been_set_externally_) {
1414 if (!parameters_.use_dual_simplex()) {
1419 dual_edge_norms_.
Clear();
1420 dual_pricing_vector_.
clear();
1421 if (matrix_is_unchanged && bounds_are_unchanged) {
1425 solve_from_scratch =
false;
1426 }
else if (only_change_is_new_cols && only_new_bounds) {
1430 variable_starting_values_);
1432 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1433 for (ColIndex& col_ref : basis_) {
1434 if (col_ref >= first_new_col) {
1435 col_ref += num_new_cols;
1442 primal_edge_norms_.
Clear();
1444 solve_from_scratch =
false;
1450 primal_edge_norms_.
Clear();
1451 if (objective_is_unchanged) {
1452 if (matrix_is_unchanged) {
1453 if (!bounds_are_unchanged) {
1455 first_slack_col_, ColIndex(0), solution_state_);
1457 variable_starting_values_);
1460 solve_from_scratch =
false;
1461 }
else if (only_change_is_new_rows) {
1465 first_slack_col_, ColIndex(0), solution_state_);
1472 dual_pricing_vector_.
clear();
1475 if (InitializeFirstBasis(basis_).ok()) {
1476 solve_from_scratch =
false;
1485 if (solve_from_scratch && !solution_state_.
IsEmpty()) {
1486 basis_factorization_.
Clear();
1488 primal_edge_norms_.
Clear();
1489 dual_edge_norms_.
Clear();
1490 dual_pricing_vector_.
clear();
1498 std::vector<ColIndex> candidates;
1500 candidates.push_back(
col);
1502 SOLVER_LOG(logger_,
"The warm-start state contains ", candidates.size(),
1503 " candidates for the basis (num_rows = ", num_rows_.value(),
1509 if (candidates.size() == num_rows_) {
1511 for (
const ColIndex
col : candidates) {
1512 basis_.push_back(
col);
1518 if (InitializeFirstBasis(basis_).ok()) {
1519 solve_from_scratch =
false;
1523 if (solve_from_scratch) {
1525 const int num_super_basic =
1528 parameters_.crossover_bound_snapping_distance(),
1529 variable_starting_values_);
1531 SOLVER_LOG(logger_,
"The initial basis did not use ",
1532 " BASIC columns from the initial state and used ",
1533 (num_rows_ - (candidates.size() - num_super_basic)).value(),
1534 " slack variables that were not marked BASIC.");
1535 if (num_snapped > 0) {
1537 " of the FREE variables where moved to their bound.");
1541 if (InitializeFirstBasis(basis_).ok()) {
1542 solve_from_scratch =
false;
1545 "RevisedSimplex is not using the warm start "
1546 "basis because it is not factorizable.");
1551 if (solve_from_scratch) {
1552 SOLVER_LOG(logger_,
"Starting basis: create from scratch.");
1553 basis_factorization_.
Clear();
1555 primal_edge_norms_.
Clear();
1556 dual_edge_norms_.
Clear();
1557 dual_pricing_vector_.
clear();
1560 SOLVER_LOG(logger_,
"Starting basis: incremental solve.");
1562 DCHECK(BasisIsConsistent());
1566 void RevisedSimplex::DisplayBasicVariableStatistics() {
1569 int num_fixed_variables = 0;
1570 int num_free_variables = 0;
1571 int num_variables_at_bound = 0;
1572 int num_slack_variables = 0;
1573 int num_infeasible_variables = 0;
1579 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1580 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1581 const ColIndex
col = basis_[
row];
1584 ++num_free_variables;
1588 ++num_infeasible_variables;
1590 if (
col >= first_slack_col_) {
1591 ++num_slack_variables;
1594 ++num_fixed_variables;
1597 ++num_variables_at_bound;
1601 SOLVER_LOG(logger_,
"The matrix with slacks has ",
1602 compact_matrix_.
num_rows().value(),
" rows, ",
1603 compact_matrix_.
num_cols().value(),
" columns, ",
1604 compact_matrix_.
num_entries().value(),
" entries.");
1605 SOLVER_LOG(logger_,
"Number of basic infeasible variables: ",
1606 num_infeasible_variables);
1607 SOLVER_LOG(logger_,
"Number of basic slack variables: ", num_slack_variables);
1609 "Number of basic variables at bound: ", num_variables_at_bound);
1610 SOLVER_LOG(logger_,
"Number of basic fixed variables: ", num_fixed_variables);
1611 SOLVER_LOG(logger_,
"Number of basic free variables: ", num_free_variables);
1612 SOLVER_LOG(logger_,
"Number of super-basic variables: ",
1613 ComputeNumberOfSuperBasicVariables());
1616 void RevisedSimplex::SaveState() {
1619 solution_state_has_been_set_externally_ =
false;
1622 RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1624 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1626 contains_data[e.row()] =
true;
1629 RowIndex num_empty_rows(0);
1630 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1631 if (!contains_data[
row]) {
1633 VLOG(2) <<
"Row " <<
row <<
" is empty.";
1636 return num_empty_rows;
1639 ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1640 ColIndex num_empty_cols(0);
1641 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1644 VLOG(2) <<
"Column " <<
col <<
" is empty.";
1647 return num_empty_cols;
1650 int RevisedSimplex::ComputeNumberOfSuperBasicVariables()
const {
1652 int num_super_basic = 0;
1653 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1655 variable_values_.
Get(
col) != 0.0) {
1659 return num_super_basic;
1662 void RevisedSimplex::CorrectErrorsOnVariableValues() {
1674 if (primal_residual >= parameters_.harris_tolerance_ratio() *
1675 parameters_.primal_feasibility_tolerance()) {
1677 VLOG(1) <<
"Primal infeasibility (bounds error) = "
1679 <<
", Primal residual |A.x - b| = "
1684 void RevisedSimplex::ComputeVariableValuesError() {
1688 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1694 void RevisedSimplex::ComputeDirection(ColIndex
col) {
1698 direction_infinity_norm_ = 0.0;
1701 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1705 direction_infinity_norm_ =
1710 for (
const auto e : direction_) {
1711 direction_infinity_norm_ =
1712 std::max(direction_infinity_norm_, std::abs(e.coefficient()));
1716 num_rows_ == 0 ? 0.0
1717 :
static_cast<double>(direction_.non_zeros.size()) /
1718 static_cast<double>(num_rows_.value())));
1721 Fractional RevisedSimplex::ComputeDirectionError(ColIndex
col) {
1724 for (
const auto e : direction_) {
1731 template <
bool is_entering_reduced_cost_positive>
1734 RowIndex
row)
const {
1735 const ColIndex
col = basis_[
row];
1739 DCHECK_NE(direction, 0.0);
1740 if (is_entering_reduced_cost_positive) {
1741 if (direction > 0.0) {
1747 if (direction > 0.0) {
1755 template <
bool is_entering_reduced_cost_positive>
1756 Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1757 Fractional bound_flip_ratio, SparseColumn* leaving_candidates)
const {
1760 parameters_.harris_tolerance_ratio() *
1761 parameters_.primal_feasibility_tolerance();
1762 const Fractional minimum_delta = parameters_.degenerate_ministep_factor() *
1763 parameters_.primal_feasibility_tolerance();
1769 leaving_candidates->Clear();
1776 ? parameters_.minimum_acceptable_pivot()
1777 : parameters_.ratio_test_zero_threshold();
1781 for (
const auto e : direction_) {
1782 const Fractional magnitude = std::abs(e.coefficient());
1783 if (magnitude <= threshold)
continue;
1786 if (
ratio <= harris_ratio) {
1787 leaving_candidates->SetCoefficient(e.row(),
ratio);
1799 harris_ratio =
std::min(harris_ratio,
1800 std::max(minimum_delta / magnitude,
1801 ratio + harris_tolerance / magnitude));
1804 return harris_ratio;
1817 if (current >= 0.0) {
1818 return candidate >= 0.0 && candidate <= current;
1820 return candidate >= current;
1828 Status RevisedSimplex::ChooseLeavingVariableRow(
1829 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1836 DCHECK_NE(0.0, reduced_cost);
1839 int stats_num_leaving_choices = 0;
1840 equivalent_leaving_choices_.clear();
1844 stats_num_leaving_choices = 0;
1848 const Fractional entering_value = variable_values_.
Get(entering_col);
1850 (reduced_cost > 0.0) ? entering_value -
lower_bounds[entering_col]
1852 DCHECK_GT(current_ratio, 0.0);
1858 (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1859 current_ratio, &leaving_candidates_)
1860 : ComputeHarrisRatioAndLeavingCandidates<false>(
1861 current_ratio, &leaving_candidates_);
1866 if (current_ratio <= harris_ratio) {
1868 *step_length = current_ratio;
1878 stats_num_leaving_choices = 0;
1880 equivalent_leaving_choices_.clear();
1883 if (
ratio > harris_ratio)
continue;
1884 ++stats_num_leaving_choices;
1885 const RowIndex
row = e.row();
1890 const Fractional candidate_magnitude = std::abs(direction_[
row]);
1891 if (candidate_magnitude < pivot_magnitude)
continue;
1892 if (candidate_magnitude == pivot_magnitude) {
1893 if (!IsRatioMoreOrEquallyStable(
ratio, current_ratio))
continue;
1894 if (
ratio == current_ratio) {
1896 equivalent_leaving_choices_.push_back(
row);
1900 equivalent_leaving_choices_.clear();
1901 current_ratio =
ratio;
1902 pivot_magnitude = candidate_magnitude;
1907 if (!equivalent_leaving_choices_.empty()) {
1908 equivalent_leaving_choices_.push_back(*leaving_row);
1910 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1911 0, equivalent_leaving_choices_.size() - 1)(random_)];
1923 if (current_ratio <= 0.0) {
1927 parameters_.degenerate_ministep_factor() *
1928 parameters_.primal_feasibility_tolerance();
1929 *step_length = minimum_delta / pivot_magnitude;
1931 *step_length = current_ratio;
1938 TestPivot(entering_col, *leaving_row);
1951 if (pivot_magnitude <
1952 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
1957 VLOG(1) <<
"Refactorizing to avoid pivoting by "
1958 << direction_[*leaving_row]
1959 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1960 <<
" reduced cost = " << reduced_cost;
1961 *refactorize =
true;
1971 VLOG(1) <<
"Couldn't avoid pivoting by " << direction_[*leaving_row]
1972 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1973 <<
" reduced cost = " << reduced_cost;
1974 DCHECK_GE(std::abs(direction_[*leaving_row]),
1975 parameters_.minimum_acceptable_pivot());
1983 const bool is_reduced_cost_positive = (reduced_cost > 0.0);
1984 const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
1985 *
target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
1992 ratio_test_stats_.leaving_choices.Add(stats_num_leaving_choices);
1993 if (!equivalent_leaving_choices_.empty()) {
1994 ratio_test_stats_.num_perfect_ties.Add(
1995 equivalent_leaving_choices_.size());
1998 ratio_test_stats_.abs_used_pivot.Add(std::abs(direction_[*leaving_row]));
2020 bool operator<(
const BreakPoint& other)
const {
2021 if (
ratio == other.ratio) {
2023 return row > other.row;
2027 return ratio > other.ratio;
2038 void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
2039 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
2040 RowIndex* leaving_row,
Fractional* step_length,
2047 DCHECK_NE(0.0, reduced_cost);
2053 const Fractional entering_value = variable_values_.
Get(entering_col);
2054 Fractional current_ratio = (reduced_cost > 0.0)
2057 DCHECK_GT(current_ratio, 0.0);
2059 std::vector<BreakPoint> breakpoints;
2060 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
2061 for (
const auto e : direction_) {
2063 reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
2064 const Fractional magnitude = std::abs(direction);
2065 if (magnitude < tolerance)
continue;
2080 const ColIndex
col = basis_[e.row()];
2091 if (to_lower >= 0.0 && to_lower < current_ratio) {
2092 breakpoints.push_back(
2093 BreakPoint(e.row(), to_lower, magnitude,
lower_bound));
2095 if (to_upper >= 0.0 && to_upper < current_ratio) {
2096 breakpoints.push_back(
2097 BreakPoint(e.row(), to_upper, magnitude,
upper_bound));
2103 std::make_heap(breakpoints.begin(), breakpoints.end());
2107 Fractional improvement = std::abs(reduced_cost);
2110 while (!breakpoints.empty()) {
2111 const BreakPoint top = breakpoints.front();
2119 if (top.coeff_magnitude > best_magnitude) {
2120 *leaving_row = top.row;
2121 current_ratio = top.ratio;
2122 best_magnitude = top.coeff_magnitude;
2128 improvement -= top.coeff_magnitude;
2129 if (improvement <= 0.0)
break;
2130 std::pop_heap(breakpoints.begin(), breakpoints.end());
2131 breakpoints.pop_back();
2137 parameters_.small_pivot_threshold() * direction_infinity_norm_;
2138 if (best_magnitude < threshold && !basis_factorization_.
IsRefactorized()) {
2139 *refactorize =
true;
2143 *step_length = current_ratio;
2147 Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
2156 if (dual_prices_.
Size() == 0) {
2158 parameters_.dual_price_prioritize_norm());
2168 const ColIndex leaving_col = basis_[*leaving_row];
2173 DCHECK_GT(*cost_variation, 0.0);
2177 DCHECK_LT(*cost_variation, 0.0);
2188 if (
cost == 0.0)
return false;
2198 template <
bool use_dense_update>
2199 void RevisedSimplex::OnDualPriceChange(
const DenseColumn& squared_norm,
2203 const bool is_candidate =
2204 IsDualPhaseILeavingCandidate(price, type, threshold);
2206 if (use_dense_update) {
2216 void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
2217 ColIndex entering_col) {
2228 dual_pricing_vector_.
empty()) {
2233 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2243 dual_pricing_vector_[leaving_row] / direction_[leaving_row];
2244 for (
const auto e : direction_) {
2245 dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2246 OnDualPriceChange(squared_norms, e.row(), variable_type[basis_[e.row()]],
2249 dual_pricing_vector_[leaving_row] = step;
2253 dual_pricing_vector_[leaving_row] -=
2254 dual_infeasibility_improvement_direction_[entering_col];
2255 if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2256 --num_dual_infeasible_positions_;
2258 dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2261 dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2264 OnDualPriceChange(squared_norms, leaving_row, variable_type[entering_col],
2268 template <
typename Cols>
2269 void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2272 bool something_to_do =
false;
2277 for (ColIndex
col : cols) {
2280 (can_increase.IsSet(
col) && reduced_cost < -tolerance) ? 1.0
2281 : (can_decrease.IsSet(
col) && reduced_cost > tolerance) ? -1.0
2283 if (sign != dual_infeasibility_improvement_direction_[
col]) {
2285 --num_dual_infeasible_positions_;
2286 }
else if (dual_infeasibility_improvement_direction_[
col] == 0.0) {
2287 ++num_dual_infeasible_positions_;
2289 if (!something_to_do) {
2290 initially_all_zero_scratchpad_.
values.
resize(num_rows_, 0.0);
2292 initially_all_zero_scratchpad_.
non_zeros.clear();
2293 something_to_do =
true;
2297 num_update_price_operations_ +=
2300 col, sign - dual_infeasibility_improvement_direction_[
col],
2301 &initially_all_zero_scratchpad_);
2302 dual_infeasibility_improvement_direction_[
col] = sign;
2305 if (something_to_do) {
2311 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2312 basis_factorization_.
RightSolve(&initially_all_zero_scratchpad_);
2313 if (initially_all_zero_scratchpad_.
non_zeros.empty()) {
2315 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2316 if (initially_all_zero_scratchpad_[
row] == 0.0)
continue;
2317 dual_pricing_vector_[
row] += initially_all_zero_scratchpad_[
row];
2318 OnDualPriceChange<
true>(
2319 squared_norms,
row, variable_type[basis_[
row]], threshold);
2323 for (
const auto e : initially_all_zero_scratchpad_) {
2324 dual_pricing_vector_[e.row()] += e.coefficient();
2325 OnDualPriceChange(squared_norms, e.row(),
2326 variable_type[basis_[e.row()]], threshold);
2327 initially_all_zero_scratchpad_[e.row()] = 0.0;
2330 initially_all_zero_scratchpad_.non_zeros.clear();
2334 Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2335 RowIndex* leaving_row,
Fractional* cost_variation,
2355 dual_pricing_vector_.
empty()) {
2357 num_dual_infeasible_positions_ = 0;
2360 dual_infeasibility_improvement_direction_.
AssignToZero(num_cols_);
2361 DualPhaseIUpdatePriceOnReducedCostChange(
2371 if (num_dual_infeasible_positions_ == 0)
return Status::OK();
2378 *cost_variation = dual_pricing_vector_[*leaving_row];
2379 const ColIndex leaving_col = basis_[*leaving_row];
2380 if (*cost_variation < 0.0) {
2389 template <
typename BoxedVariableCols>
2390 void RevisedSimplex::MakeBoxedVariableDualFeasible(
2391 const BoxedVariableCols& cols,
bool update_basic_values) {
2393 std::vector<ColIndex> changed_cols;
2410 for (
const ColIndex
col : cols) {
2422 changed_cols.push_back(
col);
2423 }
else if (reduced_cost < -threshold &&
2427 changed_cols.push_back(
col);
2431 if (!changed_cols.empty()) {
2432 iteration_stats_.num_dual_flips.Add(changed_cols.size());
2434 update_basic_values);
2438 Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2443 const ColIndex leaving_col = basis_[leaving_row];
2444 const Fractional leaving_variable_value = variable_values_.
Get(leaving_col);
2454 return unscaled_step / direction_[leaving_row];
2457 bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2458 VLOG(1) <<
"Test pivot.";
2460 const ColIndex leaving_col = basis_[leaving_row];
2461 basis_[leaving_row] = entering_col;
2465 CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2467 basis_[leaving_row] = leaving_col;
2474 void RevisedSimplex::PermuteBasis() {
2481 if (col_perm.empty())
return;
2487 if (!dual_pricing_vector_.
empty()) {
2491 &tmp_dual_pricing_vector_);
2503 Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2504 RowIndex leaving_row,
2521 pivot_from_update_row = update_row_.
GetCoefficient(entering_col);
2532 const ColIndex leaving_col = basis_[leaving_row];
2540 ratio_test_stats_.bound_shift.Add(variable_values_.
Get(leaving_col) -
2543 UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2546 const Fractional pivot_from_direction = direction_[leaving_row];
2548 std::abs(pivot_from_update_row - pivot_from_direction);
2549 if (diff > parameters_.refactorization_threshold() *
2550 (1.0 +
std::min(std::abs(pivot_from_update_row),
2551 std::abs(pivot_from_direction)))) {
2552 VLOG(1) <<
"Refactorizing: imprecise pivot " << pivot_from_direction
2553 <<
" diff = " << diff;
2556 if (basis_factorization_.
NumUpdates() < 10) {
2557 Fractional threshold = parameters_.lu_factorization_pivot_threshold();
2558 threshold =
std::min(threshold * 1.5, 0.9);
2559 VLOG(1) <<
"Increasing LU pivot threshold " << threshold;
2560 parameters_.set_lu_factorization_pivot_threshold(threshold);
2564 last_refactorization_reason_ = RefactorizationReason::IMPRECISE_PIVOT;
2568 basis_factorization_.
Update(entering_col, leaving_row, direction_));
2576 Status RevisedSimplex::RefactorizeBasisIfNeeded(
bool* refactorize) {
2583 *refactorize =
false;
2588 if (
col >= integrality_scale_.
size()) {
2589 integrality_scale_.
resize(
col + 1, 0.0);
2591 integrality_scale_[
col] = scale;
2596 Cleanup update_deterministic_time_on_return(
2603 std::vector<ColIndex> candidates;
2609 bool refactorize =
false;
2612 for (
int i = 0; i < 10; ++i) {
2615 if (num_pivots >= 5)
break;
2616 if (candidates.empty())
break;
2620 std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2621 const ColIndex entering_col = candidates[
index];
2623 candidates.pop_back();
2637 ComputeDirection(entering_col);
2639 RowIndex leaving_row;
2641 bool local_refactorize =
false;
2643 ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2646 if (local_refactorize)
continue;
2648 if (std::abs(step_length) <= 1e-6)
continue;
2649 if (leaving_row !=
kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2652 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2658 const auto get_diff = [
this](ColIndex
col,
Fractional old_value,
2660 if (
col >= integrality_scale_.
size() || integrality_scale_[
col] == 0.0) {
2664 return (std::abs(new_value * s - std::round(new_value * s)) -
2665 std::abs(old_value * s - std::round(old_value * s)));
2667 Fractional diff = get_diff(entering_col, variable_values_.
Get(entering_col),
2668 variable_values_.
Get(entering_col) + step);
2669 for (
const auto e : direction_) {
2670 const ColIndex
col = basis_[e.row()];
2672 const Fractional new_value = old_value - e.coefficient() * step;
2673 diff += get_diff(
col, old_value, new_value);
2677 if (diff > -1e-2)
continue;
2687 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2689 }
else if (step < 0.0) {
2690 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2697 const ColIndex leaving_col = basis_[leaving_row];
2708 entering_col, leaving_col, leaving_row, direction_, &update_row_);
2710 entering_col, leaving_row, direction_,
2719 const Fractional dir = -direction_[leaving_row] * step;
2720 const bool is_degenerate =
2724 if (!is_degenerate) {
2728 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2731 VLOG(1) <<
"Polish num_pivots: " << num_pivots <<
" gain:" << total_gain;
2750 Status RevisedSimplex::PrimalMinimize(TimeLimit*
time_limit) {
2752 Cleanup update_deterministic_time_on_return(
2754 num_consecutive_degenerate_iterations_ = 0;
2755 bool refactorize =
false;
2756 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
2762 if (phase_ == Phase::FEASIBILITY) {
2779 last_refactorization_reason_ = RefactorizationReason::RC;
2783 last_refactorization_reason_ = RefactorizationReason::NORM;
2789 CorrectErrorsOnVariableValues();
2790 DisplayIterationInfo(
true, last_refactorization_reason_);
2791 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
2793 if (phase_ == Phase::FEASIBILITY) {
2805 if (phase_ == Phase::OPTIMIZATION &&
2806 ComputeObjectiveValue() < primal_objective_limit_) {
2807 VLOG(1) <<
"Stopping the primal simplex because"
2808 <<
" the objective limit " << primal_objective_limit_
2809 <<
" has been reached.";
2811 objective_limit_reached_ =
true;
2814 }
else if (phase_ == Phase::FEASIBILITY) {
2827 if (phase_ == Phase::FEASIBILITY) {
2830 if (primal_infeasibility <
2831 parameters_.primal_feasibility_tolerance()) {
2834 VLOG(1) <<
"Infeasible problem! infeasibility = "
2835 << primal_infeasibility;
2844 VLOG(1) <<
"Optimal reached, double checking...";
2847 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
2854 ComputeDirection(entering_col);
2873 VLOG(1) <<
"Skipping col #" << entering_col
2874 <<
" whose reduced cost is no longer valid under precise reduced "
2885 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2891 RowIndex leaving_row;
2893 if (phase_ == Phase::FEASIBILITY) {
2894 PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2895 &refactorize, &leaving_row,
2899 ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2903 last_refactorization_reason_ = RefactorizationReason::SMALL_PIVOT;
2913 VLOG(1) <<
"Infinite step length, double checking...";
2916 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
2919 if (phase_ == Phase::FEASIBILITY) {
2921 VLOG(1) <<
"Unbounded feasibility problem !?";
2926 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2927 const ColIndex
col = basis_[
row];
2928 solution_primal_ray_[
col] = -direction_[
row];
2930 solution_primal_ray_[entering_col] = 1.0;
2931 if (reduced_cost > 0.0) {
2938 Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2939 if (phase_ == Phase::FEASIBILITY && leaving_row !=
kInvalidRow) {
2949 step = ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2953 const ColIndex leaving_col =
2959 bool is_degenerate =
false;
2961 Fractional dir = -direction_[leaving_row] * step;
2969 if (!is_degenerate) {
2970 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
2979 entering_col, basis_[leaving_row], leaving_row, direction_,
2982 direction_, &update_row_);
2984 if (!is_degenerate) {
2993 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2995 if (is_degenerate) {
2996 timer.AlsoUpdate(&iteration_stats_.degenerate);
2998 timer.AlsoUpdate(&iteration_stats_.normal);
3007 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3009 }
else if (step < 0.0) {
3010 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3017 if (phase_ == Phase::FEASIBILITY && leaving_row !=
kInvalidRow) {
3023 &objective_[leaving_col]);
3028 if (step_length == 0.0) {
3029 num_consecutive_degenerate_iterations_++;
3031 if (num_consecutive_degenerate_iterations_ > 0) {
3032 iteration_stats_.degenerate_run_size.Add(
3033 num_consecutive_degenerate_iterations_);
3034 num_consecutive_degenerate_iterations_ = 0;
3039 if (num_consecutive_degenerate_iterations_ > 0) {
3040 iteration_stats_.degenerate_run_size.Add(
3041 num_consecutive_degenerate_iterations_);
3057 Status RevisedSimplex::DualMinimize(
bool feasibility_phase,
3059 Cleanup update_deterministic_time_on_return(
3061 num_consecutive_degenerate_iterations_ = 0;
3062 bool refactorize =
false;
3063 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
3065 bound_flip_candidates_.clear();
3068 RowIndex leaving_row;
3073 ColIndex entering_col;
3084 const bool old_refactorize_value = refactorize;
3086 last_refactorization_reason_ = RefactorizationReason::RC;
3090 last_refactorization_reason_ = RefactorizationReason::NORM;
3111 if (feasibility_phase || old_refactorize_value) {
3123 if (!feasibility_phase) {
3124 MakeBoxedVariableDualFeasible(
3129 parameters_.dual_price_prioritize_norm());
3138 if (phase_ == Phase::OPTIMIZATION &&
3140 ComputeObjectiveValue() > dual_objective_limit_) {
3142 "Stopping the dual simplex because"
3143 " the objective limit ",
3144 dual_objective_limit_,
" has been reached.");
3146 objective_limit_reached_ =
true;
3151 DisplayIterationInfo(
false, last_refactorization_reason_);
3152 last_refactorization_reason_ = RefactorizationReason::DEFAULT;
3156 if (!feasibility_phase) {
3159 MakeBoxedVariableDualFeasible(bound_flip_candidates_,
3161 bound_flip_candidates_.clear();
3169 if (feasibility_phase) {
3181 VLOG(1) <<
"Optimal reached, double checking.";
3185 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
3188 if (feasibility_phase) {
3193 if (num_dual_infeasible_positions_ == 0) {
3196 VLOG(1) <<
"DUAL infeasible in dual phase I.";
3212 if (feasibility_phase) {
3213 const Fractional price = dual_pricing_vector_[leaving_row];
3217 Square(price) / squared_norms[leaving_row]);
3225 if (feasibility_phase) {
3232 &bound_flip_candidates_, &entering_col));
3238 VLOG(1) <<
"No entering column. Double checking...";
3241 last_refactorization_reason_ = RefactorizationReason::FINAL_CHECK;
3245 if (feasibility_phase) {
3247 VLOG(1) <<
"Unbounded dual feasibility problem !?";
3251 solution_dual_ray_ =
3254 &solution_dual_ray_row_combination_);
3255 if (cost_variation < 0) {
3257 ChangeSign(&solution_dual_ray_row_combination_);
3268 if (std::abs(entering_coeff) < parameters_.dual_small_pivot_threshold() &&
3270 VLOG(1) <<
"Trying not to pivot by " << entering_coeff;
3273 last_refactorization_reason_ = RefactorizationReason::SMALL_PIVOT;
3277 ComputeDirection(entering_col);
3283 if (std::abs(direction_[leaving_row]) <
3284 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
3286 VLOG(1) <<
"Trying not pivot by " << entering_coeff <<
" ("
3287 << direction_[leaving_row]
3288 <<
") because the direction has a norm of "
3289 << direction_infinity_norm_;
3292 last_refactorization_reason_ = RefactorizationReason::SMALL_PIVOT;
3302 if (num_iterations_ == parameters_.max_number_of_iterations() ||
3317 const bool increasing_rc_is_needed =
3318 (cost_variation > 0.0) == (entering_coeff > 0.0);
3324 timer.AlsoUpdate(&iteration_stats_.degenerate);
3326 timer.AlsoUpdate(&iteration_stats_.normal);
3338 entering_col, leaving_row, direction_,
3344 if (feasibility_phase) {
3345 DualPhaseIUpdatePrice(leaving_row, entering_col);
3348 ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
3353 const ColIndex leaving_col = basis_[leaving_row];
3355 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3368 Status RevisedSimplex::PrimalPush(TimeLimit*
time_limit) {
3370 Cleanup update_deterministic_time_on_return(
3372 bool refactorize =
false;
3376 primal_edge_norms_.
Clear();
3377 dual_edge_norms_.
Clear();
3381 std::vector<ColIndex> super_basic_cols;
3384 variable_values_.
Get(
col) != 0) {
3385 super_basic_cols.push_back(
col);
3389 while (!super_basic_cols.empty()) {
3397 CorrectErrorsOnVariableValues();
3398 DisplayIterationInfo(
true);
3402 ColIndex entering_col = super_basic_cols.back();
3416 const Fractional entering_value = variable_values_.
Get(entering_col);
3417 if (variables_info_.
GetTypeRow()[entering_col] ==
3419 if (entering_value > 0) {
3431 if (diff_lb <= diff_ub) {
3439 ComputeDirection(entering_col);
3442 RowIndex leaving_row;
3446 &refactorize, &leaving_row,
3449 if (refactorize)
continue;
3452 super_basic_cols.pop_back();
3455 if (variables_info_.
GetTypeRow()[entering_col] ==
3457 step_length = std::fabs(entering_value);
3459 VLOG(1) <<
"Infinite step for bounded variable ?!";
3465 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
3468 const ColIndex leaving_col =
3477 bool is_degenerate =
false;
3479 Fractional dir = -direction_[leaving_row] * step;
3487 if (!is_degenerate) {
3488 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
3495 if (!is_degenerate) {
3504 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3506 if (is_degenerate) {
3507 timer.AlsoUpdate(&iteration_stats_.degenerate);
3509 timer.AlsoUpdate(&iteration_stats_.normal);
3516 if (variables_info_.
GetTypeRow()[entering_col] ==
3518 variable_values_.
Set(entering_col, 0.0);
3519 }
else if (step > 0.0) {
3520 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3522 }
else if (step < 0.0) {
3523 SetNonBasicVariableStatusAndDeriveValue(entering_col,
3532 if (!super_basic_cols.empty()) {
3533 SOLVER_LOG(logger_,
"Push terminated early with ", super_basic_cols.size(),
3534 " super-basic variables remaining.");
3544 ColIndex RevisedSimplex::SlackColIndex(RowIndex
row)
const {
3551 result.append(iteration_stats_.StatString());
3552 result.append(ratio_test_stats_.StatString());
3553 result.append(entering_variable_.
StatString());
3556 result.append(variable_values_.
StatString());
3557 result.append(primal_edge_norms_.
StatString());
3558 result.append(dual_edge_norms_.
StatString());
3560 result.append(basis_factorization_.
StatString());
3565 void RevisedSimplex::DisplayAllStats() {
3566 if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3568 absl::FPrintF(stderr,
"%s", GetPrettySolverStats());
3572 Fractional RevisedSimplex::ComputeObjectiveValue()
const {
3578 Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue()
const {
3582 return objective_scaling_factor_ * (sum + objective_offset_);
3587 deterministic_random_.seed(
parameters.random_seed());
3591 PropagateParameters();
3594 void RevisedSimplex::PropagateParameters() {
3604 void RevisedSimplex::DisplayIterationInfo(
bool primal,
3605 RefactorizationReason reason) {
3607 const std::string first_word = primal ?
"Primal " :
"Dual ";
3614 if (reason != RefactorizationReason::DEFAULT) {
3616 case RefactorizationReason::DEFAULT:
3617 info =
" [default]";
3619 case RefactorizationReason::SMALL_PIVOT:
3620 info =
" [small pivot]";
3622 case RefactorizationReason::IMPRECISE_PIVOT:
3623 info =
" [imprecise pivot]";
3625 case RefactorizationReason::NORM:
3628 case RefactorizationReason::RC:
3629 info =
" [reduced costs]";
3631 case RefactorizationReason::VAR_VALUES:
3632 info =
" [var values]";
3634 case RefactorizationReason::FINAL_CHECK:
3641 case Phase::FEASIBILITY: {
3642 const int64_t iter = num_iterations_;
3645 if (parameters_.use_dual_simplex()) {
3646 if (parameters_.use_dedicated_dual_feasibility_algorithm()) {
3654 name =
"sum_dual_infeasibilities";
3657 name =
"sum_primal_infeasibilities";
3660 SOLVER_LOG(logger_, first_word,
"feasibility phase, iteration # ", iter,
3661 ", ",
name,
" = ", absl::StrFormat(
"%.15E", objective), info);
3664 case Phase::OPTIMIZATION: {
3665 const int64_t iter = num_iterations_ - num_feasibility_iterations_;
3671 const Fractional objective = ComputeInitialProblemObjectiveValue();
3672 SOLVER_LOG(logger_, first_word,
"optimization phase, iteration # ", iter,
3673 ", objective = ", absl::StrFormat(
"%.15E", objective), info);
3677 const int64_t iter = num_iterations_ - num_feasibility_iterations_ -
3678 num_optimization_iterations_;
3679 SOLVER_LOG(logger_, first_word,
"push phase, iteration # ", iter,
3680 ", remaining_variables_to_push = ",
3681 ComputeNumberOfSuperBasicVariables(), info);
3686 void RevisedSimplex::DisplayErrors() {
3690 SOLVER_LOG(logger_,
"Primal infeasibility (bounds) = ",
3692 SOLVER_LOG(logger_,
"Primal residual |A.x - b| = ",
3694 SOLVER_LOG(logger_,
"Dual infeasibility (reduced costs) = ",
3696 SOLVER_LOG(logger_,
"Dual residual |c_B - y.B| = ",
3702 std::string StringifyMonomialWithFlags(
const Fractional a,
3703 const std::string& x) {
3705 a, x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3711 std::string StringifyWithFlags(
const Fractional x) {
3713 absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3718 std::string RevisedSimplex::SimpleVariableInfo(ColIndex
col)
const {
3724 absl::StrAppendFormat(&output,
"%d (%s) = %s, %s, %s, [%s,%s]",
col.value(),
3725 variable_name_[
col],
3726 StringifyWithFlags(variable_values_.
Get(
col)),
3734 void RevisedSimplex::DisplayInfoOnVariables()
const {
3736 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3740 objective_coefficient * variable_value;
3741 VLOG(3) << SimpleVariableInfo(
col) <<
". " << variable_name_[
col] <<
" = "
3742 << StringifyWithFlags(variable_value) <<
" * "
3743 << StringifyWithFlags(objective_coefficient)
3744 <<
"(obj) = " << StringifyWithFlags(objective_contribution);
3746 VLOG(3) <<
"------";
3750 void RevisedSimplex::DisplayVariableBounds() {
3755 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3756 switch (variable_type[
col]) {
3760 VLOG(3) << variable_name_[
col]
3764 VLOG(3) << variable_name_[
col]
3769 <<
" <= " << variable_name_[
col]
3773 VLOG(3) << variable_name_[
col] <<
" = "
3777 LOG(DFATAL) <<
"Column " <<
col <<
" has no meaningful status.";
3787 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3788 ComputeDirection(
col);
3789 for (
const auto e : direction_) {
3790 if (column_scales ==
nullptr) {
3791 dictionary[e.row()].SetCoefficient(
col, e.coefficient());
3795 col < column_scales->
size() ? (*column_scales)[
col] : 1.0;
3797 ? (*column_scales)[
GetBasis(e.row())]
3799 dictionary[e.row()].SetCoefficient(
3800 col, direction_[e.row()] * (numerator / denominator));
3812 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3816 void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3819 DisplayInfoOnVariables();
3821 std::string output =
"z = " + StringifyWithFlags(ComputeObjectiveValue());
3824 absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[
col],
3825 variable_name_[
col]));
3827 VLOG(3) << output <<
";";
3829 const RevisedSimplexDictionary dictionary(
nullptr,
this);
3831 for (
const SparseRow&
row : dictionary) {
3833 ColIndex basic_col = basis_[r];
3834 absl::StrAppend(&output, variable_name_[basic_col],
" = ",
3835 StringifyWithFlags(variable_values_.
Get(basic_col)));
3836 for (
const SparseRowEntry e :
row) {
3837 if (e.col() != basic_col) {
3838 absl::StrAppend(&output,
3839 StringifyMonomialWithFlags(e.coefficient(),
3840 variable_name_[e.col()]));
3843 VLOG(3) << output <<
";";
3845 VLOG(3) <<
"------";
3846 DisplayVariableBounds();
3851 void RevisedSimplex::DisplayProblem()
const {
3855 DisplayInfoOnVariables();
3856 std::string output =
"min: ";
3857 bool has_objective =
false;
3858 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3860 has_objective |= (coeff != 0.0);
3861 absl::StrAppend(&output,
3862 StringifyMonomialWithFlags(coeff, variable_name_[
col]));
3864 if (!has_objective) {
3865 absl::StrAppend(&output,
" 0");
3867 VLOG(3) << output <<
";";
3868 for (RowIndex
row(0);
row < num_rows_; ++
row) {
3870 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3871 absl::StrAppend(&output,
3872 StringifyMonomialWithFlags(
3874 variable_name_[
col]));
3876 VLOG(3) << output <<
" = 0;";
3878 VLOG(3) <<
"------";
3882 void RevisedSimplex::AdvanceDeterministicTime(TimeLimit*
time_limit) {
3885 const double deterministic_time_delta =
3886 current_deterministic_time - last_deterministic_time_update_;
3887 time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3888 last_deterministic_time_update_ = current_deterministic_time;
3891 #undef DCHECK_COL_BOUNDS
3892 #undef DCHECK_ROW_BOUNDS
void push_back(const value_type &x)
bool IsSet(IndexType i) const
void SetLogToStdOut(bool enable)
bool LoggingIsEnabled() const
void EnableLogging(bool enable)
std::string StatString() const
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Fractional ComputeInfinityNormConditionNumberUpperBound() const
ABSL_MUST_USE_RESULT Status Refactorize()
const ColumnPermutation & GetColumnPermutation() const
ABSL_MUST_USE_RESULT Status Initialize()
bool IsRefactorized() const
ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
RowToColMapping ComputeInitialBasis(const std::vector< ColIndex > &candidates)
void RightSolveForProblemColumn(ColIndex col, ScatteredColumn *d) const
void SetColumnPermutationToIdentity()
void SetParameters(const GlopParameters ¶meters)
void RightSolve(ScatteredColumn *d) const
double DeterministicTime() const
ABSL_MUST_USE_RESULT Status ForceRefactorization()
std::string StatString() const
Fractional LookUpCoefficient(RowIndex index) const
Fractional EntryCoefficient(EntryIndex i) const
Fractional GetFirstCoefficient() const
RowIndex EntryRow(EntryIndex i) const
EntryIndex num_entries() const
void ColumnCopyToDenseColumn(ColIndex col, DenseColumn *dense_column) const
ColIndex num_cols() const
void ColumnAddMultipleToSparseScatteredColumn(ColIndex col, Fractional multiplier, ScatteredColumn *column) const
RowIndex num_rows() const
void PopulateFromTranspose(const CompactSparseMatrix &input)
Fractional ColumnScalarProduct(ColIndex col, const DenseRow &vector) const
void Reset(RowIndex num_rows)
void PopulateFromSparseMatrixAndAddSlacks(const SparseMatrix &input)
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn *dense_column) const
void PopulateFromMatrixView(const MatrixView &input)
EntryIndex num_entries() const
ColumnView column(ColIndex col) const
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
const DenseColumn & GetEdgeSquaredNorms()
bool TestPrecision(RowIndex leaving_row, const ScatteredRow &unit_row_left_inverse)
void ResizeOnNewRows(RowIndex new_size)
void SetParameters(const GlopParameters ¶meters)
bool NeedsBasisRefactorization() const
std::string StatString() const
void AddOrUpdate(Index position, Fractional value)
void Remove(Index position)
void DenseAddOrUpdate(Index position, Fractional value)
void ClearAndResize(Index n)
std::string StatString() const
ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn(bool nothing_to_recompute, const UpdateRow &update_row, Fractional cost_variation, ColIndex *entering_col)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
std::string StatString() const
ABSL_MUST_USE_RESULT Status DualChooseEnteringColumn(bool nothing_to_recompute, const UpdateRow &update_row, Fractional cost_variation, std::vector< ColIndex > *bound_flip_candidates, ColIndex *entering_col)
bool IsMaximizationProblem() const
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
bool TestEnteringEdgeNormPrecision(ColIndex entering_col, const ScatteredColumn &direction)
void UpdateBeforeBasisPivot(ColIndex entering_col, ColIndex leaving_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
void SetPricingRule(GlopParameters::PricingRule rule)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
bool NeedsBasisRefactorization() const
std::string StatString() const
void ForceRecomputation()
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
void UpdateBeforeBasisPivot(ColIndex entering_col, UpdateRow *update_row)
void RecomputePriceAt(ColIndex col)
ColIndex GetBestEnteringColumn()
void ResetForNewObjective()
Fractional TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction)
void MakeReducedCostsPrecise()
bool AreReducedCostsRecomputed()
bool AreReducedCostsPrecise()
bool IsValidPrimalEnteringCandidate(ColIndex col) const
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
bool HasCostShift() const
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables()
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)
Fractional ComputeSumOfDualInfeasibilities()
const DenseRow & GetFullReducedCosts()
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
const DenseRow & GetReducedCosts()
Fractional GetDualFeasibilityTolerance() const
const DenseColumn & GetDualValues()
void ClearAndRemoveCostShifts()
Fractional ComputeMaximumDualResidual()
void UpdateDataOnBasisPermutation()
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
Fractional ComputeMaximumDualInfeasibility()
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
bool NeedsBasisRefactorization() const
std::string StatString() const
const DenseRow & GetDualRayRowCombination() const
Fractional GetVariableValue(ColIndex col) const
void SetIntegralityScale(ColIndex col, Fractional scale)
const DenseRow & GetReducedCosts() const
const DenseRow & GetPrimalRay() const
Fractional GetConstraintActivity(RowIndex row) const
VariableStatus GetVariableStatus(ColIndex col) const
Fractional GetReducedCost(ColIndex col) const
const DenseColumn & GetDualRay() const
void NotifyThatMatrixIsChangedForNextSolve()
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
Fractional GetObjectiveValue() const
RowMajorSparseMatrix ComputeDictionary(const DenseRow *column_scales)
Fractional GetDualValue(RowIndex row) const
void NotifyThatMatrixIsUnchangedForNextSolve()
void SetStartingVariableValuesForNextSolve(const DenseRow &values)
ConstraintStatus GetConstraintStatus(RowIndex row) const
void ComputeBasicVariablesForState(const LinearProgram &linear_program, const BasisState &state)
ColIndex GetProblemNumCols() const
void LoadStateForNextSolve(const BasisState &state)
RowIndex GetProblemNumRows() const
void ClearStateForNextSolve()
const BasisFactorization & GetBasisFactorization() const
int64_t GetNumberOfIterations() const
const BasisState & GetState() const
ColIndex GetBasis(RowIndex row) const
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
typename Iterator::Entry Entry
void AssignToZero(IntType size)
void resize(IntType size)
const ScatteredRow & GetUnitRowLeftInverse() const
void ComputeUnitRowLeftInverse(RowIndex leaving_row)
const bool IsComputedFor(RowIndex leaving_row) const
void ComputeFullUpdateRow(RowIndex leaving_row, DenseRow *output) const
const Fractional GetCoefficient(ColIndex col) const
void ComputeUpdateRow(RowIndex leaving_row)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
const ColIndexVector & GetNonZeroPositions() const
std::string StatString() const
void UpdateDualPrices(absl::Span< const RowIndex > row)
void Set(ColIndex col, Fractional value)
void SetNonBasicVariableValueFromStatus(ColIndex col)
Fractional ComputeMaximumPrimalInfeasibility() const
Fractional ComputeSumOfPrimalInfeasibilities() const
void UpdateGivenNonBasicVariables(const std::vector< ColIndex > &cols_to_update, bool update_basic_variables)
void ResetAllNonBasicVariableValues(const DenseRow &free_initial_values)
const DenseRow & GetDenseRow() const
void RecomputeDualPrices(bool put_more_importance_on_norm=false)
void UpdateOnPivoting(const ScatteredColumn &direction, ColIndex entering_col, Fractional step)
const Fractional Get(ColIndex col) const
void RecomputeBasicVariableValues()
Fractional ComputeMaximumPrimalResidual() const
bool UpdatePrimalPhaseICosts(const Rows &rows, DenseRow *objective)
std::string StatString() const
const DenseBitRow & GetIsBasicBitRow() const
int SnapFreeVariablesToBound(Fractional distance, const DenseRow &starting_values)
const DenseRow & GetVariableUpperBounds() const
int ChangeUnusedBasicVariablesToFree(const RowToColMapping &basis)
const DenseBitRow & GetNonBasicBoxedVariables() const
Fractional GetBoundDifference(ColIndex col) const
const DenseBitRow & GetCanIncreaseBitRow() const
const DenseBitRow & GetCanDecreaseBitRow() const
const VariableTypeRow & GetTypeRow() const
void EndDualPhaseI(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
void MakeBoxedVariableRelevant(bool value)
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)
const DenseRow & GetVariableLowerBounds() const
const DenseBitRow & GetNotBasicBitRow() const
void InitializeToDefaultStatus()
const VariableStatusRow & GetStatusRow() const
void UpdateToBasicStatus(ColIndex col)
const DenseBitRow & GetIsRelevantBitRow() const
void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols, const BasisState &state)
bool LoadBoundsAndReturnTrueIfUnchanged(const DenseRow &new_lower_bounds, const DenseRow &new_upper_bounds)
void TransformToDualPhaseIProblem(Fractional dual_feasibility_tolerance, const DenseRow &reduced_costs)
ModelSharedTimeLimit * time_limit
constexpr ColIndex kInvalidCol(-1)
std::string StringifyMonomial(const Fractional a, const std::string &x, bool fraction)
Fractional Square(Fractional f)
Fractional InfinityNorm(const DenseColumn &v)
std::string Stringify(const Fractional x, bool fraction)
StrictITIVector< ColIndex, VariableType > VariableTypeRow
@ UPPER_AND_LOWER_BOUNDED
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
StrictITIVector< ColIndex, Fractional > DenseRow
std::string GetProblemStatusString(ProblemStatus problem_status)
Index ColToIntIndex(ColIndex col)
constexpr double kInfinity
Permutation< ColIndex > ColumnPermutation
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
ColIndex RowToColIndex(RowIndex row)
bool IsFinite(Fractional value)
bool AreFirstColumnsAndRowsExactlyEquals(RowIndex num_rows, ColIndex num_cols, const SparseMatrix &matrix_a, const CompactSparseMatrix &matrix_b)
constexpr RowIndex kInvalidRow(-1)
const DenseRow & Transpose(const DenseColumn &col)
Bitset64< ColIndex > DenseBitRow
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
constexpr const uint64_t kDeterministicSeed
StrictITIVector< RowIndex, ColIndex > RowToColMapping
std::string GetVariableTypeString(VariableType variable_type)
void ApplyColumnPermutationToRowIndexedVector(const Permutation< ColIndex > &col_perm, RowIndexedVector *v)
StrictITIVector< RowIndex, Fractional > DenseColumn
StrictITIVector< RowIndex, bool > DenseBooleanColumn
static double DeterministicTimeForFpOperations(int64_t n)
std::string GetVariableStatusString(VariableStatus status)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
DisabledScopedTimeDistributionUpdater ScopedTimeDistributionUpdater
#define RETURN_IF_NULL(x)
Fractional coeff_magnitude
#define DCHECK_ROW_BOUNDS(row)
ABSL_FLAG(bool, simplex_display_numbers_as_fractions, false, "Display numbers as fractions.")
#define DCHECK_COL_BOUNDS(col)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
#define GLOP_RETURN_IF_ERROR(function_call)
#define GLOP_RETURN_ERROR_IF_NULL(arg)
VariableStatusRow statuses
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation)
std::vector< Index > non_zeros
StrictITIVector< Index, Fractional > values
#define SOLVER_LOG(logger,...)
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)