24#include "absl/container/flat_hash_map.h"
25#include "absl/log/check.h"
26#include "absl/strings/str_cat.h"
27#include "absl/strings/str_format.h"
48 DCHECK(!std::isnan(lower_bound));
49 DCHECK(!std::isnan(upper_bound));
52 DCHECK_LE(lower_bound, upper_bound);
61 lower_bound != upper_bound) {
67template <
class I,
class T>
68double Average(
const util_intops::StrongVector<I, T>& v) {
69 const size_t size = v.size();
72 for (I
i(0);
i < size; ++
i) {
73 if (v[i] == 0.0)
continue;
75 sum +=
static_cast<double>(v[
i].value());
77 return n == 0.0 ? 0.0 : sum / n;
80template <
class I,
class T>
81double StandardDeviation(
const util_intops::StrongVector<I, T>& v) {
82 const size_t size = v.size();
84 double sigma_square = 0.0;
86 for (I
i(0);
i < size; ++
i) {
87 double sample =
static_cast<double>(v[
i].value());
88 if (sample == 0.0)
continue;
89 sigma_square += sample * sample;
93 return n == 0.0 ? 0.0 : sqrt((sigma_square - sigma * sigma / n) / n);
97template <
class I,
class T>
98T GetMaxElement(
const util_intops::StrongVector<I, T>& v) {
99 const size_t size = v.size();
104 T max_index = v[I(0)];
105 for (I
i(1);
i < size; ++
i) {
106 if (max_index < v[i]) {
121 constraint_lower_bounds_(),
122 constraint_upper_bounds_(),
124 objective_coefficients_(),
125 variable_lower_bounds_(),
126 variable_upper_bounds_(),
129 integer_variables_list_(),
132 objective_offset_(0.0),
133 objective_scaling_factor_(1.0),
135 columns_are_known_to_be_clean_(true),
136 transpose_matrix_is_consistent_(true),
137 integer_variables_list_is_consistent_(true),
143 transpose_matrix_.Clear();
145 constraint_lower_bounds_.clear();
146 constraint_upper_bounds_.clear();
147 constraint_names_.clear();
149 objective_coefficients_.clear();
150 variable_lower_bounds_.clear();
151 variable_upper_bounds_.clear();
152 variable_types_.clear();
153 integer_variables_list_.clear();
154 variable_names_.clear();
156 constraint_table_.clear();
157 variable_table_.clear();
160 objective_offset_ = 0.0;
161 objective_scaling_factor_ = 1.0;
162 columns_are_known_to_be_clean_ =
true;
163 transpose_matrix_is_consistent_ =
true;
164 integer_variables_list_is_consistent_ =
true;
171 <<
"New variables can't be added to programs that already have slack "
172 "variables. Consider calling LinearProgram::DeleteSlackVariables() "
173 "before adding new variables to the problem.";
174 objective_coefficients_.push_back(0.0);
175 variable_lower_bounds_.push_back(0);
176 variable_upper_bounds_.push_back(
kInfinity);
178 variable_names_.push_back(
"");
179 transpose_matrix_is_consistent_ =
false;
180 return matrix_.AppendEmptyColumn();
186 const std::string&
name) {
187 objective_coefficients_.push_back(0.0);
188 variable_lower_bounds_.push_back(lower_bound);
189 variable_upper_bounds_.push_back(upper_bound);
190 variable_types_.push_back(is_integer_slack_variable
193 variable_names_.push_back(
name);
194 transpose_matrix_is_consistent_ =
false;
195 return matrix_.AppendEmptyColumn();
200 <<
"New constraints can't be added to programs that already have slack "
201 "variables. Consider calling LinearProgram::DeleteSlackVariables() "
202 "before adding new variables to the problem.";
203 const RowIndex row(constraint_names_.size());
204 matrix_.SetNumRows(row + 1);
205 constraint_lower_bounds_.push_back(
Fractional(0.0));
206 constraint_upper_bounds_.push_back(
Fractional(0.0));
207 constraint_names_.push_back(
"");
208 transpose_matrix_is_consistent_ =
false;
213 const absl::flat_hash_map<std::string, ColIndex>::iterator it =
214 variable_table_.find(variable_id);
215 if (it != variable_table_.end()) {
219 variable_names_[col] = variable_id;
220 variable_table_[variable_id] = col;
226 absl::string_view constraint_id) {
227 const absl::flat_hash_map<std::string, RowIndex>::iterator it =
228 constraint_table_.find(constraint_id);
229 if (it != constraint_table_.end()) {
233 constraint_names_[row] = constraint_id;
234 constraint_table_[constraint_id] = row;
240 variable_names_[col] = std::string(
name);
245 variable_types_[col] = type;
247 if (var_is_integer != var_was_integer) {
248 integer_variables_list_is_consistent_ =
false;
253 constraint_names_[row] = std::string(
name);
258 if (dcheck_bounds_) DebugCheckBoundsValid(lower_bound, upper_bound);
260 variable_lower_bounds_[col] = lower_bound;
261 variable_upper_bounds_[col] = upper_bound;
263 if (var_is_binary != var_was_binary) {
264 integer_variables_list_is_consistent_ =
false;
268void LinearProgram::UpdateAllIntegerVariableLists()
const {
269 if (integer_variables_list_is_consistent_)
return;
270 integer_variables_list_.clear();
271 binary_variables_list_.clear();
272 non_binary_variables_list_.clear();
274 for (ColIndex col(0); col < num_cols; ++col) {
276 integer_variables_list_.push_back(col);
278 binary_variables_list_.push_back(col);
280 non_binary_variables_list_.push_back(col);
284 integer_variables_list_is_consistent_ =
true;
288 UpdateAllIntegerVariableLists();
289 return integer_variables_list_;
293 UpdateAllIntegerVariableLists();
294 return binary_variables_list_;
298 UpdateAllIntegerVariableLists();
299 return non_binary_variables_list_;
311 (variable_lower_bounds_[col] >
Fractional(-1)) &&
313 (variable_upper_bounds_[col] < 2);
318 if (dcheck_bounds_) DebugCheckBoundsValid(lower_bound, upper_bound);
319 ResizeRowsIfNeeded(row);
320 constraint_lower_bounds_[row] = lower_bound;
321 constraint_upper_bounds_[row] = upper_bound;
327 ResizeRowsIfNeeded(row);
328 columns_are_known_to_be_clean_ =
false;
329 transpose_matrix_is_consistent_ =
false;
330 matrix_.mutable_column(col)->SetCoefficient(row, value);
335 objective_coefficients_[col] = value;
351 maximize_ = maximize;
355 if (columns_are_known_to_be_clean_)
return;
357 columns_are_known_to_be_clean_ =
true;
358 transpose_matrix_is_consistent_ =
false;
362 if (columns_are_known_to_be_clean_)
return true;
363 columns_are_known_to_be_clean_ = matrix_.IsCleanedUp();
364 return columns_are_known_to_be_clean_;
368 return col >= variable_names_.
size() || variable_names_[col].empty()
369 ? absl::StrFormat(
"c%d", col.value())
370 : variable_names_[col];
374 return row >= constraint_names_.
size() || constraint_names_[row].empty()
375 ? absl::StrFormat(
"r%d", row.value())
376 : constraint_names_[row];
380 return variable_types_[col];
384 if (!transpose_matrix_is_consistent_) {
385 transpose_matrix_.PopulateFromTranspose(matrix_);
386 transpose_matrix_is_consistent_ =
true;
388 DCHECK_EQ(transpose_matrix_.
num_rows().value(), matrix_.
num_cols().value());
389 DCHECK_EQ(transpose_matrix_.
num_cols().value(), matrix_.
num_rows().value());
390 return transpose_matrix_;
394 if (!transpose_matrix_is_consistent_) {
395 transpose_matrix_.PopulateFromTranspose(matrix_);
400 transpose_matrix_is_consistent_ =
false;
401 return &transpose_matrix_;
405 DCHECK_EQ(transpose_matrix_.
num_rows().value(), matrix_.
num_cols().value());
406 DCHECK_EQ(transpose_matrix_.num_cols().value(), matrix_.num_rows().value());
407 matrix_.PopulateFromTranspose(transpose_matrix_);
408 transpose_matrix_is_consistent_ =
true;
412 transpose_matrix_.
Clear();
413 transpose_matrix_is_consistent_ =
false;
417 return matrix_.
column(col);
421 columns_are_known_to_be_clean_ =
false;
422 transpose_matrix_is_consistent_ =
false;
423 return matrix_.mutable_column(col);
427 ColIndex col)
const {
435 matrix_.ComputeMinAndMaxMagnitudes(&min_magnitude, &max_magnitude);
436 return absl::StrFormat(
437 "%d rows, %d columns, %d entries with magnitude in [%e, %e]",
440 static_cast<int64_t
>(
num_entries().value()), min_magnitude,
446template <
typename FractionalValues>
447void UpdateStats(
const FractionalValues& values, int64_t* num_non_zeros,
451 *min_value = std::min(*min_value, v);
452 *max_value = std::max(*max_value, v);
460 int64_t num_non_zeros = 0;
463 UpdateStats(objective_coefficients_, &num_non_zeros, &min_value, &max_value);
464 if (num_non_zeros == 0) {
465 return "No objective term. This is a pure feasibility problem.";
467 return absl::StrFormat(
"%d non-zeros, range [%e, %e]", num_non_zeros,
468 min_value, max_value);
473 int64_t num_non_zeros = 0;
476 UpdateStats(variable_lower_bounds_, &num_non_zeros, &min_value, &max_value);
477 UpdateStats(variable_upper_bounds_, &num_non_zeros, &min_value, &max_value);
478 UpdateStats(constraint_lower_bounds_, &num_non_zeros, &min_value, &max_value);
479 UpdateStats(constraint_upper_bounds_, &num_non_zeros, &min_value, &max_value);
480 if (num_non_zeros == 0) {
481 return "All variables/constraints bounds are zero or +/- infinity.";
483 return absl::StrFormat(
"%d non-zeros, range [%e, %e]", num_non_zeros,
484 min_value, max_value);
493 for (ColIndex col = ColIndex(0); col < num_cols; ++col) {
497 if (lb_error > absolute_tolerance || ub_error > absolute_tolerance) {
511 for (RowIndex row = RowIndex(0); row < num_rows; ++row) {
517 if (lb_error > absolute_tolerance || ub_error > absolute_tolerance) {
531 if (fractionality > absolute_tolerance)
return false;
548 for (RowIndex row = RowIndex(0); row < num_rows; ++row) {
553 (*solution)[slack_variable] = -sum;
569 std::string output = maximize_ ?
"max:" :
"min:";
570 if (objective_offset_ != 0.0) {
574 for (ColIndex col(0); col < num_cols; ++col) {
580 output.append(
";\n");
585 for (RowIndex row(0); row < num_rows; ++row) {
590 if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
595 for (
const auto& entry : transpose.column(
RowToColIndex(row))) {
600 if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
603 }
else if (lower_bound == upper_bound) {
617 for (ColIndex col(0); col < num_cols; ++col) {
620 if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
625 if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
628 }
else if (lower_bound == upper_bound) {
644 if (!integer_variables.empty()) {
646 for (ColIndex col : integer_variables) {
659 for (ColIndex col(0); col < variable_values.size(); ++col) {
660 if (!output.empty()) absl::StrAppend(&output,
", ");
662 (variable_values[col]));
668 return ProblemStatFormatter(
669 "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
674 return ProblemStatFormatter(
675 "Number of rows : %d\n"
676 "Number of variables in file : %d\n"
677 "Number of entries (non-zeros) : %d\n"
678 "Number of entries in the objective : %d\n"
679 "Number of entries in the right-hand side : %d\n"
680 "Number of <= constraints : %d\n"
681 "Number of >= constraints : %d\n"
682 "Number of = constraints : %d\n"
683 "Number of range constraints : %d\n"
684 "Number of non-negative variables : %d\n"
685 "Number of boxed variables : %d\n"
686 "Number of free variables : %d\n"
687 "Number of fixed variables : %d\n"
688 "Number of other variables : %d\n"
689 "Number of integer variables : %d\n"
690 "Number of binary variables : %d\n"
691 "Number of non-binary integer variables : %d\n"
692 "Number of continuous variables : %d\n");
696 return NonZeroStatFormatter(
"%.2f%%,%d,%.2f,%.2f,%d,%.2f,%.2f");
700 return NonZeroStatFormatter(
701 "Fill rate : %.2f%%\n"
702 "Entries in row (Max / average / std. dev.) : %d / %.2f / %.2f\n"
703 "Entries in column (Max / average / std. dev.): %d / %.2f / %.2f\n");
707 bool detect_integer_constraints) {
723 detect_integer_constraints);
724 if (detect_integer_constraints) {
728 for (
const SparseColumn::Entry& entry : column) {
729 const RowIndex row = entry.row();
730 has_integer_slack_variable[row] =
731 has_integer_slack_variable[row] && is_integer_variable &&
732 round(entry.coefficient()) == entry.coefficient();
742 slack_variable_index < original_num_variables) {
747 has_integer_slack_variable[row], -constraint_upper_bounds_[row],
748 -constraint_lower_bounds_[row], absl::StrCat(
"s", row.value()));
753 columns_are_known_to_be_clean_ =
true;
754 transpose_matrix_is_consistent_ =
false;
756 first_slack_variable_ = original_num_variables;
761 return first_slack_variable_;
765 DCHECK_GE(row, RowIndex(0));
775 const ColIndex dual_num_variables = dual.num_variables();
776 const RowIndex dual_num_constraints = dual.num_constraints();
791 for (RowIndex dual_row(0); dual_row < dual_num_constraints; ++dual_row) {
794 const Fractional lower_bound = dual.constraint_lower_bounds()[dual_row];
795 const Fractional upper_bound = dual.constraint_upper_bounds()[dual_row];
796 if (lower_bound == upper_bound) {
809 LOG(DFATAL) <<
"PopulateFromDual() was called with a program "
810 <<
"containing free constraints.";
814 for (ColIndex dual_col(0); dual_col < dual_num_variables; ++dual_col) {
815 const Fractional lower_bound = dual.variable_lower_bounds()[dual_col];
825 for (ColIndex dual_col(0); dual_col < dual_num_variables; ++dual_col) {
826 const Fractional upper_bound = dual.variable_upper_bounds()[dual_col];
836 for (ColIndex dual_col(0); dual_col < dual_num_variables; ++dual_col) {
839 dual.GetObjectiveCoefficientForMinimizationVersion(dual_col);
841 for (
const SparseColumn::Entry e : dual.GetSparseColumn(dual_col)) {
842 const RowIndex dual_row = e.row();
849 duplicated_rows->assign(dual_num_constraints,
kInvalidCol);
850 for (RowIndex dual_row(0); dual_row < dual_num_constraints; ++dual_row) {
851 const Fractional lower_bound = dual.constraint_lower_bounds()[dual_row];
852 const Fractional upper_bound = dual.constraint_upper_bounds()[dual_row];
853 if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
860 matrix_.mutable_column(col)->PopulateFromSparseVector(
862 (*duplicated_rows)[dual_row] = col;
867 columns_are_known_to_be_clean_ =
true;
868 transpose_matrix_is_consistent_ =
false;
873 matrix_.PopulateFromSparseMatrix(linear_program.matrix_);
874 if (linear_program.transpose_matrix_is_consistent_) {
875 transpose_matrix_is_consistent_ =
true;
876 transpose_matrix_.PopulateFromSparseMatrix(
877 linear_program.transpose_matrix_);
879 transpose_matrix_is_consistent_ =
false;
880 transpose_matrix_.
Clear();
883 constraint_lower_bounds_ = linear_program.constraint_lower_bounds_;
884 constraint_upper_bounds_ = linear_program.constraint_upper_bounds_;
885 constraint_names_ = linear_program.constraint_names_;
886 constraint_table_.clear();
888 PopulateNameObjectiveAndVariablesFromLinearProgram(linear_program);
889 first_slack_variable_ = linear_program.first_slack_variable_;
895 DCHECK(lp.IsCleanedUp());
896 DCHECK_EQ(row_permutation.size(), lp.num_constraints());
897 DCHECK_EQ(col_permutation.
size(), lp.num_variables());
898 DCHECK_EQ(lp.GetFirstSlackVariable(),
kInvalidCol);
904 matrix_.PopulateFromPermutedMatrix(lp.matrix_, row_permutation,
905 inverse_col_permutation);
910 &constraint_lower_bounds_);
912 &constraint_upper_bounds_);
916 &objective_coefficients_);
918 &variable_lower_bounds_);
920 &variable_upper_bounds_);
922 integer_variables_list_is_consistent_ =
false;
926 constraint_names_.resize(lp.num_constraints());
927 for (RowIndex old_row(0); old_row < lp.num_constraints(); ++old_row) {
928 const RowIndex new_row = row_permutation[old_row];
929 constraint_names_[new_row] = lp.constraint_names_[old_row];
931 variable_names_.resize(lp.num_variables());
932 for (ColIndex old_col(0); old_col < lp.num_variables(); ++old_col) {
933 const ColIndex new_col = col_permutation[old_col];
934 variable_names_[new_col] = lp.variable_names_[old_col];
938 maximize_ = lp.maximize_;
939 objective_offset_ = lp.objective_offset_;
940 objective_scaling_factor_ = lp.objective_scaling_factor_;
948 transpose_matrix_is_consistent_ =
false;
949 transpose_matrix_.Clear();
951 constraint_lower_bounds_.clear();
952 constraint_upper_bounds_.clear();
953 constraint_names_.clear();
954 constraint_table_.clear();
956 PopulateNameObjectiveAndVariablesFromLinearProgram(linear_program);
959void LinearProgram::PopulateNameObjectiveAndVariablesFromLinearProgram(
961 objective_coefficients_ = linear_program.objective_coefficients_;
962 variable_lower_bounds_ = linear_program.variable_lower_bounds_;
963 variable_upper_bounds_ = linear_program.variable_upper_bounds_;
964 variable_names_ = linear_program.variable_names_;
965 variable_types_ = linear_program.variable_types_;
966 integer_variables_list_is_consistent_ =
967 linear_program.integer_variables_list_is_consistent_;
968 integer_variables_list_ = linear_program.integer_variables_list_;
969 binary_variables_list_ = linear_program.binary_variables_list_;
970 non_binary_variables_list_ = linear_program.non_binary_variables_list_;
971 variable_table_.clear();
973 maximize_ = linear_program.maximize_;
974 objective_offset_ = linear_program.objective_offset_;
975 objective_scaling_factor_ = linear_program.objective_scaling_factor_;
976 columns_are_known_to_be_clean_ =
977 linear_program.columns_are_known_to_be_clean_;
978 name_ = linear_program.name_;
982 const SparseMatrix& coefficients,
const DenseColumn& left_hand_sides,
985 const RowIndex num_new_constraints = coefficients.num_rows();
987 DCHECK_EQ(num_new_constraints, left_hand_sides.size());
988 DCHECK_EQ(num_new_constraints, right_hand_sides.
size());
989 DCHECK_EQ(num_new_constraints, names.
size());
991 matrix_.AppendRowsFromSparseMatrix(coefficients);
992 transpose_matrix_is_consistent_ =
false;
993 transpose_matrix_.Clear();
994 columns_are_known_to_be_clean_ =
false;
997 constraint_lower_bounds_.insert(constraint_lower_bounds_.end(),
998 left_hand_sides.begin(),
999 left_hand_sides.end());
1000 constraint_upper_bounds_.insert(constraint_upper_bounds_.end(),
1001 right_hand_sides.begin(),
1002 right_hand_sides.end());
1003 constraint_names_.insert(constraint_names_.end(), names.begin(), names.end());
1010 bool detect_integer_constraints_for_slack) {
1011 AddConstraints(coefficients, left_hand_sides, right_hand_sides, names);
1016 const DenseRow& variable_lower_bounds,
1022 DenseRow new_lower_bounds(num_vars, 0);
1023 DenseRow new_upper_bounds(num_vars, 0);
1024 for (ColIndex i(0); i < num_vars; ++i) {
1029 if (new_lower_bound > new_upper_bound) {
1032 new_lower_bounds[i] = new_lower_bound;
1033 new_upper_bounds[i] = new_upper_bound;
1035 variable_lower_bounds_.swap(new_lower_bounds);
1036 variable_upper_bounds_.swap(new_upper_bounds);
1041 matrix_.
Swap(&linear_program->matrix_);
1042 transpose_matrix_.Swap(&linear_program->transpose_matrix_);
1044 constraint_lower_bounds_.swap(linear_program->constraint_lower_bounds_);
1045 constraint_upper_bounds_.swap(linear_program->constraint_upper_bounds_);
1046 constraint_names_.swap(linear_program->constraint_names_);
1048 objective_coefficients_.swap(linear_program->objective_coefficients_);
1049 variable_lower_bounds_.swap(linear_program->variable_lower_bounds_);
1050 variable_upper_bounds_.swap(linear_program->variable_upper_bounds_);
1051 variable_names_.swap(linear_program->variable_names_);
1052 variable_types_.swap(linear_program->variable_types_);
1053 integer_variables_list_.swap(linear_program->integer_variables_list_);
1054 binary_variables_list_.swap(linear_program->binary_variables_list_);
1055 non_binary_variables_list_.swap(linear_program->non_binary_variables_list_);
1057 variable_table_.swap(linear_program->variable_table_);
1058 constraint_table_.swap(linear_program->constraint_table_);
1060 std::swap(maximize_, linear_program->maximize_);
1061 std::swap(objective_offset_, linear_program->objective_offset_);
1062 std::swap(objective_scaling_factor_,
1063 linear_program->objective_scaling_factor_);
1064 std::swap(columns_are_known_to_be_clean_,
1065 linear_program->columns_are_known_to_be_clean_);
1066 std::swap(transpose_matrix_is_consistent_,
1067 linear_program->transpose_matrix_is_consistent_);
1068 std::swap(integer_variables_list_is_consistent_,
1069 linear_program->integer_variables_list_is_consistent_);
1070 name_.swap(linear_program->name_);
1071 std::swap(first_slack_variable_, linear_program->first_slack_variable_);
1075 if (columns_to_delete.empty())
return;
1076 integer_variables_list_is_consistent_ =
false;
1079 ColIndex new_index(0);
1080 for (ColIndex col(0); col < num_cols; ++col) {
1081 permutation[col] = new_index;
1082 if (col >= columns_to_delete.
size() || !columns_to_delete[col]) {
1083 objective_coefficients_[new_index] = objective_coefficients_[col];
1084 variable_lower_bounds_[new_index] = variable_lower_bounds_[col];
1085 variable_upper_bounds_[new_index] = variable_upper_bounds_[col];
1086 variable_names_[new_index] = variable_names_[col];
1087 variable_types_[new_index] = variable_types_[col];
1095 objective_coefficients_.
resize(new_index, 0.0);
1096 variable_lower_bounds_.
resize(new_index, 0.0);
1097 variable_upper_bounds_.
resize(new_index, 0.0);
1099 variable_names_.
resize(new_index,
"");
1102 absl::flat_hash_map<std::string, ColIndex>::iterator it =
1103 variable_table_.begin();
1104 while (it != variable_table_.end()) {
1105 const ColIndex col = it->second;
1106 if (col >= columns_to_delete.
size() || !columns_to_delete[col]) {
1107 it->second = permutation[col];
1111 variable_table_.erase(it++);
1116 if (transpose_matrix_is_consistent_) {
1117 transpose_matrix_.DeleteRows(
1127 for (ColIndex slack_variable = first_slack_variable_;
1128 slack_variable < matrix_.num_cols(); ++slack_variable) {
1129 const SparseColumn& column = matrix_.column(slack_variable);
1134 const RowIndex row = column.
EntryRow(EntryIndex(0));
1135 DCHECK_EQ(constraint_lower_bounds_[row], 0.0);
1136 DCHECK_EQ(constraint_upper_bounds_[row], 0.0);
1138 -variable_lower_bounds_[slack_variable]);
1139 slack_variables[slack_variable] =
true;
1150template <
typename FractionalRange>
1151void UpdateMinAndMaxMagnitude(
const FractionalRange& range,
1155 const Fractional magnitude = std::abs(value);
1156 if (magnitude == 0 || magnitude ==
kInfinity)
continue;
1157 *min_magnitude = std::min(*min_magnitude, magnitude);
1158 *max_magnitude = std::max(*max_magnitude, magnitude);
1163 std::vector<Fractional> median;
1165 if (value == 0.0)
continue;
1166 median.push_back(std::abs(value));
1168 if (median.empty())
return 1.0;
1169 std::sort(median.begin(), median.end());
1170 return median[median.size() / 2];
1175 int num_non_zeros = 0;
1177 if (value == 0.0)
continue;
1179 mean += std::abs(value);
1181 if (num_non_zeros == 0.0)
return 1.0;
1182 return mean /
static_cast<Fractional>(num_non_zeros);
1187 if (min_magnitude > 1.0 && min_magnitude <
kInfinity) {
1188 return min_magnitude;
1189 }
else if (max_magnitude > 0.0 && max_magnitude < 1.0) {
1190 return max_magnitude;
1208 cost_scaling_factor =
1209 ComputeDivisorSoThatRangeContainsOne(min_magnitude, max_magnitude);
1218 if (cost_scaling_factor != 1.0) {
1227 VLOG(1) <<
"Objective magnitude range is [" << min_magnitude <<
", "
1228 << max_magnitude <<
"] (dividing by " << cost_scaling_factor <<
").";
1229 return cost_scaling_factor;
1244 ComputeDivisorSoThatRangeContainsOne(min_magnitude, max_magnitude);
1245 if (bound_scaling_factor != 1.0) {
1247 bound_scaling_factor);
1261 VLOG(1) <<
"Bounds magnitude range is [" << min_magnitude <<
", "
1262 << max_magnitude <<
"] (dividing bounds by " << bound_scaling_factor
1264 return bound_scaling_factor;
1268 if (rows_to_delete.empty())
return;
1274 RowIndex new_index(0);
1275 for (RowIndex row(0); row < num_rows; ++row) {
1276 if (row >= rows_to_delete.size() || !rows_to_delete[row]) {
1277 constraint_lower_bounds_[new_index] = constraint_lower_bounds_[row];
1278 constraint_upper_bounds_[new_index] = constraint_upper_bounds_[row];
1279 constraint_names_[new_index].swap(constraint_names_[row]);
1280 permutation[row] = new_index;
1286 constraint_lower_bounds_.
resize(new_index, 0.0);
1287 constraint_upper_bounds_.
resize(new_index, 0.0);
1288 constraint_names_.
resize(new_index,
"");
1294 absl::flat_hash_map<std::string, RowIndex>::iterator it =
1295 constraint_table_.begin();
1296 while (it != constraint_table_.end()) {
1297 const RowIndex row = it->second;
1299 it->second = permutation[row];
1303 constraint_table_.erase(it++);
1308 if (transpose_matrix_is_consistent_) {
1309 transpose_matrix_.DeleteColumns(
1315 if (!
IsFinite(objective_offset_))
return false;
1316 if (std::abs(objective_offset_) > max_valid_magnitude)
return false;
1318 if (!
IsFinite(objective_scaling_factor_))
return false;
1319 if (objective_scaling_factor_ == 0.0)
return false;
1320 if (std::abs(objective_scaling_factor_) > max_valid_magnitude)
return false;
1323 for (ColIndex col(0); col < num_cols; ++col) {
1327 if (
IsFinite(lb) && std::abs(lb) > max_valid_magnitude)
return false;
1328 if (
IsFinite(ub) && std::abs(ub) > max_valid_magnitude)
return false;
1338 if (!
IsFinite(e.coefficient()))
return false;
1339 if (std::abs(e.coefficient()) > max_valid_magnitude)
return false;
1342 if (constraint_upper_bounds_.size() != constraint_lower_bounds_.size()) {
1345 for (RowIndex row(0); row < constraint_lower_bounds_.size(); ++row) {
1349 if (
IsFinite(lb) && std::abs(lb) > max_valid_magnitude)
return false;
1350 if (
IsFinite(ub) && std::abs(ub) > max_valid_magnitude)
return false;
1355std::string LinearProgram::ProblemStatFormatter(
1356 const absl::string_view format)
const {
1357 int num_objective_non_zeros = 0;
1358 int num_non_negative_variables = 0;
1359 int num_boxed_variables = 0;
1360 int num_free_variables = 0;
1361 int num_fixed_variables = 0;
1362 int num_other_variables = 0;
1364 for (ColIndex col(0); col < num_cols; ++col) {
1366 ++num_objective_non_zeros;
1371 const bool lower_bounded = (lower_bound != -
kInfinity);
1372 const bool upper_bounded = (upper_bound !=
kInfinity);
1374 if (!lower_bounded && !upper_bounded) {
1375 ++num_free_variables;
1376 }
else if (lower_bound == 0.0 && !upper_bounded) {
1377 ++num_non_negative_variables;
1378 }
else if (!upper_bounded || !lower_bounded) {
1379 ++num_other_variables;
1380 }
else if (lower_bound == upper_bound) {
1381 ++num_fixed_variables;
1383 ++num_boxed_variables;
1387 int num_range_constraints = 0;
1388 int num_less_than_constraints = 0;
1389 int num_greater_than_constraints = 0;
1390 int num_equal_constraints = 0;
1391 int num_rhs_non_zeros = 0;
1393 for (RowIndex row(0); row < num_rows; ++row) {
1396 if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
1399 ++num_range_constraints;
1402 if (lower_bound == upper_bound) {
1403 ++num_equal_constraints;
1404 if (lower_bound != 0) {
1405 ++num_rhs_non_zeros;
1410 ++num_less_than_constraints;
1411 if (upper_bound != 0) {
1412 ++num_rhs_non_zeros;
1417 ++num_greater_than_constraints;
1418 if (lower_bound != 0) {
1419 ++num_rhs_non_zeros;
1423 LOG(DFATAL) <<
"There is a bug since all possible cases for the row bounds "
1424 "should have been accounted for. row="
1431 const int num_continuous_variables =
1433 auto format_runtime =
1434 absl::ParsedFormat<
'd',
'd',
'd',
'd',
'd',
'd',
'd',
'd',
'd',
'd',
'd',
1435 'd',
'd',
'd',
'd',
'd',
'd',
'd'>::New(format);
1436 CHECK(format_runtime);
1437 return absl::StrFormat(
1440 num_objective_non_zeros, num_rhs_non_zeros, num_less_than_constraints,
1441 num_greater_than_constraints, num_equal_constraints,
1442 num_range_constraints, num_non_negative_variables, num_boxed_variables,
1443 num_free_variables, num_fixed_variables, num_other_variables,
1444 num_integer_variables, num_binary_variables, num_non_binary_variables,
1445 num_continuous_variables);
1448std::string LinearProgram::NonZeroStatFormatter(
1449 const absl::string_view format)
const {
1450 StrictITIVector<RowIndex, EntryIndex> num_entries_in_row(
num_constraints(),
1452 StrictITIVector<ColIndex, EntryIndex> num_entries_in_column(
num_variables(),
1456 for (ColIndex col(0); col < num_cols; ++col) {
1459 num_entries_in_column[col] = sparse_column.num_entries();
1460 for (
const SparseColumn::Entry e : sparse_column) {
1461 ++num_entries_in_row[e.row()];
1469 const double fill_rate = 100.0 *
static_cast<double>(
num_entries.value()) /
1470 static_cast<double>(height * width);
1472 auto format_runtime =
1473 absl::ParsedFormat<'f', 'd', 'f', 'f', 'd', 'f', 'f'>::New(format);
1474 return absl::StrFormat(
1475 *format_runtime, fill_rate, GetMaxElement(num_entries_in_row).value(),
1476 Average(num_entries_in_row), StandardDeviation(num_entries_in_row),
1477 GetMaxElement(num_entries_in_column).value(),
1478 Average(num_entries_in_column), StandardDeviation(num_entries_in_column));
1481void LinearProgram::ResizeRowsIfNeeded(RowIndex row) {
1484 transpose_matrix_is_consistent_ =
false;
1485 matrix_.SetNumRows(row + 1);
1486 constraint_lower_bounds_.resize(row + 1,
Fractional(0.0));
1487 constraint_upper_bounds_.resize(row + 1,
Fractional(0.0));
1488 constraint_names_.resize(row + 1,
"");
1493 for (RowIndex constraint(0); constraint <
num_constraints(); ++constraint) {
1494 if (constraint_lower_bounds_[constraint] != 0.0 ||
1495 constraint_upper_bounds_[constraint] != 0.0) {
1499 const ColIndex num_slack_variables =
1508 if ((
IsFinite(variable_lower_bounds_[col]) &&
1510 (
IsFinite(variable_upper_bounds_[col]) &&
1512 VLOG(1) <<
"Bounds of variable " << col.value() <<
" are non-integer ("
1513 << variable_lower_bounds_[col] <<
", "
1514 << variable_upper_bounds_[col] <<
").";
1528 bool integer_constraint =
true;
1531 integer_constraint =
false;
1537 if (std::round(var.coefficient()) != var.coefficient()) {
1538 integer_constraint =
false;
1542 if (integer_constraint) {
1543 if ((
IsFinite(constraint_lower_bounds_[row]) &&
1546 (
IsFinite(constraint_upper_bounds_[row]) &&
1549 VLOG(1) <<
"Bounds of constraint " << row.value()
1550 <<
" are non-integer (" << constraint_lower_bounds_[row] <<
", "
1551 << constraint_upper_bounds_[row] <<
").";
1560 int64_t num_removed_objective_entries = 0;
1561 int64_t num_removed_matrix_entries = 0;
1562 for (ColIndex col(0); col < matrix_.num_cols(); ++col) {
1563 const int64_t old_size = matrix_.column(col).num_entries().value();
1564 matrix_.mutable_column(col)->RemoveNearZeroEntries(threshold);
1565 num_removed_matrix_entries +=
1566 old_size - matrix_.column(col).num_entries().value();
1567 if (std::abs(objective_coefficients_[col]) <= threshold) {
1568 objective_coefficients_[col] = 0.0;
1569 ++num_removed_objective_entries;
1572 if (num_removed_matrix_entries > 0) {
1573 transpose_matrix_is_consistent_ =
false;
1574 VLOG(1) <<
"Removed " << num_removed_matrix_entries <<
" matrix entries.";
1576 if (num_removed_objective_entries > 0) {
1577 VLOG(1) <<
"Removed " << num_removed_objective_entries
1578 <<
" objective coefficients.";
1588 absl::StrAppendFormat(&s,
"\n Var #%d: %s %g", col.value(),
1592 s +=
"\n------------------------------";
1594 absl::StrAppendFormat(&s,
"\n Constraint #%d: %s %g", row.value(),
static constexpr CostScalingAlgorithm MEDIAN_COST_SCALING
GlopParameters_CostScalingAlgorithm CostScalingAlgorithm
static constexpr CostScalingAlgorithm CONTAIN_ONE_COST_SCALING
static constexpr CostScalingAlgorithm MEAN_COST_SCALING
static constexpr CostScalingAlgorithm NO_COST_SCALING
bool BoundsOfIntegerConstraintsAreInteger(Fractional tolerance) const
std::string GetDimensionString() const
A short string with the problem dimension.
const DenseRow & objective_coefficients() const
Returns the objective coefficients (or cost) of variables as a row vector.
void Clear()
Clears, i.e. reset the object to its initial value.
void SetConstraintName(RowIndex row, absl::string_view name)
const std::vector< ColIndex > & NonBinaryVariablesList() const
@ INTEGER
The variable must only take integer values.
std::string GetPrettyNonZeroStats() const
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
void RemoveNearZeroEntries(Fractional threshold)
Removes objective and coefficient with magnitude <= threshold.
bool BoundsOfIntegerVariablesAreInteger(Fractional tolerance) const
void ComputeSlackVariableValues(DenseRow *solution) const
bool IsValid(Fractional max_valid_magnitude=kInfinity) const
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
void PopulateFromPermutedLinearProgram(const LinearProgram &lp, const RowPermutation &row_permutation, const ColumnPermutation &col_permutation)
void PopulateFromLinearProgramVariables(const LinearProgram &linear_program)
bool SolutionIsInteger(const DenseRow &solution, Fractional absolute_tolerance) const
void SetObjectiveOffset(Fractional objective_offset)
std::string GetConstraintName(RowIndex row) const
void SetObjectiveCoefficient(ColIndex col, Fractional value)
bool SolutionIsLPFeasible(const DenseRow &solution, Fractional absolute_tolerance) const
std::string GetPrettyProblemStats() const
const std::string & name() const
bool SolutionIsWithinVariableBounds(const DenseRow &solution, Fractional absolute_tolerance) const
Checks if each variable respects its bounds, nothing else.
Fractional RemoveObjectiveScalingAndOffset(Fractional value) const
void SetObjectiveScalingFactor(Fractional objective_scaling_factor)
bool IsInEquationForm() const
const DenseColumn & constraint_lower_bounds() const
const SparseMatrix & GetTransposeSparseMatrix() const
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
void ClearTransposeMatrix()
Release the memory used by the transpose matrix.
const DenseRow & variable_lower_bounds() const
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
ColIndex GetSlackVariable(RowIndex row) const
const std::vector< ColIndex > & BinaryVariablesList() const
EntryIndex num_entries() const
Returns the number of entries in the linear program matrix.
SparseMatrix * GetMutableTransposeSparseMatrix()
void DeleteSlackVariables()
SparseColumn * GetMutableSparseColumn(ColIndex col)
Gets a pointer to the underlying SparseColumn with the given index.
void AddConstraintsWithSlackVariables(const SparseMatrix &coefficients, const DenseColumn &left_hand_sides, const DenseColumn &right_hand_sides, const StrictITIVector< RowIndex, std::string > &names, bool detect_integer_constraints_for_slack)
const std::vector< ColIndex > & IntegerVariablesList() const
std::string GetProblemStats() const
bool IsVariableBinary(ColIndex col) const
Returns whether the variable at column col must take binary values or not.
Fractional ScaleObjective(GlopParameters::CostScalingAlgorithm method)
void Swap(LinearProgram *linear_program)
void DeleteRows(const DenseBooleanColumn &rows_to_delete)
void SetVariableType(ColIndex col, VariableType type)
Set the type of the variable.
ColIndex FindOrCreateVariable(absl::string_view variable_id)
std::string DumpSolution(const DenseRow &variable_values) const
bool SolutionIsMIPFeasible(const DenseRow &solution, Fractional absolute_tolerance) const
Tests if the solution is both LP-feasible and integer within the tolerance.
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Defines the coefficient for col / row.
void UseTransposeMatrixAsReference()
std::string GetVariableName(ColIndex col) const
const DenseRow & variable_upper_bounds() const
void AddConstraints(const SparseMatrix &coefficients, const DenseColumn &left_hand_sides, const DenseColumn &right_hand_sides, const StrictITIVector< RowIndex, std::string > &names)
void SetVariableName(ColIndex col, absl::string_view name)
ColIndex CreateNewSlackVariable(bool is_integer_slack_variable, Fractional lower_bound, Fractional upper_bound, const std::string &name)
VariableType GetVariableType(ColIndex col) const
Returns the type of variable.
const DenseColumn & constraint_upper_bounds() const
std::string GetNonZeroStats() const
RowIndex CreateNewConstraint()
const SparseColumn & GetSparseColumn(ColIndex col) const
bool IsVariableInteger(ColIndex col) const
Returns whether the variable at column col is constrained to be integer.
void PopulateFromLinearProgram(const LinearProgram &linear_program)
Populates the calling object with the given LinearProgram.
bool UpdateVariableBoundsToIntersection(const DenseRow &variable_lower_bounds, const DenseRow &variable_upper_bounds)
Fractional objective_scaling_factor() const
Fractional ApplyObjectiveScalingAndOffset(Fractional value) const
ColIndex num_variables() const
Returns the number of variables.
ColIndex GetFirstSlackVariable() const
std::string GetObjectiveStatsString() const
A short line with some stats on the problem coefficients.
ColIndex CreateNewVariable()
Fractional objective_offset() const
Returns the objective offset and scaling factor.
RowIndex num_constraints() const
Returns the number of constraints.
std::string GetBoundsStatsString() const
RowIndex FindOrCreateConstraint(absl::string_view constraint_id)
void PopulateFromDual(const LinearProgram &dual, RowToColMapping *duplicated_rows)
void SetMaximizationProblem(bool maximize)
void PopulateFromInverse(const Permutation &inverse)
RowIndex EntryRow(EntryIndex i) const
Use a separate API to get the row and coefficient of entry i.
const SparseColumn & column(ColIndex col) const
Access the underlying sparse columns.
void Swap(SparseMatrix *matrix)
void DeleteRows(RowIndex num_rows, const RowPermutation &permutation)
RowIndex num_rows() const
Return the matrix dimension.
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
ColIndex num_cols() const
EntryIndex num_entries() const
Note this method can only be used when the vector has no duplicates.
void resize(IntType size)
constexpr ColIndex kInvalidCol(-1)
StrictITIVector< RowIndex, ColIndex > RowToColMapping
bool AreBoundsValid(Fractional lower_bound, Fractional upper_bound)
StrictITIVector< RowIndex, bool > DenseBooleanColumn
Column of booleans.
Permutation< ColIndex > ColumnPermutation
std::string Stringify(const Fractional x, bool fraction)
Permutation< RowIndex > RowPermutation
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
std::string GetVariableStatusString(VariableStatus status)
Returns the string representation of the VariableStatus enum.
Index ColToIntIndex(ColIndex col)
Get the integer index corresponding to the col.
constexpr Fractional kEpsilon
Epsilon for type Fractional, i.e. the smallest e such that 1.0 + e != 1.0 .
bool IsRightMostSquareMatrixIdentity(const SparseMatrix &matrix)
Returns true iff the rightmost square matrix is an identity matrix.
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
bool IsFinite(Fractional value)
constexpr RowIndex kInvalidRow(-1)
VariableType
Different types of variables.
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
std::string GetConstraintStatusString(ConstraintStatus status)
Returns the string representation of the ConstraintStatus enum.
std::string StringifyMonomial(const Fractional a, absl::string_view x, bool fraction)
constexpr Fractional kInfinity
Infinity for type Fractional.
void ApplyPermutation(const Permutation< IndexType > &perm, const ITIVectorType &b, ITIVectorType *result)
Fractional PartialScalarProduct(const DenseRowOrColumn &u, const SparseColumn &v, int max_index)
Computes a scalar product for entries with index not greater than max_index.
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
StrictITIVector< ColIndex, bool > DenseBooleanRow
Row of booleans.
std::string GetProblemStatusString(ProblemStatus problem_status)
Returns the string representation of the ProblemStatus enum.
Index RowToIntIndex(RowIndex row)
Get the integer index corresponding to the row.
dual_gradient T(y - `dual_solution`) class DiagonalTrustRegionProblemFromQp
In SWIG mode, we don't want anything besides these top-level includes.
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
static constexpr double kInfinity
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
util_intops::StrongVector< ColumnEntryIndex, ElementIndex > SparseColumn
ConstraintStatusColumn constraint_statuses
std::string DebugString() const
VariableStatusRow variable_statuses
ProblemStatus status
The solution status.