34 : max_scaled_abs_cost_(0.0),
35 bixby_column_comparator_(*this),
36 triangular_column_comparator_(*this),
37 compact_matrix_(compact_matrix),
38 objective_(objective),
41 variable_type_(variable_type) {}
47 const RowIndex num_rows = compact_matrix_.
num_rows();
50 DCHECK_EQ(num_rows, basis->
size());
52 for (RowIndex
row(0);
row < num_rows; ++
row) {
54 can_be_replaced[
row] =
true;
55 has_zero_coefficient[
row] =
true;
64 std::vector<ColIndex> candidates;
69 for (
int i = 0; i < candidates.size(); ++i) {
70 bool enter_basis =
false;
71 const ColIndex candidate_col_index = candidates[i];
72 const auto& candidate_col = compact_matrix_.
column(candidate_col_index);
80 RowIndex candidate_row;
82 candidate_col, has_zero_coefficient, &candidate_row);
84 if (candidate_coeff > kBixbyHighThreshold) {
86 }
else if (
IsDominated(candidate_col, scaled_diagonal_abs)) {
89 if (candidate_coeff != 0.0) {
95 can_be_replaced[candidate_row] =
false;
98 scaled_diagonal_abs[candidate_row] =
99 kBixbyLowThreshold * std::abs(candidate_coeff);
100 (*basis)[candidate_row] = candidate_col_index;
107 return GetMarosBasis<false>(num_cols, basis);
112 return GetMarosBasis<true>(num_cols, basis);
117 return CompleteTriangularBasis<false>(num_cols, basis);
122 return CompleteTriangularBasis<true>(num_cols, basis);
125template <
bool only_allow_zero_cost_column>
126void InitialBasis::CompleteTriangularBasis(ColIndex num_cols,
129 const RowIndex num_rows = compact_matrix_.
num_rows();
131 DCHECK_EQ(num_rows, basis->
size());
133 for (RowIndex
row(0);
row < num_rows; ++
row) {
135 can_be_replaced[
row] =
true;
140 MatrixNonZeroPattern residual_pattern;
141 residual_pattern.Reset(num_rows, num_cols);
142 for (ColIndex
col(0);
col < num_cols; ++
col) {
143 if (only_allow_zero_cost_column && objective_[
col] != 0.0)
continue;
144 for (
const SparseColumn::Entry e : compact_matrix_.
column(
col)) {
145 if (can_be_replaced[e.row()]) {
146 residual_pattern.AddEntry(e.row(),
col);
153 std::vector<ColIndex> residual_singleton_column;
154 max_scaled_abs_cost_ = 0.0;
155 for (ColIndex
col(0);
col < num_cols; ++
col) {
156 max_scaled_abs_cost_ =
157 std::max(max_scaled_abs_cost_, std::abs(objective_[
col]));
158 if (residual_pattern.ColDegree(
col) == 1) {
159 residual_singleton_column.push_back(
col);
163 max_scaled_abs_cost_ =
164 (max_scaled_abs_cost_ == 0.0) ? 1.0 : kBixbyWeight * max_scaled_abs_cost_;
165 std::priority_queue<ColIndex, std::vector<ColIndex>,
166 InitialBasis::TriangularColumnComparator>
167 queue(residual_singleton_column.begin(), residual_singleton_column.end(),
168 triangular_column_comparator_);
172 while (!queue.empty()) {
173 const ColIndex candidate = queue.top();
175 if (residual_pattern.ColDegree(candidate) != 1)
continue;
182 for (
const SparseColumn::Entry e : compact_matrix_.
column(candidate)) {
183 max_magnitude = std::max(max_magnitude, std::abs(e.coefficient()));
184 if (can_be_replaced[e.row()]) {
186 coeff = e.coefficient();
191 if (std::abs(coeff) < kStabilityThreshold * max_magnitude)
continue;
195 (*basis)[
row] = candidate;
196 can_be_replaced[
row] =
false;
197 residual_pattern.DeleteRowAndColumn(
row, candidate);
198 for (
const ColIndex
col : residual_pattern.RowNonZero(
row)) {
199 if (
col == candidate)
continue;
200 residual_pattern.DecreaseColDegree(
col);
201 if (residual_pattern.ColDegree(
col) == 1) {
208int InitialBasis::GetMarosPriority(ColIndex
col)
const {
210 switch (variable_type_[
col]) {
224int InitialBasis::GetMarosPriority(RowIndex
row)
const {
230 return GetMarosPriority(slack_index);
233template <
bool only_allow_zero_cost_column>
234void InitialBasis::GetMarosBasis(ColIndex num_cols,
RowToColMapping* basis) {
235 VLOG(1) <<
"Starting Maros crash procedure.";
238 const RowIndex num_rows = compact_matrix_.
num_rows();
239 const ColIndex first_slack = num_cols -
RowToColIndex(num_rows);
240 DCHECK_EQ(num_rows, basis->size());
241 basis->resize(num_rows);
242 for (RowIndex
row(0);
row < num_rows;
row++) {
248 for (ColIndex
col(0);
col < first_slack; ++
col) {
250 (only_allow_zero_cost_column && objective_[
col] != 0.0)) {
251 available[
col] =
false;
254 for (ColIndex
col = first_slack;
col < num_cols; ++
col) {
256 available[
col] =
false;
261 MatrixNonZeroPattern residual_pattern;
262 residual_pattern.Reset(num_rows, num_cols);
263 for (ColIndex
col(0);
col < first_slack; ++
col) {
264 for (
const SparseColumn::Entry e : compact_matrix_.
column(
col)) {
266 residual_pattern.AddEntry(e.row(),
col);
272 for (RowIndex
row(0);
row < num_rows;
row++) {
273 if (residual_pattern.RowDegree(
row) == 0) {
281 int max_row_priority_function = std::numeric_limits<int>::min();
283 for (RowIndex
row(0);
row < num_rows;
row++) {
286 10 * (3 - GetMarosPriority(
row)) - residual_pattern.RowDegree(
row);
287 if (rpf > max_row_priority_function) {
288 max_row_priority_function = rpf;
299 int max_col_priority_function(std::numeric_limits<int>::min());
301 for (
const ColIndex
col : residual_pattern.RowNonZero(max_rpf_row)) {
302 if (!available[
col])
continue;
304 10 * GetMarosPriority(
col) - residual_pattern.ColDegree(
col);
305 if (cpf > max_col_priority_function) {
308 pivot_absolute_value = 0.0;
309 const auto& column_values = compact_matrix_.
column(
col);
310 for (
const SparseColumn::Entry e : column_values) {
311 const Fractional absolute_value = std::fabs(e.coefficient());
312 if (e.row() == max_rpf_row) pivot_absolute_value = absolute_value;
313 max_magnitude = std::max(max_magnitude, absolute_value);
315 if (pivot_absolute_value >= kStabilityThreshold * max_magnitude) {
316 max_col_priority_function = cpf;
330 const int row_priority = GetMarosPriority(max_rpf_row);
331 const int column_priority = GetMarosPriority(max_cpf_col);
332 if (row_priority >= column_priority) {
339 (*basis)[max_rpf_row] = max_cpf_col;
341 VLOG(2) <<
"Slack variable " << max_rpf_row <<
" replaced by column "
343 <<
". Pivot coefficient magnitude: " << pivot_absolute_value <<
".";
345 available[max_cpf_col] =
false;
351 residual_pattern.DeleteRowAndColumn(max_rpf_row, max_cpf_col);
352 for (
const ColIndex
col : residual_pattern.RowNonZero(max_rpf_row)) {
353 available[
col] =
false;
359 std::vector<ColIndex>* candidates) {
361 max_scaled_abs_cost_ = 0.0;
362 for (ColIndex
col(0);
col < num_cols; ++
col) {
365 candidates->push_back(
col);
366 max_scaled_abs_cost_ =
367 std::max(max_scaled_abs_cost_, std::abs(objective_[
col]));
371 max_scaled_abs_cost_ =
372 (max_scaled_abs_cost_ == 0.0) ? 1.0 : kBixbyWeight * max_scaled_abs_cost_;
373 std::sort(candidates->begin(), candidates->end(), bixby_column_comparator_);
376int InitialBasis::GetColumnCategory(ColIndex
col)
const {
379 switch (variable_type_[
col]) {
393Fractional InitialBasis::GetColumnPenalty(ColIndex
col)
const {
397 penalty = lower_bound_[
col];
400 penalty = -upper_bound_[
col];
403 penalty = lower_bound_[
col] - upper_bound_[
col];
405 return penalty + std::abs(objective_[
col]) / max_scaled_abs_cost_;
408bool InitialBasis::BixbyColumnComparator::operator()(ColIndex col_a,
409 ColIndex col_b)
const {
410 if (col_a == col_b)
return false;
411 const int category_a = initial_basis_.GetColumnCategory(col_a);
412 const int category_b = initial_basis_.GetColumnCategory(col_b);
413 if (category_a != category_b) {
414 return category_a < category_b;
416 return initial_basis_.GetColumnPenalty(col_a) <
417 initial_basis_.GetColumnPenalty(col_b);
421bool InitialBasis::TriangularColumnComparator::operator()(
422 ColIndex col_a, ColIndex col_b)
const {
423 if (col_a == col_b)
return false;
424 const int category_a = initial_basis_.GetColumnCategory(col_a);
425 const int category_b = initial_basis_.GetColumnCategory(col_b);
426 if (category_a != category_b) {
427 return category_a > category_b;
436 if (initial_basis_.compact_matrix_.column(col_a).num_entries() !=
437 initial_basis_.compact_matrix_.column(col_b).num_entries()) {
438 return initial_basis_.compact_matrix_.column(col_a).num_entries() >
439 initial_basis_.compact_matrix_.column(col_b).num_entries();
441 return initial_basis_.GetColumnPenalty(col_a) >
442 initial_basis_.GetColumnPenalty(col_b);
EntryIndex num_entries() const
RowIndex num_rows() const
ColIndex num_cols() 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)
Takes references to the linear program data we need.
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)
void resize(IntType size)
constexpr ColIndex kInvalidCol(-1)
StrictITIVector< RowIndex, ColIndex > RowToColMapping
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
constexpr RowIndex kInvalidRow(-1)
VariableType
Different types of variables.
@ UPPER_AND_LOWER_BOUNDED
Fractional RestrictedInfinityNorm(const ColumnView &column, const DenseBooleanColumn &rows_to_consider, RowIndex *row_index)
Fractional InfinityNorm(const DenseColumn &v)
Returns the maximum of the coefficients of 'v'.
void SetSupportToFalse(const ColumnView &column, DenseBooleanColumn *b)
bool IsDominated(const ColumnView &column, const DenseColumn &radius)
Returns true iff for all 'row' we have '|column[row]| <= radius[row]'.
StrictITIVector< ColIndex, bool > DenseBooleanRow
Row of booleans.
In SWIG mode, we don't want anything besides these top-level includes.