23#include "absl/base/optimization.h"
24#include "absl/log/check.h"
25#include "absl/log/log.h"
40 : max_scaled_abs_cost_(0.0),
41 bixby_column_comparator_(*this),
42 triangular_column_comparator_(*this),
43 compact_matrix_(compact_matrix),
44 objective_(objective),
45 lower_bound_(lower_bound),
46 upper_bound_(upper_bound),
47 variable_type_(variable_type) {}
53 const RowIndex num_rows = compact_matrix_.num_rows();
56 DCHECK_EQ(num_rows, basis->
size());
58 for (RowIndex row(0); row < num_rows; ++row) {
60 can_be_replaced[row] =
true;
61 has_zero_coefficient[row] =
true;
70 std::vector<ColIndex> candidates;
75 for (
int i = 0; i < candidates.size(); ++i) {
76 bool enter_basis =
false;
77 const ColIndex candidate_col_index = candidates[i];
78 const auto& candidate_col = compact_matrix_.column(candidate_col_index);
86 RowIndex candidate_row;
88 candidate_col, has_zero_coefficient, &candidate_row);
90 if (candidate_coeff > kBixbyHighThreshold) {
92 }
else if (
IsDominated(candidate_col, scaled_diagonal_abs)) {
95 if (candidate_coeff != 0.0) {
101 can_be_replaced[candidate_row] =
false;
104 scaled_diagonal_abs[candidate_row] =
105 kBixbyLowThreshold * std::abs(candidate_coeff);
106 (*basis)[candidate_row] = candidate_col_index;
113 return GetMarosBasis<false>(num_cols, basis);
118 return GetMarosBasis<true>(num_cols, basis);
123 return CompleteTriangularBasis<false>(num_cols, basis);
128 return CompleteTriangularBasis<true>(num_cols, basis);
131template <
bool only_allow_zero_cost_column>
132void InitialBasis::CompleteTriangularBasis(ColIndex num_cols,
135 const RowIndex num_rows = compact_matrix_.
num_rows();
137 DCHECK_EQ(num_rows, basis->
size());
139 for (RowIndex row(0); row < num_rows; ++row) {
141 can_be_replaced[row] =
true;
146 MatrixNonZeroPattern residual_pattern;
147 residual_pattern.Reset(num_rows, num_cols);
148 for (ColIndex col(0); col < num_cols; ++col) {
149 if (only_allow_zero_cost_column && objective_[col] != 0.0)
continue;
151 if (can_be_replaced[e.row()]) {
152 residual_pattern.AddEntry(e.row(), col);
159 std::vector<ColIndex> residual_singleton_column;
160 max_scaled_abs_cost_ = 0.0;
161 for (ColIndex col(0); col < num_cols; ++col) {
162 max_scaled_abs_cost_ =
163 std::max(max_scaled_abs_cost_, std::abs(objective_[col]));
164 if (residual_pattern.ColDegree(col) == 1) {
165 residual_singleton_column.push_back(col);
169 max_scaled_abs_cost_ =
170 (max_scaled_abs_cost_ == 0.0) ? 1.0 : kBixbyWeight * max_scaled_abs_cost_;
171 std::priority_queue<ColIndex, std::vector<ColIndex>,
172 InitialBasis::TriangularColumnComparator>
173 queue(residual_singleton_column.begin(), residual_singleton_column.end(),
174 triangular_column_comparator_);
178 while (!queue.empty()) {
179 const ColIndex candidate = queue.top();
181 if (residual_pattern.ColDegree(candidate) != 1)
continue;
189 max_magnitude = std::max(max_magnitude, std::abs(e.coefficient()));
190 if (can_be_replaced[e.row()]) {
192 coeff = e.coefficient();
197 if (std::abs(coeff) < kStabilityThreshold * max_magnitude)
continue;
201 (*basis)[row] = candidate;
202 can_be_replaced[row] =
false;
203 residual_pattern.DeleteRowAndColumn(row, candidate);
204 for (
const ColIndex col : residual_pattern.RowNonZero(row)) {
205 if (col == candidate)
continue;
206 residual_pattern.DecreaseColDegree(col);
207 if (residual_pattern.ColDegree(col) == 1) {
214int InitialBasis::GetMarosPriority(ColIndex col)
const {
216 switch (variable_type_[col]) {
228 LOG(FATAL) <<
"Invalid variable type: "
229 <<
static_cast<int>(variable_type_[col]);
232int InitialBasis::GetMarosPriority(RowIndex row)
const {
235 ColIndex slack_index(
RowToColIndex(row) + compact_matrix_.num_cols() -
238 return GetMarosPriority(slack_index);
241template <
bool only_allow_zero_cost_column>
242void InitialBasis::GetMarosBasis(ColIndex num_cols,
RowToColMapping* basis) {
243 VLOG(1) <<
"Starting Maros crash procedure.";
246 const RowIndex num_rows = compact_matrix_.num_rows();
247 const ColIndex first_slack = num_cols -
RowToColIndex(num_rows);
248 DCHECK_EQ(num_rows, basis->size());
249 basis->resize(num_rows);
250 for (RowIndex row(0); row < num_rows; row++) {
256 for (ColIndex col(0); col < first_slack; ++col) {
258 (only_allow_zero_cost_column && objective_[col] != 0.0)) {
259 available[col] =
false;
262 for (ColIndex col = first_slack; col < num_cols; ++col) {
264 available[col] =
false;
269 MatrixNonZeroPattern residual_pattern;
270 residual_pattern.Reset(num_rows, num_cols);
271 for (ColIndex col(0); col < first_slack; ++col) {
274 residual_pattern.AddEntry(e.row(), col);
280 for (RowIndex row(0); row < num_rows; row++) {
281 if (residual_pattern.RowDegree(row) == 0) {
289 int max_row_priority_function = std::numeric_limits<int>::min();
291 for (RowIndex row(0); row < num_rows; row++) {
294 10 * (3 - GetMarosPriority(row)) - residual_pattern.RowDegree(row);
295 if (rpf > max_row_priority_function) {
296 max_row_priority_function = rpf;
307 int max_col_priority_function(std::numeric_limits<int>::min());
309 for (
const ColIndex col : residual_pattern.RowNonZero(max_rpf_row)) {
310 if (!available[col])
continue;
312 10 * GetMarosPriority(col) - residual_pattern.ColDegree(col);
313 if (cpf > max_col_priority_function) {
316 pivot_absolute_value = 0.0;
317 const auto& column_values = compact_matrix_.column(col);
319 const Fractional absolute_value = std::fabs(e.coefficient());
320 if (e.row() == max_rpf_row) pivot_absolute_value = absolute_value;
321 max_magnitude = std::max(max_magnitude, absolute_value);
323 if (pivot_absolute_value >= kStabilityThreshold * max_magnitude) {
324 max_col_priority_function = cpf;
338 const int row_priority = GetMarosPriority(max_rpf_row);
339 const int column_priority = GetMarosPriority(max_cpf_col);
340 if (row_priority >= column_priority) {
347 (*basis)[max_rpf_row] = max_cpf_col;
349 VLOG(2) <<
"Slack variable " << max_rpf_row <<
" replaced by column "
351 <<
". Pivot coefficient magnitude: " << pivot_absolute_value <<
".";
353 available[max_cpf_col] =
false;
359 residual_pattern.DeleteRowAndColumn(max_rpf_row, max_cpf_col);
360 for (
const ColIndex col : residual_pattern.RowNonZero(max_rpf_row)) {
361 available[col] =
false;
367 std::vector<ColIndex>* candidates) {
369 max_scaled_abs_cost_ = 0.0;
370 for (ColIndex col(0); col < num_cols; ++col) {
372 compact_matrix_.column(col).num_entries() > 0) {
373 candidates->push_back(col);
374 max_scaled_abs_cost_ =
375 std::max(max_scaled_abs_cost_, std::abs(objective_[col]));
379 max_scaled_abs_cost_ =
380 (max_scaled_abs_cost_ == 0.0) ? 1.0 : kBixbyWeight * max_scaled_abs_cost_;
381 std::sort(candidates->begin(), candidates->end(), bixby_column_comparator_);
384int InitialBasis::GetColumnCategory(ColIndex col)
const {
387 switch (variable_type_[col]) {
402Fractional InitialBasis::GetColumnPenalty(ColIndex col)
const {
406 penalty = lower_bound_[col];
409 penalty = -upper_bound_[col];
412 penalty = lower_bound_[col] - upper_bound_[col];
414 return penalty + std::abs(objective_[col]) / max_scaled_abs_cost_;
417bool InitialBasis::BixbyColumnComparator::operator()(ColIndex col_a,
418 ColIndex col_b)
const {
419 if (col_a == col_b)
return false;
420 const int category_a = initial_basis_.GetColumnCategory(col_a);
421 const int category_b = initial_basis_.GetColumnCategory(col_b);
422 if (category_a != category_b) {
423 return category_a < category_b;
425 return initial_basis_.GetColumnPenalty(col_a) <
426 initial_basis_.GetColumnPenalty(col_b);
430bool InitialBasis::TriangularColumnComparator::operator()(
431 ColIndex col_a, ColIndex col_b)
const {
432 if (col_a == col_b)
return false;
433 const int category_a = initial_basis_.GetColumnCategory(col_a);
434 const int category_b = initial_basis_.GetColumnCategory(col_b);
435 if (category_a != category_b) {
436 return category_a > category_b;
445 if (initial_basis_.compact_matrix_.column(col_a).num_entries() !=
446 initial_basis_.compact_matrix_.column(col_b).num_entries()) {
447 return initial_basis_.compact_matrix_.column(col_a).num_entries() >
448 initial_basis_.compact_matrix_.column(col_b).num_entries();
450 return initial_basis_.GetColumnPenalty(col_a) >
451 initial_basis_.GetColumnPenalty(col_b);
RowIndex num_rows() const
ColumnView column(ColIndex col) const
void CompleteTriangularPrimalBasis(ColIndex num_cols, RowToColMapping *basis)
InitialBasis(const CompactSparseMatrix &compact_matrix, const DenseRow &objective, const DenseRow &lower_bound, const DenseRow &upper_bound, const VariableTypeRow &variable_type)
void CompleteBixbyBasis(ColIndex num_cols, RowToColMapping *basis)
void GetPrimalMarosBasis(ColIndex num_cols, RowToColMapping *basis)
void ComputeCandidates(ColIndex num_cols, std::vector< ColIndex > *candidates)
void GetDualMarosBasis(ColIndex num_cols, RowToColMapping *basis)
void CompleteTriangularDualBasis(ColIndex num_cols, RowToColMapping *basis)
typename Iterator::Entry Entry
void resize(IntType size)
constexpr ColIndex kInvalidCol(-1)
StrictITIVector< RowIndex, ColIndex > RowToColMapping
StrictITIVector< RowIndex, bool > DenseBooleanColumn
ColIndex RowToColIndex(RowIndex row)
constexpr RowIndex kInvalidRow(-1)
@ UPPER_AND_LOWER_BOUNDED
StrictITIVector< ColIndex, VariableType > VariableTypeRow
Fractional RestrictedInfinityNorm(const ColumnView &column, const DenseBooleanColumn &rows_to_consider, RowIndex *row_index)
Fractional InfinityNorm(const DenseColumn &v)
void SetSupportToFalse(const ColumnView &column, DenseBooleanColumn *b)
constexpr Fractional kInfinity
StrictITIVector< RowIndex, Fractional > DenseColumn
StrictITIVector< ColIndex, Fractional > DenseRow
bool IsDominated(const ColumnView &column, const DenseColumn &radius)
StrictITIVector< ColIndex, bool > DenseBooleanRow