24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/str_format.h"
39 ABSL_FLAG(
bool, simplex_display_numbers_as_fractions,
false,
40 "Display numbers as fractions.");
41 ABSL_FLAG(
bool, simplex_stop_after_first_basis,
false,
42 "Stop after first basis has been computed.");
43 ABSL_FLAG(
bool, simplex_stop_after_feasibility,
false,
44 "Stop after first phase has been completed.");
45 ABSL_FLAG(
bool, simplex_display_stats,
false,
"Display algorithm statistics.");
55 explicit Cleanup(std::function<
void()> closure)
56 : closure_(std::move(closure)) {}
57 ~Cleanup() { closure_(); }
60 std::function<void()> closure_;
64 #define DCHECK_COL_BOUNDS(col) \
67 DCHECK_GT(num_cols_, col); \
70 #define DCHECK_ROW_BOUNDS(row) \
73 DCHECK_GT(num_rows_, row); \
90 basis_factorization_(&compact_matrix_, &basis_),
91 variables_info_(compact_matrix_, lower_bound_, upper_bound_),
92 variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
93 basis_factorization_),
94 dual_edge_norms_(basis_factorization_),
95 primal_edge_norms_(compact_matrix_, variables_info_,
96 basis_factorization_),
97 update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
98 basis_factorization_),
99 reduced_costs_(compact_matrix_,
objective_, basis_, variables_info_,
100 basis_factorization_, &random_),
101 entering_variable_(variables_info_, &random_, &reduced_costs_,
102 &primal_edge_norms_),
104 num_feasibility_iterations_(0),
105 num_optimization_iterations_(0),
107 feasibility_time_(0.0),
108 optimization_time_(0.0),
109 last_deterministic_time_update_(0.0),
112 function_stats_(
"SimplexFunctionStats"),
115 feasibility_phase_(true),
127 solution_state_ = state;
128 solution_state_has_been_set_externally_ =
true;
132 notify_that_matrix_is_unchanged_ =
true;
141 "The problem is not in the equations form.");
143 Cleanup update_deterministic_time_on_return(
148 const double start_time =
time_limit->GetElapsedTime();
151 dual_infeasibility_improvement_direction_.
clear();
155 feasibility_phase_ =
true;
157 num_feasibility_iterations_ = 0;
158 num_optimization_iterations_ = 0;
159 feasibility_time_ = 0.0;
160 optimization_time_ = 0.0;
167 solution_state_has_been_set_externally_ =
true;
170 ComputeNumberOfEmptyRows();
171 ComputeNumberOfEmptyColumns();
172 DisplayBasicVariableStatistics();
175 if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
180 const bool use_dual = parameters_.use_dual_simplex();
181 const bool log_info = parameters_.log_search_progress() ||
VLOG_IS_ON(1);
183 LOG(
INFO) <<
"------ " << (use_dual ?
"Dual simplex." :
"Primal simplex.");
184 LOG(
INFO) <<
"The matrix has " << compact_matrix_.
num_rows() <<
" rows, "
185 << compact_matrix_.
num_cols() <<
" columns, "
191 if (log_info)
LOG(
INFO) <<
"------ First phase: feasibility.";
192 entering_variable_.
SetPricingRule(parameters_.feasibility_rule());
194 if (parameters_.perturb_costs_in_dual_simplex()) {
200 DisplayIterationInfo();
225 DisplayIterationInfo();
229 InitializeObjectiveAndTestIfUnchanged(lp);
240 feasibility_phase_ =
false;
241 feasibility_time_ =
time_limit->GetElapsedTime() - start_time;
242 entering_variable_.
SetPricingRule(parameters_.optimization_rule());
243 num_feasibility_iterations_ = num_iterations_;
245 if (log_info)
LOG(
INFO) <<
"------ Second phase: optimization.";
259 for (
int num_optims = 0;
263 num_optims <= parameters_.max_number_of_reoptimizations() &&
264 !objective_limit_reached_ &&
265 (num_iterations_ == 0 ||
266 num_iterations_ < parameters_.max_number_of_iterations()) &&
268 !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
290 if (!integrality_scale_.
empty() &&
310 DisplayIterationInfo();
319 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
324 LOG(
INFO) <<
"DUAL_UNBOUNDED was reported, but the residual and/or "
325 <<
"dual infeasibility is above the tolerance";
335 parameters_.solution_feasibility_tolerance();
337 solution_tolerance ||
340 LOG(
INFO) <<
"OPTIMAL was reported, yet one of the residuals is "
341 "above the solution feasibility tolerance after the "
342 "shift/perturbation are removed.";
344 if (parameters_.change_status_to_imprecise()) {
351 parameters_.primal_feasibility_tolerance();
353 parameters_.dual_feasibility_tolerance();
358 if (primal_infeasibility > primal_tolerance &&
359 dual_infeasibility > dual_tolerance) {
361 LOG(
INFO) <<
"OPTIMAL was reported, yet both of the infeasibility "
362 "are above the tolerance after the "
363 "shift/perturbation are removed.";
365 if (parameters_.change_status_to_imprecise()) {
368 }
else if (primal_infeasibility > primal_tolerance) {
369 if (log_info)
LOG(
INFO) <<
"Re-optimizing with dual simplex ... ";
371 }
else if (dual_infeasibility > dual_tolerance) {
372 if (log_info)
LOG(
INFO) <<
"Re-optimizing with primal simplex ... ";
383 if (parameters_.change_status_to_imprecise() &&
385 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
406 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
417 solution_objective_value_ =
421 solution_objective_value_ = -solution_objective_value_;
425 total_time_ =
time_limit->GetElapsedTime() - start_time;
426 optimization_time_ = total_time_ - feasibility_time_;
427 num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
434 return problem_status_;
438 return solution_objective_value_;
448 return variable_values_.
Get(
col);
452 return solution_reduced_costs_[
col];
456 return solution_reduced_costs_;
460 return solution_dual_values_[
row];
472 return -variable_values_.
Get(SlackColIndex(
row));
490 return solution_primal_ray_;
494 return solution_dual_ray_;
499 return solution_dual_ray_row_combination_;
506 return basis_factorization_;
509 std::string RevisedSimplex::GetPrettySolverStats()
const {
510 return absl::StrFormat(
511 "Problem status : %s\n"
512 "Solving time : %-6.4g\n"
513 "Number of iterations : %u\n"
514 "Time for solvability (first phase) : %-6.4g\n"
515 "Number of iterations for solvability : %u\n"
516 "Time for optimization : %-6.4g\n"
517 "Number of iterations for optimization : %u\n"
518 "Stop after first basis : %d\n",
520 feasibility_time_, num_feasibility_iterations_, optimization_time_,
521 num_optimization_iterations_,
522 absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
533 void RevisedSimplex::SetVariableNames() {
534 variable_name_.
resize(num_cols_,
"");
535 for (ColIndex
col(0);
col < first_slack_col_; ++
col) {
536 const ColIndex var_index =
col + 1;
539 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
540 const ColIndex var_index =
col - first_slack_col_ + 1;
546 ColIndex
col)
const {
548 if (lower_bound_[
col] == upper_bound_[
col]) {
558 return std::abs(lower_bound_[
col]) <= std::abs(upper_bound_[
col])
563 void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
569 bool RevisedSimplex::BasisIsConsistent()
const {
572 for (RowIndex
row(0);
row < num_rows_; ++
row) {
573 const ColIndex
col = basis_[
row];
574 if (!is_basic.IsSet(
col))
return false;
577 ColIndex cols_in_basis(0);
578 ColIndex cols_not_in_basis(0);
579 for (ColIndex
col(0);
col < num_cols_; ++
col) {
580 cols_in_basis += is_basic.IsSet(
col);
581 cols_not_in_basis += !is_basic.IsSet(
col);
582 if (is_basic.IsSet(
col) !=
588 if (cols_not_in_basis != num_cols_ -
RowToColIndex(num_rows_))
return false;
594 void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
603 DCHECK_NE(basis_[basis_row], entering_col);
606 const ColIndex leaving_col = basis_[basis_row];
612 variables_info_.
Update(leaving_col, leaving_variable_status);
617 basis_[basis_row] = entering_col;
625 class ColumnComparator {
628 bool operator()(ColIndex col_a, ColIndex col_b)
const {
629 return value_[col_a] < value_[col_b];
646 void RevisedSimplex::UseSingletonColumnInInitialBasis(
RowToColMapping* basis) {
653 std::vector<ColIndex> singleton_column;
654 DenseRow cost_variation(num_cols_, 0.0);
655 for (ColIndex
col(0);
col < num_cols_; ++
col) {
657 if (lower_bound_[
col] == upper_bound_[
col])
continue;
659 if (variable_values_.
Get(
col) == lower_bound_[
col]) {
660 cost_variation[
col] = objective_[
col] / std::abs(slope);
662 cost_variation[
col] = -objective_[
col] / std::abs(slope);
664 singleton_column.push_back(
col);
666 if (singleton_column.empty())
return;
673 ColumnComparator comparator(cost_variation);
674 std::sort(singleton_column.begin(), singleton_column.end(), comparator);
675 DCHECK_LE(cost_variation[singleton_column.front()],
676 cost_variation[singleton_column.back()]);
684 for (
const ColIndex
col : singleton_column) {
696 if (error_[
row] == 0.0)
continue;
704 if (new_value >= lower_bound_[
col] && new_value <= upper_bound_[
col]) {
719 if (variable_values[
col] == lower_bound_[
col] && error_sign > 0.0) {
721 error_[
row] -= coeff * box_width;
722 SetNonBasicVariableStatusAndDeriveValue(
col,
726 if (variable_values[
col] == upper_bound_[
col] && error_sign < 0.0) {
728 error_[
row] += coeff * box_width;
729 SetNonBasicVariableStatusAndDeriveValue(
col,
736 bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
737 const LinearProgram& lp,
bool* only_change_is_new_rows,
738 bool* only_change_is_new_cols, ColIndex* num_new_cols) {
740 DCHECK(only_change_is_new_rows !=
nullptr);
741 DCHECK(only_change_is_new_cols !=
nullptr);
742 DCHECK(num_new_cols !=
nullptr);
748 lp.GetFirstSlackVariable() +
RowToColIndex(lp.num_constraints()));
750 const bool old_part_of_matrix_is_unchanged =
752 num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
757 if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
758 lp.num_variables() == num_cols_) {
764 *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
765 lp.num_constraints() > num_rows_ &&
766 lp.GetFirstSlackVariable() == first_slack_col_;
770 *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
771 lp.num_constraints() == num_rows_ &&
772 lp.GetFirstSlackVariable() > first_slack_col_;
774 *only_change_is_new_cols ? lp.num_variables() - num_cols_ : ColIndex(0);
777 first_slack_col_ = lp.GetFirstSlackVariable();
780 num_rows_ = lp.num_constraints();
781 num_cols_ = lp.num_variables();
788 if (parameters_.use_transposed_matrix()) {
794 bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
795 const LinearProgram& lp, ColIndex num_new_cols) {
797 DCHECK_EQ(lp.num_variables(), num_cols_);
798 DCHECK_LE(num_new_cols, first_slack_col_);
799 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
802 for (ColIndex
col(0);
col < first_new_col; ++
col) {
803 if (lower_bound_[
col] != lp.variable_lower_bounds()[
col] ||
804 upper_bound_[
col] != lp.variable_upper_bounds()[
col]) {
809 for (ColIndex
col(first_new_col);
col < first_slack_col_; ++
col) {
810 if (lp.variable_lower_bounds()[
col] != 0.0 &&
811 lp.variable_upper_bounds()[
col] != 0.0) {
816 for (ColIndex
col(first_slack_col_);
col < num_cols_; ++
col) {
817 if (lower_bound_[
col - num_new_cols] != lp.variable_lower_bounds()[
col] ||
818 upper_bound_[
col - num_new_cols] != lp.variable_upper_bounds()[
col]) {
825 bool RevisedSimplex::InitializeBoundsAndTestIfUnchanged(
826 const LinearProgram& lp) {
828 lower_bound_.
resize(num_cols_, 0.0);
829 upper_bound_.
resize(num_cols_, 0.0);
833 bool bounds_are_unchanged =
true;
834 DCHECK_EQ(lp.num_variables(), num_cols_);
835 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
836 if (lower_bound_[
col] != lp.variable_lower_bounds()[
col] ||
837 upper_bound_[
col] != lp.variable_upper_bounds()[
col]) {
838 bounds_are_unchanged =
false;
842 if (!bounds_are_unchanged) {
843 lower_bound_ = lp.variable_lower_bounds();
844 upper_bound_ = lp.variable_upper_bounds();
846 return bounds_are_unchanged;
849 bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
850 const LinearProgram& lp) {
853 bool objective_is_unchanged =
true;
854 objective_.
resize(num_cols_, 0.0);
855 DCHECK_EQ(num_cols_, lp.num_variables());
856 if (lp.IsMaximizationProblem()) {
858 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
860 if (objective_[
col] != coeff) {
861 objective_is_unchanged =
false;
863 objective_[
col] = coeff;
865 objective_offset_ = -lp.objective_offset();
866 objective_scaling_factor_ = -lp.objective_scaling_factor();
868 for (ColIndex
col(0);
col < lp.num_variables(); ++
col) {
869 if (objective_[
col] != lp.objective_coefficients()[
col]) {
870 objective_is_unchanged =
false;
874 if (!objective_is_unchanged) {
875 objective_ = lp.objective_coefficients();
877 objective_offset_ = lp.objective_offset();
878 objective_scaling_factor_ = lp.objective_scaling_factor();
880 return objective_is_unchanged;
883 void RevisedSimplex::InitializeObjectiveLimit(
const LinearProgram& lp) {
884 objective_limit_reached_ =
false;
885 DCHECK(std::isfinite(objective_offset_));
886 DCHECK(std::isfinite(objective_scaling_factor_));
887 DCHECK_NE(0.0, objective_scaling_factor_);
890 for (
const bool set_dual : {
true,
false}) {
902 const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
903 ? parameters_.objective_lower_limit()
904 : parameters_.objective_upper_limit();
906 limit / objective_scaling_factor_ - objective_offset_;
908 dual_objective_limit_ = shifted_limit;
910 primal_objective_limit_ = shifted_limit;
915 void RevisedSimplex::InitializeVariableStatusesForWarmStart(
916 const BasisState& state, ColIndex num_new_cols) {
918 RowIndex num_basic_variables(0);
919 DCHECK_LE(num_new_cols, first_slack_col_);
920 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
923 for (ColIndex
col(0);
col < num_cols_; ++
col) {
928 if (
col < first_new_col &&
col < state.statuses.size()) {
929 status = state.statuses[
col];
930 }
else if (
col >= first_slack_col_ &&
931 col - num_new_cols < state.statuses.size()) {
932 status = state.statuses[
col - num_new_cols];
937 if (num_basic_variables == num_rows_) {
938 VLOG(1) <<
"Too many basic variables in the warm-start basis."
939 <<
"Only keeping the first ones as VariableStatus::BASIC.";
942 ++num_basic_variables;
949 if ((status != default_status) &&
957 status = default_status;
972 Status RevisedSimplex::CreateInitialBasis() {
979 int num_free_variables = 0;
981 for (ColIndex
col(0);
col < num_cols_; ++
col) {
983 SetNonBasicVariableStatusAndDeriveValue(
col, status);
986 VLOG(1) <<
"Number of free variables in the problem: " << num_free_variables;
990 for (RowIndex
row(0);
row < num_rows_; ++
row) {
991 basis[
row] = SlackColIndex(
row);
996 if (!parameters_.use_dual_simplex() &&
997 parameters_.initial_basis() != GlopParameters::MAROS &&
998 parameters_.exploit_singleton_column_in_initial_basis()) {
1002 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1006 if (objective > 0 &&
IsFinite(lower_bound_[
col]) &&
1008 SetNonBasicVariableStatusAndDeriveValue(
col,
1010 }
else if (objective < 0 &&
IsFinite(upper_bound_[
col]) &&
1012 SetNonBasicVariableStatusAndDeriveValue(
col,
1019 ComputeVariableValuesError();
1028 UseSingletonColumnInInitialBasis(&basis);
1031 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1033 basis[
row] = SlackColIndex(
row);
1039 if (parameters_.initial_basis() == GlopParameters::NONE) {
1040 return InitializeFirstBasis(basis);
1042 if (parameters_.initial_basis() == GlopParameters::MAROS) {
1043 InitialBasis initial_basis(compact_matrix_, objective_, lower_bound_,
1045 if (parameters_.use_dual_simplex()) {
1048 initial_basis.GetDualMarosBasis(num_cols_, &basis);
1050 initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1052 int number_changed = 0;
1053 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1054 if (basis[
row] != SlackColIndex(
row)) {
1058 VLOG(1) <<
"Number of Maros basis changes: " << number_changed;
1059 }
else if (parameters_.initial_basis() == GlopParameters::BIXBY ||
1060 parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1062 int num_fixed_variables = 0;
1063 for (RowIndex
row(0);
row < basis.size(); ++
row) {
1064 const ColIndex
col = basis[
row];
1065 if (lower_bound_[
col] == upper_bound_[
col]) {
1067 ++num_fixed_variables;
1071 if (num_fixed_variables == 0) {
1072 VLOG(1) <<
"Crash is set to " << parameters_.initial_basis()
1073 <<
" but there is no equality rows to remove from initial all "
1077 VLOG(1) <<
"Trying to remove " << num_fixed_variables
1078 <<
" fixed variables from the initial basis.";
1079 InitialBasis initial_basis(compact_matrix_, objective_, lower_bound_,
1082 if (parameters_.initial_basis() == GlopParameters::BIXBY) {
1083 if (parameters_.use_scaling()) {
1084 initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1086 VLOG(1) <<
"Bixby initial basis algorithm requires the problem "
1087 <<
"to be scaled. Skipping Bixby's algorithm.";
1089 }
else if (parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1092 if (parameters_.use_dual_simplex()) {
1095 initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1097 initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1100 const Status status = InitializeFirstBasis(basis);
1104 VLOG(1) <<
"Reverting to all slack basis.";
1106 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1107 basis[
row] = SlackColIndex(
row);
1113 LOG(
WARNING) <<
"Unsupported initial_basis parameters: "
1114 << parameters_.initial_basis();
1117 return InitializeFirstBasis(basis);
1120 Status RevisedSimplex::InitializeFirstBasis(
const RowToColMapping& basis) {
1126 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1128 basis_[
row] = SlackColIndex(
row);
1142 if (condition_number_ub > parameters_.initial_condition_number_threshold()) {
1143 const std::string error_message =
1144 absl::StrCat(
"The matrix condition number upper bound is too high: ",
1145 condition_number_ub);
1146 VLOG(1) << error_message;
1151 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1154 DCHECK(BasisIsConsistent());
1161 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1163 VLOG(1) << absl::StrCat(
1164 "The primal residual of the initial basis is above the tolerance, ",
1171 Status RevisedSimplex::Initialize(
const LinearProgram& lp) {
1172 parameters_ = initial_parameters_;
1173 PropagateParameters();
1180 ColIndex num_new_cols(0);
1181 bool only_change_is_new_rows =
false;
1182 bool only_change_is_new_cols =
false;
1183 bool matrix_is_unchanged =
true;
1184 bool only_new_bounds =
false;
1185 if (solution_state_.
IsEmpty() || !notify_that_matrix_is_unchanged_) {
1186 matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1187 lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols);
1188 only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1189 OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1192 CHECK(InitializeMatrixAndTestIfUnchanged(
1193 lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols));
1195 notify_that_matrix_is_unchanged_ =
false;
1196 const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1197 const bool bounds_are_unchanged = InitializeBoundsAndTestIfUnchanged(lp);
1202 if (matrix_is_unchanged && parameters_.allow_simplex_algorithm_change()) {
1203 if (objective_is_unchanged && !bounds_are_unchanged) {
1204 parameters_.set_use_dual_simplex(
true);
1205 PropagateParameters();
1207 if (bounds_are_unchanged && !objective_is_unchanged) {
1208 parameters_.set_use_dual_simplex(
false);
1209 PropagateParameters();
1213 InitializeObjectiveLimit(lp);
1229 bool solve_from_scratch =
true;
1232 if (!solution_state_.
IsEmpty() && !solution_state_has_been_set_externally_) {
1233 if (!parameters_.use_dual_simplex()) {
1238 dual_edge_norms_.
Clear();
1239 dual_pricing_vector_.
clear();
1240 if (matrix_is_unchanged && bounds_are_unchanged) {
1244 solve_from_scratch =
false;
1245 }
else if (only_change_is_new_cols && only_new_bounds) {
1246 InitializeVariableStatusesForWarmStart(solution_state_, num_new_cols);
1247 const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1248 for (ColIndex& col_ref : basis_) {
1249 if (col_ref >= first_new_col) {
1250 col_ref += num_new_cols;
1257 primal_edge_norms_.
Clear();
1259 solve_from_scratch =
false;
1265 primal_edge_norms_.
Clear();
1266 if (objective_is_unchanged) {
1267 if (matrix_is_unchanged) {
1268 if (!bounds_are_unchanged) {
1269 InitializeVariableStatusesForWarmStart(solution_state_,
1273 solve_from_scratch =
false;
1274 }
else if (only_change_is_new_rows) {
1277 InitializeVariableStatusesForWarmStart(solution_state_, ColIndex(0));
1284 dual_pricing_vector_.
clear();
1287 if (InitializeFirstBasis(basis_).ok()) {
1288 solve_from_scratch =
false;
1297 const bool log_info = parameters_.log_search_progress() ||
VLOG_IS_ON(1);
1298 if (solve_from_scratch && !solution_state_.
IsEmpty()) {
1301 InitializeVariableStatusesForWarmStart(solution_state_, ColIndex(0));
1309 basis_factorization_.
Clear();
1311 primal_edge_norms_.
Clear();
1312 dual_edge_norms_.
Clear();
1313 dual_pricing_vector_.
clear();
1318 if (InitializeFirstBasis(basis_).ok()) {
1319 solve_from_scratch =
false;
1322 LOG(
INFO) <<
"RevisedSimplex is not using the warm start "
1323 "basis because it is not factorizable.";
1328 if (solve_from_scratch) {
1329 if (log_info)
LOG(
INFO) <<
"Solve from scratch.";
1330 basis_factorization_.
Clear();
1332 primal_edge_norms_.
Clear();
1333 dual_edge_norms_.
Clear();
1334 dual_pricing_vector_.
clear();
1337 if (log_info)
LOG(
INFO) <<
"Incremental solve.";
1339 DCHECK(BasisIsConsistent());
1343 void RevisedSimplex::DisplayBasicVariableStatistics() {
1346 int num_fixed_variables = 0;
1347 int num_free_variables = 0;
1348 int num_variables_at_bound = 0;
1349 int num_slack_variables = 0;
1350 int num_infeasible_variables = 0;
1354 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1355 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1356 const ColIndex
col = basis_[
row];
1359 ++num_free_variables;
1361 if (
value > upper_bound_[
col] + tolerance ||
1362 value < lower_bound_[
col] - tolerance) {
1363 ++num_infeasible_variables;
1365 if (
col >= first_slack_col_) {
1366 ++num_slack_variables;
1368 if (lower_bound_[
col] == upper_bound_[
col]) {
1369 ++num_fixed_variables;
1370 }
else if (variable_values[
col] == lower_bound_[
col] ||
1371 variable_values[
col] == upper_bound_[
col]) {
1372 ++num_variables_at_bound;
1376 VLOG(1) <<
"Basis size: " << num_rows_;
1377 VLOG(1) <<
"Number of basic infeasible variables: "
1378 << num_infeasible_variables;
1379 VLOG(1) <<
"Number of basic slack variables: " << num_slack_variables;
1380 VLOG(1) <<
"Number of basic variables at bound: " << num_variables_at_bound;
1381 VLOG(1) <<
"Number of basic fixed variables: " << num_fixed_variables;
1382 VLOG(1) <<
"Number of basic free variables: " << num_free_variables;
1385 void RevisedSimplex::SaveState() {
1388 solution_state_has_been_set_externally_ =
false;
1391 RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1393 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1395 contains_data[e.row()] =
true;
1398 RowIndex num_empty_rows(0);
1399 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1400 if (!contains_data[
row]) {
1402 VLOG(1) <<
"Row " <<
row <<
" is empty.";
1405 return num_empty_rows;
1408 ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1409 ColIndex num_empty_cols(0);
1410 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1413 VLOG(1) <<
"Column " <<
col <<
" is empty.";
1416 return num_empty_cols;
1419 void RevisedSimplex::CorrectErrorsOnVariableValues() {
1431 if (primal_residual >= parameters_.harris_tolerance_ratio() *
1432 parameters_.primal_feasibility_tolerance()) {
1434 VLOG(1) <<
"Primal infeasibility (bounds error) = "
1436 <<
", Primal residual |A.x - b| = "
1449 (!feasibility_phase_ && num_consecutive_degenerate_iterations_ >= 100)) {
1450 VLOG(1) <<
"Perturbing the problem.";
1451 const Fractional tolerance = parameters_.harris_tolerance_ratio() *
1452 parameters_.primal_feasibility_tolerance();
1453 std::uniform_real_distribution<double> dist(0, tolerance);
1454 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1455 bound_perturbation_[
col] += dist(random_);
1460 void RevisedSimplex::ComputeVariableValuesError() {
1464 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1470 void RevisedSimplex::ComputeDirection(ColIndex
col) {
1474 direction_infinity_norm_ = 0.0;
1477 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1481 direction_infinity_norm_ =
1486 for (
const auto e : direction_) {
1487 direction_infinity_norm_ =
1488 std::max(direction_infinity_norm_, std::abs(e.coefficient()));
1492 num_rows_ == 0 ? 0.0
1493 :
static_cast<double>(direction_.non_zeros.size()) /
1494 static_cast<double>(num_rows_.value())));
1497 Fractional RevisedSimplex::ComputeDirectionError(ColIndex
col) {
1500 for (
const auto e : direction_) {
1507 template <
bool is_entering_reduced_cost_positive>
1509 const ColIndex
col = basis_[
row];
1514 if (is_entering_reduced_cost_positive) {
1515 if (direction > 0.0) {
1516 return (upper_bound_[
col] -
value) / direction;
1518 return (lower_bound_[
col] -
value) / direction;
1521 if (direction > 0.0) {
1522 return (
value - lower_bound_[
col]) / direction;
1524 return (
value - upper_bound_[
col]) / direction;
1529 template <
bool is_entering_reduced_cost_positive>
1530 Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1531 Fractional bound_flip_ratio, SparseColumn* leaving_candidates)
const {
1534 parameters_.harris_tolerance_ratio() *
1535 parameters_.primal_feasibility_tolerance();
1536 const Fractional minimum_delta = parameters_.degenerate_ministep_factor() *
1537 parameters_.primal_feasibility_tolerance();
1543 leaving_candidates->Clear();
1550 ? parameters_.minimum_acceptable_pivot()
1551 : parameters_.ratio_test_zero_threshold();
1553 for (
const auto e : direction_) {
1554 const Fractional magnitude = std::abs(e.coefficient());
1555 if (magnitude <= threshold)
continue;
1556 Fractional ratio = GetRatio<is_entering_reduced_cost_positive>(e.row());
1559 if (
false &&
ratio < 0.0) {
1562 ratio += std::abs(bound_perturbation_[basis_[e.row()]] / e.coefficient());
1564 if (
ratio <= harris_ratio) {
1565 leaving_candidates->SetCoefficient(e.row(),
ratio);
1577 harris_ratio =
std::min(harris_ratio,
1578 std::max(minimum_delta / magnitude,
1579 ratio + harris_tolerance / magnitude));
1582 return harris_ratio;
1595 if (current >= 0.0) {
1596 return candidate >= 0.0 && candidate <= current;
1598 return candidate >= current;
1606 Status RevisedSimplex::ChooseLeavingVariableRow(
1607 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1617 int stats_num_leaving_choices = 0;
1618 equivalent_leaving_choices_.clear();
1620 stats_num_leaving_choices = 0;
1624 const Fractional entering_value = variable_values_.
Get(entering_col);
1626 (reduced_cost > 0.0) ? entering_value - lower_bound_[entering_col]
1627 : upper_bound_[entering_col] - entering_value;
1634 (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1635 current_ratio, &leaving_candidates_)
1636 : ComputeHarrisRatioAndLeavingCandidates<false>(
1637 current_ratio, &leaving_candidates_);
1642 if (current_ratio <= harris_ratio) {
1644 *step_length = current_ratio;
1654 stats_num_leaving_choices = 0;
1656 equivalent_leaving_choices_.clear();
1659 if (
ratio > harris_ratio)
continue;
1660 ++stats_num_leaving_choices;
1661 const RowIndex
row = e.row();
1666 const Fractional candidate_magnitude = std::abs(direction_[
row]);
1667 if (candidate_magnitude < pivot_magnitude)
continue;
1668 if (candidate_magnitude == pivot_magnitude) {
1669 if (!IsRatioMoreOrEquallyStable(
ratio, current_ratio))
continue;
1670 if (
ratio == current_ratio) {
1672 equivalent_leaving_choices_.push_back(
row);
1676 equivalent_leaving_choices_.clear();
1677 current_ratio =
ratio;
1678 pivot_magnitude = candidate_magnitude;
1683 if (!equivalent_leaving_choices_.empty()) {
1684 equivalent_leaving_choices_.push_back(*leaving_row);
1686 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1687 0, equivalent_leaving_choices_.size() - 1)(random_)];
1699 if (current_ratio <= 0.0) {
1703 parameters_.degenerate_ministep_factor() *
1704 parameters_.primal_feasibility_tolerance();
1705 *step_length = minimum_delta / pivot_magnitude;
1707 *step_length = current_ratio;
1714 TestPivot(entering_col, *leaving_row);
1727 if (pivot_magnitude <
1728 parameters_.small_pivot_threshold() * direction_infinity_norm_) {
1733 VLOG(1) <<
"Refactorizing to avoid pivoting by "
1734 << direction_[*leaving_row]
1735 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1736 <<
" reduced cost = " << reduced_cost;
1737 *refactorize =
true;
1747 VLOG(1) <<
"Couldn't avoid pivoting by " << direction_[*leaving_row]
1748 <<
" direction_infinity_norm_ = " << direction_infinity_norm_
1749 <<
" reduced cost = " << reduced_cost;
1750 DCHECK_GE(std::abs(direction_[*leaving_row]),
1751 parameters_.minimum_acceptable_pivot());
1759 const bool is_reduced_cost_positive = (reduced_cost > 0.0);
1760 const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
1761 *
target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
1762 ? upper_bound_[basis_[*leaving_row]]
1763 : lower_bound_[basis_[*leaving_row]];
1768 ratio_test_stats_.leaving_choices.Add(stats_num_leaving_choices);
1769 if (!equivalent_leaving_choices_.empty()) {
1770 ratio_test_stats_.num_perfect_ties.Add(
1771 equivalent_leaving_choices_.size());
1774 ratio_test_stats_.abs_used_pivot.Add(std::abs(direction_[*leaving_row]));
1796 bool operator<(
const BreakPoint& other)
const {
1797 if (
ratio == other.ratio) {
1799 return row > other.row;
1803 return ratio > other.ratio;
1814 void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
1815 ColIndex entering_col,
Fractional reduced_cost,
bool* refactorize,
1816 RowIndex* leaving_row,
Fractional* step_length,
1827 const Fractional entering_value = variable_values_.
Get(entering_col);
1828 Fractional current_ratio = (reduced_cost > 0.0)
1829 ? entering_value - lower_bound_[entering_col]
1830 : upper_bound_[entering_col] - entering_value;
1833 std::vector<BreakPoint> breakpoints;
1834 const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1835 for (
const auto e : direction_) {
1837 reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
1838 const Fractional magnitude = std::abs(direction);
1839 if (magnitude < tolerance)
continue;
1854 const ColIndex
col = basis_[e.row()];
1860 const Fractional to_lower = (lower_bound - tolerance -
value) / direction;
1861 const Fractional to_upper = (upper_bound + tolerance -
value) / direction;
1865 if (to_lower >= 0.0 && to_lower < current_ratio) {
1866 breakpoints.push_back(
1867 BreakPoint(e.row(), to_lower, magnitude, lower_bound));
1869 if (to_upper >= 0.0 && to_upper < current_ratio) {
1870 breakpoints.push_back(
1871 BreakPoint(e.row(), to_upper, magnitude, upper_bound));
1877 std::make_heap(breakpoints.begin(), breakpoints.end());
1881 Fractional improvement = std::abs(reduced_cost);
1884 while (!breakpoints.empty()) {
1885 const BreakPoint top = breakpoints.front();
1893 if (top.coeff_magnitude > best_magnitude) {
1894 *leaving_row = top.row;
1895 current_ratio = top.ratio;
1896 best_magnitude = top.coeff_magnitude;
1902 improvement -= top.coeff_magnitude;
1903 if (improvement <= 0.0)
break;
1904 std::pop_heap(breakpoints.begin(), breakpoints.end());
1905 breakpoints.pop_back();
1911 parameters_.small_pivot_threshold() * direction_infinity_norm_;
1912 if (best_magnitude < threshold && !basis_factorization_.
IsRefactorized()) {
1913 *refactorize =
true;
1917 *step_length = current_ratio;
1921 Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
1936 equivalent_leaving_choices_.clear();
1938 const Fractional scaled_best_price = best_price * squared_norm[
row];
1939 if (squared_infeasibilities[
row] >= scaled_best_price) {
1940 if (squared_infeasibilities[
row] == scaled_best_price) {
1942 equivalent_leaving_choices_.push_back(
row);
1945 equivalent_leaving_choices_.clear();
1946 best_price = squared_infeasibilities[
row] / squared_norm[
row];
1952 if (!equivalent_leaving_choices_.empty()) {
1953 equivalent_leaving_choices_.push_back(*leaving_row);
1955 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1956 0, equivalent_leaving_choices_.size() - 1)(random_)];
1962 const ColIndex leaving_col = basis_[*leaving_row];
1964 if (
value < lower_bound_[leaving_col]) {
1965 *cost_variation = lower_bound_[leaving_col] -
value;
1969 *cost_variation = upper_bound_[leaving_col] -
value;
1983 if (
cost == 0.0)
return false;
1992 void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
1993 ColIndex entering_col) {
1996 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2002 dual_pricing_vector_[leaving_row] / direction_[leaving_row];
2003 for (
const auto e : direction_) {
2004 dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2005 is_dual_entering_candidate_.
Set(
2006 e.row(), IsDualPhaseILeavingCandidate(dual_pricing_vector_[e.row()],
2007 variable_type[basis_[e.row()]],
2010 dual_pricing_vector_[leaving_row] = step;
2014 dual_pricing_vector_[leaving_row] -=
2015 dual_infeasibility_improvement_direction_[entering_col];
2016 if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2017 --num_dual_infeasible_positions_;
2019 dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2022 dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2025 is_dual_entering_candidate_.
Set(
2027 IsDualPhaseILeavingCandidate(dual_pricing_vector_[leaving_row],
2028 variable_type[entering_col], threshold));
2031 template <
typename Cols>
2032 void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2035 bool something_to_do =
false;
2040 for (ColIndex
col : cols) {
2043 (can_increase.IsSet(
col) && reduced_cost < -tolerance) ? 1.0
2044 : (can_decrease.IsSet(
col) && reduced_cost > tolerance) ? -1.0
2046 if (sign != dual_infeasibility_improvement_direction_[
col]) {
2048 --num_dual_infeasible_positions_;
2049 }
else if (dual_infeasibility_improvement_direction_[
col] == 0.0) {
2050 ++num_dual_infeasible_positions_;
2052 if (!something_to_do) {
2053 initially_all_zero_scratchpad_.
values.
resize(num_rows_, 0.0);
2055 initially_all_zero_scratchpad_.
non_zeros.clear();
2056 something_to_do =
true;
2059 col, sign - dual_infeasibility_improvement_direction_[
col],
2060 &initially_all_zero_scratchpad_);
2061 dual_infeasibility_improvement_direction_[
col] = sign;
2064 if (something_to_do) {
2069 const Fractional threshold = parameters_.ratio_test_zero_threshold();
2070 basis_factorization_.
RightSolve(&initially_all_zero_scratchpad_);
2071 if (initially_all_zero_scratchpad_.
non_zeros.empty()) {
2072 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2073 if (initially_all_zero_scratchpad_[
row] == 0.0)
continue;
2074 dual_pricing_vector_[
row] += initially_all_zero_scratchpad_[
row];
2075 is_dual_entering_candidate_.
Set(
2076 row, IsDualPhaseILeavingCandidate(dual_pricing_vector_[
row],
2077 variable_type[basis_[
row]],
2082 for (
const auto e : initially_all_zero_scratchpad_) {
2083 dual_pricing_vector_[e.row()] += e.coefficient();
2084 initially_all_zero_scratchpad_[e.row()] = 0.0;
2085 is_dual_entering_candidate_.
Set(
2086 e.row(), IsDualPhaseILeavingCandidate(
2087 dual_pricing_vector_[e.row()],
2088 variable_type[basis_[e.row()]], threshold));
2091 initially_all_zero_scratchpad_.non_zeros.clear();
2095 Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2096 RowIndex* leaving_row,
Fractional* cost_variation,
2113 dual_pricing_vector_.
empty()) {
2115 num_dual_infeasible_positions_ = 0;
2118 dual_infeasibility_improvement_direction_.
AssignToZero(num_cols_);
2119 DualPhaseIUpdatePriceOnReducedCostChange(
2129 if (num_dual_infeasible_positions_ == 0)
return Status::OK();
2138 equivalent_leaving_choices_.clear();
2139 for (
const RowIndex
row : is_dual_entering_candidate_) {
2141 const Fractional scaled_best_price = best_price * squared_norm[
row];
2142 if (squared_cost >= scaled_best_price) {
2143 if (squared_cost == scaled_best_price) {
2145 equivalent_leaving_choices_.push_back(
row);
2148 equivalent_leaving_choices_.clear();
2149 best_price = squared_cost / squared_norm[
row];
2155 if (!equivalent_leaving_choices_.empty()) {
2156 equivalent_leaving_choices_.push_back(*leaving_row);
2158 equivalent_leaving_choices_[std::uniform_int_distribution<int>(
2159 0, equivalent_leaving_choices_.size() - 1)(random_)];
2165 *cost_variation = dual_pricing_vector_[*leaving_row];
2166 const ColIndex leaving_col = basis_[*leaving_row];
2167 if (*cost_variation < 0.0) {
2176 template <
typename BoxedVariableCols>
2177 void RevisedSimplex::MakeBoxedVariableDualFeasible(
2178 const BoxedVariableCols& cols,
bool update_basic_values) {
2180 std::vector<ColIndex> changed_cols;
2188 const Fractional dual_feasibility_tolerance =
2191 for (
const ColIndex
col : cols) {
2198 variable_values[
col] == upper_bound_[
col] ||
2200 if (reduced_cost > dual_feasibility_tolerance &&
2203 changed_cols.push_back(
col);
2204 }
else if (reduced_cost < -dual_feasibility_tolerance &&
2207 changed_cols.push_back(
col);
2211 if (!changed_cols.empty()) {
2213 update_basic_values);
2217 Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2222 const ColIndex leaving_col = basis_[leaving_row];
2223 const Fractional leaving_variable_value = variable_values_.
Get(leaving_col);
2233 return unscaled_step / direction_[leaving_row];
2236 bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2237 VLOG(1) <<
"Test pivot.";
2239 const ColIndex leaving_col = basis_[leaving_row];
2240 basis_[leaving_row] = entering_col;
2244 CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2246 basis_[leaving_row] = leaving_col;
2253 void RevisedSimplex::PermuteBasis() {
2260 if (col_perm.empty())
return;
2266 if (!dual_pricing_vector_.
empty()) {
2283 Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2284 RowIndex leaving_row,
2287 const ColIndex leaving_col = basis_[leaving_row];
2289 lower_bound_[leaving_col] == upper_bound_[leaving_col]
2295 ratio_test_stats_.bound_shift.Add(variable_values_.
Get(leaving_col) -
2298 UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2300 const Fractional pivot_from_direction = direction_[leaving_row];
2304 std::abs(pivot_from_update_row - pivot_from_direction);
2305 if (diff > parameters_.refactorization_threshold() *
2306 (1 + std::abs(pivot_from_direction))) {
2307 VLOG(1) <<
"Refactorizing: imprecise pivot " << pivot_from_direction
2308 <<
" diff = " << diff;
2312 basis_factorization_.
Update(entering_col, leaving_row, direction_));
2320 bool RevisedSimplex::NeedsBasisRefactorization(
bool refactorize) {
2323 const GlopParameters::PricingRule pricing_rule =
2324 feasibility_phase_ ? parameters_.feasibility_rule()
2325 : parameters_.optimization_rule();
2326 if (parameters_.use_dual_simplex()) {
2328 DCHECK_EQ(pricing_rule, GlopParameters::STEEPEST_EDGE);
2331 if (pricing_rule == GlopParameters::STEEPEST_EDGE &&
2339 Status RevisedSimplex::RefactorizeBasisIfNeeded(
bool* refactorize) {
2341 if (NeedsBasisRefactorization(*refactorize)) {
2346 *refactorize =
false;
2351 if (
col >= integrality_scale_.
size()) {
2352 integrality_scale_.
resize(
col + 1, 0.0);
2354 integrality_scale_[
col] = scale;
2359 Cleanup update_deterministic_time_on_return(
2366 std::vector<ColIndex> candidates;
2372 bool refactorize =
false;
2375 for (
int i = 0; i < 10; ++i) {
2378 if (num_pivots >= 5)
break;
2379 if (candidates.empty())
break;
2383 std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2384 const ColIndex entering_col = candidates[
index];
2385 std::swap(candidates[
index], candidates.back());
2386 candidates.pop_back();
2397 ComputeDirection(entering_col);
2399 RowIndex leaving_row;
2401 bool local_refactorize =
false;
2403 ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2406 if (local_refactorize)
continue;
2408 if (std::abs(step_length) <= 1e-6)
continue;
2409 if (leaving_row !=
kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2412 const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2418 const auto get_diff = [
this](ColIndex
col,
Fractional old_value,
2420 if (
col >= integrality_scale_.
size() || integrality_scale_[
col] == 0.0) {
2424 return (std::abs(new_value * s - std::round(new_value * s)) -
2425 std::abs(old_value * s - std::round(old_value * s)));
2427 Fractional diff = get_diff(entering_col, variable_values_.
Get(entering_col),
2428 variable_values_.
Get(entering_col) + step);
2429 for (
const auto e : direction_) {
2430 const ColIndex
col = basis_[e.row()];
2432 const Fractional new_value = old_value - e.coefficient() * step;
2433 diff += get_diff(
col, old_value, new_value);
2437 if (diff > -1e-2)
continue;
2447 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2449 }
else if (step < 0.0) {
2450 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2458 const ColIndex leaving_col = basis_[leaving_row];
2461 entering_col, leaving_col, leaving_row, direction_, &update_row_);
2465 const Fractional dir = -direction_[leaving_row] * step;
2466 const bool is_degenerate =
2470 if (!is_degenerate) {
2474 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2477 VLOG(1) <<
"Polish num_pivots: " << num_pivots <<
" gain:" << total_gain;
2496 Status RevisedSimplex::Minimize(TimeLimit*
time_limit) {
2498 Cleanup update_deterministic_time_on_return(
2500 num_consecutive_degenerate_iterations_ = 0;
2501 DisplayIterationInfo();
2502 bool refactorize =
false;
2504 if (feasibility_phase_) {
2520 CorrectErrorsOnVariableValues();
2521 DisplayIterationInfo();
2523 if (feasibility_phase_) {
2535 if (!feasibility_phase_ &&
2536 ComputeObjectiveValue() < primal_objective_limit_) {
2537 VLOG(1) <<
"Stopping the primal simplex because"
2538 <<
" the objective limit " << primal_objective_limit_
2539 <<
" has been reached.";
2541 objective_limit_reached_ =
true;
2544 }
else if (feasibility_phase_) {
2560 if (feasibility_phase_) {
2563 if (primal_infeasibility <
2564 parameters_.primal_feasibility_tolerance()) {
2567 VLOG(1) <<
"Infeasible problem! infeasibility = "
2568 << primal_infeasibility;
2576 VLOG(1) <<
"Optimal reached, double checking...";
2586 ComputeDirection(entering_col);
2590 entering_col, direction_, &reduced_cost)) {
2591 VLOG(1) <<
"Skipping col #" << entering_col <<
" whose reduced cost is "
2602 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2608 RowIndex leaving_row;
2610 if (feasibility_phase_) {
2611 PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2612 &refactorize, &leaving_row,
2616 ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2619 if (refactorize)
continue;
2624 VLOG(1) <<
"Infinite step length, double checking...";
2628 if (feasibility_phase_) {
2630 VLOG(1) <<
"Unbounded feasibility problem !?";
2633 VLOG(1) <<
"Unbounded problem.";
2636 for (RowIndex
row(0);
row < num_rows_; ++
row) {
2637 const ColIndex
col = basis_[
row];
2638 solution_primal_ray_[
col] = -direction_[
row];
2640 solution_primal_ray_[entering_col] = 1.0;
2648 Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2649 if (feasibility_phase_ && leaving_row !=
kInvalidRow) {
2659 step = ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2663 const ColIndex leaving_col =
2669 bool is_degenerate =
false;
2671 Fractional dir = -direction_[leaving_row] * step;
2679 if (!is_degenerate) {
2680 DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
2688 entering_col, basis_[leaving_row], leaving_row, direction_,
2691 direction_, &update_row_);
2692 if (!is_degenerate) {
2701 UpdateAndPivot(entering_col, leaving_row,
target_bound));
2703 if (is_degenerate) {
2704 timer.AlsoUpdate(&iteration_stats_.degenerate);
2706 timer.AlsoUpdate(&iteration_stats_.normal);
2715 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2717 }
else if (step < 0.0) {
2718 SetNonBasicVariableStatusAndDeriveValue(entering_col,
2725 if (feasibility_phase_ && leaving_row !=
kInvalidRow) {
2729 &objective_[leaving_col]);
2733 if (step_length == 0.0) {
2734 num_consecutive_degenerate_iterations_++;
2736 if (num_consecutive_degenerate_iterations_ > 0) {
2737 iteration_stats_.degenerate_run_size.Add(
2738 num_consecutive_degenerate_iterations_);
2739 num_consecutive_degenerate_iterations_ = 0;
2744 if (num_consecutive_degenerate_iterations_ > 0) {
2745 iteration_stats_.degenerate_run_size.Add(
2746 num_consecutive_degenerate_iterations_);
2761 Status RevisedSimplex::DualMinimize(TimeLimit*
time_limit) {
2762 Cleanup update_deterministic_time_on_return(
2764 num_consecutive_degenerate_iterations_ = 0;
2765 bool refactorize =
false;
2767 bound_flip_candidates_.clear();
2768 pair_to_ignore_.clear();
2771 RowIndex leaving_row;
2776 ColIndex entering_col;
2785 const bool old_refactorize_value = refactorize;
2802 !old_refactorize_value) {
2805 if (dual_residual_error >
2807 VLOG(1) <<
"Recomputing reduced costs. Dual residual = "
2808 << dual_residual_error;
2823 if (!feasibility_phase_) {
2824 MakeBoxedVariableDualFeasible(
2832 if (ComputeObjectiveValue() > dual_objective_limit_) {
2833 VLOG(1) <<
"Stopping the dual simplex because"
2834 <<
" the objective limit " << dual_objective_limit_
2835 <<
" has been reached.";
2837 objective_limit_reached_ =
true;
2843 DisplayIterationInfo();
2847 if (!feasibility_phase_) {
2850 MakeBoxedVariableDualFeasible(bound_flip_candidates_,
2852 bound_flip_candidates_.clear();
2857 direction_.non_zeros);
2861 if (feasibility_phase_) {
2870 VLOG(1) <<
"Optimal reached, double checking.";
2874 if (feasibility_phase_) {
2879 if (num_dual_infeasible_positions_ == 0) {
2891 for (std::pair<RowIndex, ColIndex> pair : pair_to_ignore_) {
2892 if (pair.first == leaving_row) {
2896 if (feasibility_phase_) {
2898 update_row_, cost_variation, &entering_col, &
ratio));
2901 update_row_, cost_variation, &bound_flip_candidates_, &entering_col,
2908 VLOG(1) <<
"No entering column. Double checking...";
2913 if (feasibility_phase_) {
2915 VLOG(1) <<
"Unbounded dual feasibility problem !?";
2919 solution_dual_ray_ =
2922 solution_dual_ray_row_combination_.
AssignToZero(num_cols_);
2924 solution_dual_ray_row_combination_[
col] =
2927 if (cost_variation < 0) {
2929 ChangeSign(&solution_dual_ray_row_combination_);
2937 if (std::abs(entering_coeff) < parameters_.dual_small_pivot_threshold() &&
2939 VLOG(1) <<
"Trying not to pivot by " << entering_coeff;
2948 ComputeDirection(entering_col);
2949 if (std::abs(direction_[leaving_row]) <
2950 parameters_.minimum_acceptable_pivot()) {
2951 VLOG(1) <<
"Do not pivot by " << entering_coeff
2952 <<
" because the direction is " << direction_[leaving_row];
2954 pair_to_ignore_.push_back({leaving_row, entering_col});
2957 pair_to_ignore_.clear();
2964 if (num_iterations_ == parameters_.max_number_of_iterations() ||
2971 timer.AlsoUpdate(&iteration_stats_.degenerate);
2973 timer.AlsoUpdate(&iteration_stats_.normal);
2985 if (feasibility_phase_) {
2986 DualPhaseIUpdatePrice(leaving_row, entering_col);
2989 ComputeStepToMoveBasicVariableToBound(leaving_row,
target_bound);
2996 entering_col, leaving_row, direction_,
3000 const ColIndex leaving_col = basis_[leaving_row];
3002 UpdateAndPivot(entering_col, leaving_row,
target_bound));
3012 if (std::abs(primal_step) * parameters_.primal_feasibility_tolerance() >
3021 ColIndex RevisedSimplex::SlackColIndex(RowIndex
row)
const {
3029 result.append(iteration_stats_.StatString());
3030 result.append(ratio_test_stats_.StatString());
3031 result.append(entering_variable_.
StatString());
3033 result.append(variable_values_.
StatString());
3034 result.append(primal_edge_norms_.
StatString());
3035 result.append(dual_edge_norms_.
StatString());
3037 result.append(basis_factorization_.
StatString());
3042 void RevisedSimplex::DisplayAllStats() {
3043 if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3045 absl::FPrintF(stderr,
"%s", GetPrettySolverStats());
3049 Fractional RevisedSimplex::ComputeObjectiveValue()
const {
3055 Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue()
const {
3059 return objective_scaling_factor_ * (sum + objective_offset_);
3067 PropagateParameters();
3070 void RevisedSimplex::PropagateParameters() {
3080 void RevisedSimplex::DisplayIterationInfo()
const {
3081 if (parameters_.log_search_progress() ||
VLOG_IS_ON(1)) {
3082 const int iter = feasibility_phase_
3084 : num_iterations_ - num_feasibility_iterations_;
3091 ? ComputeInitialProblemObjectiveValue()
3092 : (parameters_.use_dual_simplex()
3093 ? reduced_costs_.ComputeSumOfDualInfeasibilities()
3094 : variable_values_.ComputeSumOfPrimalInfeasibilities());
3095 LOG(
INFO) << (feasibility_phase_ ?
"Feasibility" :
"Optimization")
3096 <<
" phase, iteration # " << iter
3097 <<
", objective = " << absl::StrFormat(
"%.15E", objective);
3101 void RevisedSimplex::DisplayErrors()
const {
3102 if (parameters_.log_search_progress() ||
VLOG_IS_ON(1)) {
3103 LOG(
INFO) <<
"Primal infeasibility (bounds) = "
3105 LOG(
INFO) <<
"Primal residual |A.x - b| = "
3107 LOG(
INFO) <<
"Dual infeasibility (reduced costs) = "
3109 LOG(
INFO) <<
"Dual residual |c_B - y.B| = "
3116 std::string StringifyMonomialWithFlags(
const Fractional a,
3117 const std::string& x) {
3119 a, x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3125 std::string StringifyWithFlags(
const Fractional x) {
3127 absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3132 std::string RevisedSimplex::SimpleVariableInfo(ColIndex
col)
const {
3136 absl::StrAppendFormat(&output,
"%d (%s) = %s, %s, %s, [%s,%s]",
col.value(),
3137 variable_name_[
col],
3138 StringifyWithFlags(variable_values_.
Get(
col)),
3141 StringifyWithFlags(lower_bound_[
col]),
3142 StringifyWithFlags(upper_bound_[
col]));
3146 void RevisedSimplex::DisplayInfoOnVariables()
const {
3148 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3152 objective_coefficient * variable_value;
3153 VLOG(3) << SimpleVariableInfo(
col) <<
". " << variable_name_[
col] <<
" = "
3154 << StringifyWithFlags(variable_value) <<
" * "
3155 << StringifyWithFlags(objective_coefficient)
3156 <<
"(obj) = " << StringifyWithFlags(objective_contribution);
3158 VLOG(3) <<
"------";
3162 void RevisedSimplex::DisplayVariableBounds() {
3165 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3166 switch (variable_type[
col]) {
3170 VLOG(3) << variable_name_[
col]
3171 <<
" >= " << StringifyWithFlags(lower_bound_[
col]) <<
";";
3174 VLOG(3) << variable_name_[
col]
3175 <<
" <= " << StringifyWithFlags(upper_bound_[
col]) <<
";";
3178 VLOG(3) << StringifyWithFlags(lower_bound_[
col])
3179 <<
" <= " << variable_name_[
col]
3180 <<
" <= " << StringifyWithFlags(upper_bound_[
col]) <<
";";
3183 VLOG(3) << variable_name_[
col] <<
" = "
3184 << StringifyWithFlags(lower_bound_[
col]) <<
";";
3187 LOG(DFATAL) <<
"Column " <<
col <<
" has no meaningful status.";
3197 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3198 ComputeDirection(
col);
3199 for (
const auto e : direction_) {
3200 if (column_scales ==
nullptr) {
3201 dictionary[e.row()].SetCoefficient(
col, e.coefficient());
3205 col < column_scales->
size() ? (*column_scales)[
col] : 1.0;
3207 ? (*column_scales)[
GetBasis(e.row())]
3209 dictionary[e.row()].SetCoefficient(
3210 col, direction_[e.row()] * (numerator / denominator));
3219 Status status = Initialize(linear_program);
3223 solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3227 void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3230 DisplayInfoOnVariables();
3232 std::string output =
"z = " + StringifyWithFlags(ComputeObjectiveValue());
3235 absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[
col],
3236 variable_name_[
col]));
3238 VLOG(3) << output <<
";";
3240 const RevisedSimplexDictionary dictionary(
nullptr,
this);
3242 for (
const SparseRow&
row : dictionary) {
3244 ColIndex basic_col = basis_[r];
3245 absl::StrAppend(&output, variable_name_[basic_col],
" = ",
3246 StringifyWithFlags(variable_values_.
Get(basic_col)));
3247 for (
const SparseRowEntry e :
row) {
3248 if (e.col() != basic_col) {
3249 absl::StrAppend(&output,
3250 StringifyMonomialWithFlags(e.coefficient(),
3251 variable_name_[e.col()]));
3254 VLOG(3) << output <<
";";
3256 VLOG(3) <<
"------";
3257 DisplayVariableBounds();
3262 void RevisedSimplex::DisplayProblem()
const {
3266 DisplayInfoOnVariables();
3267 std::string output =
"min: ";
3268 bool has_objective =
false;
3269 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3271 has_objective |= (coeff != 0.0);
3272 absl::StrAppend(&output,
3273 StringifyMonomialWithFlags(coeff, variable_name_[
col]));
3275 if (!has_objective) {
3276 absl::StrAppend(&output,
" 0");
3278 VLOG(3) << output <<
";";
3279 for (RowIndex
row(0);
row < num_rows_; ++
row) {
3281 for (ColIndex
col(0);
col < num_cols_; ++
col) {
3282 absl::StrAppend(&output,
3283 StringifyMonomialWithFlags(
3285 variable_name_[
col]));
3287 VLOG(3) << output <<
" = 0;";
3289 VLOG(3) <<
"------";
3293 void RevisedSimplex::AdvanceDeterministicTime(TimeLimit*
time_limit) {
3296 const double deterministic_time_delta =
3297 current_deterministic_time - last_deterministic_time_update_;
3298 time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3299 last_deterministic_time_update_ = current_deterministic_time;
3302 #undef DCHECK_COL_BOUNDS
3303 #undef DCHECK_ROW_BOUNDS
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
void push_back(const value_type &x)
void ClearAndResize(IndexType size)
bool IsSet(IndexType i) const
std::string StatString() const
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Fractional ComputeInfinityNormConditionNumberUpperBound() const
ABSL_MUST_USE_RESULT Status Refactorize()
const ColumnPermutation & GetColumnPermutation() const
ABSL_MUST_USE_RESULT Status Initialize()
bool IsRefactorized() const
ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
void RightSolveForProblemColumn(ColIndex col, ScatteredColumn *d) const
void SetColumnPermutationToIdentity()
void SetParameters(const GlopParameters ¶meters)
void RightSolve(ScatteredColumn *d) const
double DeterministicTime() const
ABSL_MUST_USE_RESULT Status ForceRefactorization()
std::string StatString() const
Fractional LookUpCoefficient(RowIndex index) const
Fractional EntryCoefficient(EntryIndex i) const
Fractional GetFirstCoefficient() const
RowIndex EntryRow(EntryIndex i) const
EntryIndex num_entries() const
void ColumnCopyToDenseColumn(ColIndex col, DenseColumn *dense_column) const
ColIndex num_cols() const
void ColumnAddMultipleToSparseScatteredColumn(ColIndex col, Fractional multiplier, ScatteredColumn *column) const
RowIndex num_rows() const
void PopulateFromTranspose(const CompactSparseMatrix &input)
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn *dense_column) const
void PopulateFromMatrixView(const MatrixView &input)
EntryIndex num_entries() const
ColumnView column(ColIndex col) const
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
const DenseColumn & GetEdgeSquaredNorms()
bool NeedsBasisRefactorization()
void ResizeOnNewRows(RowIndex new_size)
void SetParameters(const GlopParameters ¶meters)
std::string StatString() const
ABSL_MUST_USE_RESULT Status PrimalChooseEnteringColumn(ColIndex *entering_col)
ABSL_MUST_USE_RESULT Status DualChooseEnteringColumn(const UpdateRow &update_row, Fractional cost_variation, std::vector< ColIndex > *bound_flip_candidates, ColIndex *entering_col, Fractional *step)
ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn(const UpdateRow &update_row, Fractional cost_variation, ColIndex *entering_col, Fractional *step)
void SetPricingRule(GlopParameters::PricingRule rule)
void SetParameters(const GlopParameters ¶meters)
std::string StatString() const
bool IsInEquationForm() const
bool IsMaximizationProblem() const
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
void UpdateBeforeBasisPivot(ColIndex entering_col, ColIndex leaving_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
void TestEnteringEdgeNormPrecision(ColIndex entering_col, const ScatteredColumn &direction)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
bool NeedsBasisRefactorization() const
std::string StatString() const
void ResetForNewObjective()
Fractional ComputeMaximumDualResidual() const
void MakeReducedCostsPrecise()
bool AreReducedCostsRecomputed()
bool TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction, Fractional *reduced_cost)
bool AreReducedCostsPrecise()
bool IsValidPrimalEnteringCandidate(ColIndex col) const
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
const DenseRow & GetReducedCosts()
Fractional GetDualFeasibilityTolerance() const
Fractional ComputeMaximumDualInfeasibility() const
void MaintainDualInfeasiblePositions(bool maintain)
const DenseColumn & GetDualValues()
void ClearAndRemoveCostShifts()
void UpdateDataOnBasisPermutation()
void SetParameters(const GlopParameters ¶meters)
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
int64 GetNumberOfIterations() const
const DenseRow & GetPrimalRay() const
Fractional GetConstraintActivity(RowIndex row) const
VariableStatus GetVariableStatus(ColIndex col) const
Fractional GetReducedCost(ColIndex col) const
const DenseColumn & GetDualRay() const
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
Fractional GetObjectiveValue() const
RowMajorSparseMatrix ComputeDictionary(const DenseRow *column_scales)
Fractional GetDualValue(RowIndex row) const
void NotifyThatMatrixIsUnchangedForNextSolve()
ConstraintStatus GetConstraintStatus(RowIndex row) const
void ComputeBasicVariablesForState(const LinearProgram &linear_program, const BasisState &state)
ColIndex GetProblemNumCols() const
void LoadStateForNextSolve(const BasisState &state)
RowIndex GetProblemNumRows() const
void ClearStateForNextSolve()
const BasisFactorization & GetBasisFactorization() const
const BasisState & GetState() const
ColIndex GetBasis(RowIndex row) const
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
typename Iterator::Entry Entry
void AssignToZero(IntType size)
void resize(IntType size)
const ScatteredRow & GetUnitRowLeftInverse() const
void RecomputeFullUpdateRow(RowIndex leaving_row)
void IgnoreUpdatePosition(ColIndex col)
const Fractional GetCoefficient(ColIndex col) const
void ComputeUpdateRow(RowIndex leaving_row)
void SetParameters(const GlopParameters ¶meters)
double DeterministicTime() const
const ColIndexVector & GetNonZeroPositions() const
std::string StatString() const
void Set(ColIndex col, Fractional value)
void SetNonBasicVariableValueFromStatus(ColIndex col)
const DenseColumn & GetPrimalSquaredInfeasibilities() const
Fractional ComputeMaximumPrimalInfeasibility() const
void UpdateGivenNonBasicVariables(const std::vector< ColIndex > &cols_to_update, bool update_basic_variables)
const DenseBitColumn & GetPrimalInfeasiblePositions() const
const DenseRow & GetDenseRow() const
void ResetPrimalInfeasibilityInformation()
void UpdateOnPivoting(const ScatteredColumn &direction, ColIndex entering_col, Fractional step)
const Fractional Get(ColIndex col) const
void UpdatePrimalInfeasibilityInformation(const std::vector< RowIndex > &rows)
void RecomputeBasicVariableValues()
Fractional ComputeMaximumPrimalResidual() const
bool UpdatePrimalPhaseICosts(const Rows &rows, DenseRow *objective)
void ResetAllNonBasicVariableValues()
std::string StatString() const
const DenseBitRow & GetIsBasicBitRow() const
const DenseBitRow & GetNonBasicBoxedVariables() const
Fractional GetBoundDifference(ColIndex col) const
const DenseBitRow & GetCanIncreaseBitRow() const
const DenseBitRow & GetCanDecreaseBitRow() const
const VariableTypeRow & GetTypeRow() const
void MakeBoxedVariableRelevant(bool value)
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)
void InitializeAndComputeType()
const DenseBitRow & GetNotBasicBitRow() const
const VariableStatusRow & GetStatusRow() const
void UpdateToBasicStatus(ColIndex col)
const DenseBitRow & GetIsRelevantBitRow() const
void Update(ColIndex col, VariableStatus status)
SharedTimeLimit * time_limit
std::string StringifyMonomial(const Fractional a, const std::string &x, bool fraction)
bool IsRightMostSquareMatrixIdentity(const SparseMatrix &matrix)
Fractional Square(Fractional f)
Fractional InfinityNorm(const DenseColumn &v)
const RowIndex kInvalidRow(-1)
std::string Stringify(const Fractional x, bool fraction)
StrictITIVector< ColIndex, VariableType > VariableTypeRow
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
StrictITIVector< ColIndex, Fractional > DenseRow
std::string GetProblemStatusString(ProblemStatus problem_status)
Index ColToIntIndex(ColIndex col)
Permutation< ColIndex > ColumnPermutation
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
constexpr const uint64 kDeterministicSeed
ColIndex RowToColIndex(RowIndex row)
bool IsFinite(Fractional value)
bool AreFirstColumnsAndRowsExactlyEquals(RowIndex num_rows, ColIndex num_cols, const SparseMatrix &matrix_a, const CompactSparseMatrix &matrix_b)
const DenseRow & Transpose(const DenseColumn &col)
Bitset64< ColIndex > DenseBitRow
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
@ UPPER_AND_LOWER_BOUNDED
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
std::string GetVariableStatusString(VariableStatus status)
const ColIndex kInvalidCol(-1)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
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)
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
#define GLOP_RETURN_IF_ERROR(function_call)
#define GLOP_RETURN_ERROR_IF_NULL(arg)
VariableStatusRow statuses
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation)
std::vector< Index > non_zeros
StrictITIVector< Index, Fractional > values
#define VLOG_IS_ON(verboselevel)