29 : max_scaled_abs_cost_(0.0),
30 bixby_column_comparator_(*this),
31 triangular_column_comparator_(*this),
32 compact_matrix_(compact_matrix),
34 lower_bound_(lower_bound),
35 upper_bound_(upper_bound),
36 variable_type_(variable_type) {}
42 const RowIndex num_rows = compact_matrix_.
num_rows();
47 for (RowIndex
row(0);
row < num_rows; ++
row) {
49 can_be_replaced[
row] =
true;
50 has_zero_coefficient[
row] =
true;
59 std::vector<ColIndex> candidates;
64 for (
int i = 0; i < candidates.size(); ++i) {
65 bool enter_basis =
false;
66 const ColIndex candidate_col_index = candidates[i];
67 const auto& candidate_col = compact_matrix_.
column(candidate_col_index);
75 RowIndex candidate_row;
77 candidate_col, has_zero_coefficient, &candidate_row);
79 if (candidate_coeff > kBixbyHighThreshold) {
81 }
else if (
IsDominated(candidate_col, scaled_diagonal_abs)) {
84 if (candidate_coeff != 0.0) {
90 can_be_replaced[candidate_row] =
false;
93 scaled_diagonal_abs[candidate_row] =
94 kBixbyLowThreshold * std::abs(candidate_coeff);
95 (*basis)[candidate_row] = candidate_col_index;
102 return GetMarosBasis<false>(num_cols, basis);
107 return GetMarosBasis<true>(num_cols, basis);
112 return CompleteTriangularBasis<false>(num_cols, basis);
117 return CompleteTriangularBasis<true>(num_cols, basis);
120 template <
bool only_allow_zero_cost_column>
121 void InitialBasis::CompleteTriangularBasis(ColIndex num_cols,
124 const RowIndex num_rows = compact_matrix_.
num_rows();
128 for (RowIndex
row(0);
row < num_rows; ++
row) {
130 can_be_replaced[
row] =
true;
135 MatrixNonZeroPattern residual_pattern;
136 residual_pattern.Reset(num_rows, num_cols);
137 for (ColIndex
col(0);
col < num_cols; ++
col) {
138 if (only_allow_zero_cost_column && objective_[
col] != 0.0)
continue;
140 if (can_be_replaced[e.row()]) {
141 residual_pattern.AddEntry(e.row(),
col);
148 std::vector<ColIndex> residual_singleton_column;
149 max_scaled_abs_cost_ = 0.0;
150 for (ColIndex
col(0);
col < num_cols; ++
col) {
151 max_scaled_abs_cost_ =
152 std::max(max_scaled_abs_cost_, std::abs(objective_[
col]));
153 if (residual_pattern.ColDegree(
col) == 1) {
154 residual_singleton_column.push_back(
col);
158 max_scaled_abs_cost_ =
159 (max_scaled_abs_cost_ == 0.0) ? 1.0 : kBixbyWeight * max_scaled_abs_cost_;
160 std::priority_queue<ColIndex, std::vector<ColIndex>,
161 InitialBasis::TriangularColumnComparator>
162 queue(residual_singleton_column.begin(), residual_singleton_column.end(),
163 triangular_column_comparator_);
167 while (!queue.empty()) {
168 const ColIndex candidate = queue.top();
170 if (residual_pattern.ColDegree(candidate) != 1)
continue;
178 max_magnitude =
std::max(max_magnitude, std::abs(e.coefficient()));
179 if (can_be_replaced[e.row()]) {
181 coeff = e.coefficient();
186 if (std::abs(coeff) < kStabilityThreshold * max_magnitude)
continue;
190 (*basis)[
row] = candidate;
191 can_be_replaced[
row] =
false;
192 residual_pattern.DeleteRowAndColumn(
row, candidate);
193 for (
const ColIndex
col : residual_pattern.RowNonZero(
row)) {
194 if (
col == candidate)
continue;
195 residual_pattern.DecreaseColDegree(
col);
196 if (residual_pattern.ColDegree(
col) == 1) {
203 int InitialBasis::GetMarosPriority(ColIndex
col)
const {
205 switch (variable_type_[
col]) {
206 case VariableType::UNCONSTRAINED:
208 case VariableType::LOWER_BOUNDED:
210 case VariableType::UPPER_BOUNDED:
212 case VariableType::UPPER_AND_LOWER_BOUNDED:
219 int InitialBasis::GetMarosPriority(RowIndex
row)
const {
225 return GetMarosPriority(slack_index);
228 template <
bool only_allow_zero_cost_column>
229 void InitialBasis::GetMarosBasis(ColIndex num_cols,
RowToColMapping* basis) {
230 VLOG(1) <<
"Starting Maros crash procedure.";
233 const RowIndex num_rows = compact_matrix_.
num_rows();
234 const ColIndex first_slack = num_cols -
RowToColIndex(num_rows);
236 basis->resize(num_rows);
237 for (RowIndex
row(0);
row < num_rows;
row++) {
243 for (ColIndex
col(0);
col < first_slack; ++
col) {
245 (only_allow_zero_cost_column && objective_[
col] != 0.0)) {
246 available[
col] =
false;
249 for (ColIndex
col = first_slack;
col < num_cols; ++
col) {
250 if (variable_type_[
col] == VariableType::UNCONSTRAINED) {
251 available[
col] =
false;
256 MatrixNonZeroPattern residual_pattern;
257 residual_pattern.Reset(num_rows, num_cols);
258 for (ColIndex
col(0);
col < first_slack; ++
col) {
261 residual_pattern.AddEntry(e.row(),
col);
267 for (RowIndex
row(0);
row < num_rows;
row++) {
268 if (residual_pattern.RowDegree(
row) == 0) {
278 for (RowIndex
row(0);
row < num_rows;
row++) {
281 10 * (3 - GetMarosPriority(
row)) - residual_pattern.RowDegree(
row);
282 if (rpf > max_row_priority_function) {
283 max_row_priority_function = rpf;
296 for (
const ColIndex
col : residual_pattern.RowNonZero(max_rpf_row)) {
297 if (!available[
col])
continue;
299 10 * GetMarosPriority(
col) - residual_pattern.ColDegree(
col);
300 if (cpf > max_col_priority_function) {
303 pivot_absolute_value = 0.0;
304 const auto& column_values = compact_matrix_.
column(
col);
306 const Fractional absolute_value = std::fabs(e.coefficient());
307 if (e.row() == max_rpf_row) pivot_absolute_value = absolute_value;
308 max_magnitude =
std::max(max_magnitude, absolute_value);
310 if (pivot_absolute_value >= kStabilityThreshold * max_magnitude) {
311 max_col_priority_function = cpf;
325 const int row_priority = GetMarosPriority(max_rpf_row);
326 const int column_priority = GetMarosPriority(max_cpf_col);
327 if (row_priority >= column_priority) {
334 (*basis)[max_rpf_row] = max_cpf_col;
336 VLOG(2) <<
"Slack variable " << max_rpf_row <<
" replaced by column "
338 <<
". Pivot coefficient magnitude: " << pivot_absolute_value <<
".";
340 available[max_cpf_col] =
false;
346 residual_pattern.DeleteRowAndColumn(max_rpf_row, max_cpf_col);
347 for (
const ColIndex
col : residual_pattern.RowNonZero(max_rpf_row)) {
348 available[
col] =
false;
354 std::vector<ColIndex>* candidates) {
356 max_scaled_abs_cost_ = 0.0;
357 for (ColIndex
col(0);
col < num_cols; ++
col) {
360 candidates->push_back(
col);
361 max_scaled_abs_cost_ =
362 std::max(max_scaled_abs_cost_, std::abs(objective_[
col]));
366 max_scaled_abs_cost_ =
367 (max_scaled_abs_cost_ == 0.0) ? 1.0 : kBixbyWeight * max_scaled_abs_cost_;
368 std::sort(candidates->begin(), candidates->end(), bixby_column_comparator_);
371 int InitialBasis::GetColumnCategory(ColIndex
col)
const {
374 switch (variable_type_[
col]) {
375 case VariableType::UNCONSTRAINED:
377 case VariableType::LOWER_BOUNDED:
379 case VariableType::UPPER_BOUNDED:
381 case VariableType::UPPER_AND_LOWER_BOUNDED:
388 Fractional InitialBasis::GetColumnPenalty(ColIndex
col)
const {
391 if (type == VariableType::LOWER_BOUNDED) {
392 penalty = lower_bound_[
col];
394 if (type == VariableType::UPPER_BOUNDED) {
395 penalty = -upper_bound_[
col];
397 if (type == VariableType::UPPER_AND_LOWER_BOUNDED) {
398 penalty = lower_bound_[
col] - upper_bound_[
col];
400 return penalty + std::abs(objective_[
col]) / max_scaled_abs_cost_;
403 bool InitialBasis::BixbyColumnComparator::operator()(ColIndex col_a,
404 ColIndex col_b)
const {
405 if (col_a == col_b)
return false;
406 const int category_a = initial_basis_.GetColumnCategory(col_a);
407 const int category_b = initial_basis_.GetColumnCategory(col_b);
408 if (category_a != category_b) {
409 return category_a < category_b;
411 return initial_basis_.GetColumnPenalty(col_a) <
412 initial_basis_.GetColumnPenalty(col_b);
416 bool InitialBasis::TriangularColumnComparator::operator()(
417 ColIndex col_a, ColIndex col_b)
const {
418 if (col_a == col_b)
return false;
419 const int category_a = initial_basis_.GetColumnCategory(col_a);
420 const int category_b = initial_basis_.GetColumnCategory(col_b);
421 if (category_a != category_b) {
422 return category_a > category_b;
431 if (initial_basis_.compact_matrix_.column(col_a).num_entries() !=
432 initial_basis_.compact_matrix_.column(col_b).num_entries()) {
433 return initial_basis_.compact_matrix_.column(col_a).num_entries() >
434 initial_basis_.compact_matrix_.column(col_b).num_entries();
436 return initial_basis_.GetColumnPenalty(col_a) >
437 initial_basis_.GetColumnPenalty(col_b);