2022-06-17 08:40:20 +02:00
|
|
|
// Copyright 2010-2022 Google LLC
|
2014-07-08 17:35:15 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/glop/entering_variable.h"
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2022-03-25 15:10:39 +01:00
|
|
|
#include <algorithm>
|
2023-04-05 14:00:30 +02:00
|
|
|
#include <cstdlib>
|
2022-07-22 14:35:40 +02:00
|
|
|
#include <limits>
|
2014-07-08 09:27:02 +00:00
|
|
|
#include <queue>
|
2022-07-22 14:35:40 +02:00
|
|
|
#include <vector>
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/timer.h"
|
|
|
|
|
#include "ortools/lp_data/lp_utils.h"
|
2017-12-06 11:23:11 +01:00
|
|
|
#include "ortools/port/proto_utils.h"
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
namespace glop {
|
|
|
|
|
|
2020-10-28 13:42:36 +01:00
|
|
|
EnteringVariable::EnteringVariable(const VariablesInfo& variables_info,
|
2021-04-13 14:31:06 +02:00
|
|
|
absl::BitGenRef random,
|
2021-04-14 14:33:47 +02:00
|
|
|
ReducedCosts* reduced_costs)
|
2020-10-22 23:36:58 +02:00
|
|
|
: variables_info_(variables_info),
|
|
|
|
|
random_(random),
|
|
|
|
|
reduced_costs_(reduced_costs),
|
2021-04-14 14:33:47 +02:00
|
|
|
parameters_() {}
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
Status EnteringVariable::DualChooseEnteringColumn(
|
2021-03-24 20:59:34 +01:00
|
|
|
bool nothing_to_recompute, const UpdateRow& update_row,
|
|
|
|
|
Fractional cost_variation, std::vector<ColIndex>* bound_flip_candidates,
|
2021-03-25 22:38:06 +01:00
|
|
|
ColIndex* entering_col) {
|
2017-04-26 17:30:25 +02:00
|
|
|
GLOP_RETURN_ERROR_IF_NULL(entering_col);
|
2022-11-22 11:04:33 +01:00
|
|
|
const auto update_coefficients = update_row.GetCoefficients().const_view();
|
|
|
|
|
const auto reduced_costs = reduced_costs_->GetReducedCosts().const_view();
|
2014-07-08 09:27:02 +00:00
|
|
|
SCOPED_TIME_STAT(&stats_);
|
|
|
|
|
|
2017-11-10 18:31:37 +01:00
|
|
|
breakpoints_.clear();
|
|
|
|
|
breakpoints_.reserve(update_row.GetNonZeroPositions().size());
|
2020-10-28 13:42:36 +01:00
|
|
|
const DenseBitRow& can_decrease = variables_info_.GetCanDecreaseBitRow();
|
|
|
|
|
const DenseBitRow& can_increase = variables_info_.GetCanIncreaseBitRow();
|
|
|
|
|
const DenseBitRow& is_boxed = variables_info_.GetNonBasicBoxedVariables();
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2021-03-24 20:59:34 +01:00
|
|
|
// If everything has the best possible precision currently, we ignore
|
|
|
|
|
// low coefficients. This make sure we will never choose a pivot too small. It
|
|
|
|
|
// however can degrade the dual feasibility of the solution, but we can always
|
|
|
|
|
// fix that later.
|
|
|
|
|
//
|
|
|
|
|
// TODO(user): It is unclear if this is a good idea, but the primal simplex
|
|
|
|
|
// have pretty good/stable behavior with a similar logic. Experiment seems
|
|
|
|
|
// to show that this works well with the dual too.
|
|
|
|
|
const Fractional threshold = nothing_to_recompute
|
|
|
|
|
? parameters_.minimum_acceptable_pivot()
|
|
|
|
|
: parameters_.ratio_test_zero_threshold();
|
|
|
|
|
|
2022-11-10 11:28:22 +01:00
|
|
|
Fractional variation_magnitude = std::abs(cost_variation) - threshold;
|
|
|
|
|
|
2014-07-08 09:27:02 +00:00
|
|
|
// Harris ratio test. See below for more explanation. Here this is used to
|
|
|
|
|
// prune the first pass by not enqueueing ColWithRatio for columns that have
|
|
|
|
|
// a ratio greater than the current harris_ratio.
|
|
|
|
|
const Fractional harris_tolerance =
|
|
|
|
|
parameters_.harris_tolerance_ratio() *
|
|
|
|
|
reduced_costs_->GetDualFeasibilityTolerance();
|
|
|
|
|
Fractional harris_ratio = std::numeric_limits<Fractional>::max();
|
|
|
|
|
|
2021-03-25 22:38:06 +01:00
|
|
|
// Like for the primal, we always allow a positive ministep, even if a
|
|
|
|
|
// variable is already infeasible by more than the tolerance.
|
|
|
|
|
const Fractional minimum_delta =
|
|
|
|
|
parameters_.degenerate_ministep_factor() *
|
|
|
|
|
reduced_costs_->GetDualFeasibilityTolerance();
|
|
|
|
|
|
2021-03-23 17:55:40 +01:00
|
|
|
num_operations_ += 10 * update_row.GetNonZeroPositions().size();
|
2014-07-08 09:27:02 +00:00
|
|
|
for (const ColIndex col : update_row.GetNonZeroPositions()) {
|
|
|
|
|
// We will add ratio * coeff to this column with a ratio positive or zero.
|
|
|
|
|
// cost_variation makes sure the leaving variable will be dual-feasible
|
|
|
|
|
// (its update coeff is sign(cost_variation) * 1.0).
|
2022-11-22 11:04:33 +01:00
|
|
|
const Fractional coeff = (cost_variation > 0.0) ? update_coefficients[col]
|
|
|
|
|
: -update_coefficients[col];
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2022-11-10 11:28:22 +01:00
|
|
|
ColWithRatio entry;
|
2014-07-08 09:27:02 +00:00
|
|
|
if (can_decrease.IsSet(col) && coeff > threshold) {
|
2022-11-10 11:28:22 +01:00
|
|
|
// In this case, at some point the reduced cost will be positive if not
|
|
|
|
|
// already, and the column will be dual-infeasible.
|
|
|
|
|
if (-reduced_costs[col] > harris_ratio * coeff) continue;
|
|
|
|
|
entry = ColWithRatio(col, -reduced_costs[col], coeff);
|
|
|
|
|
} else if (can_increase.IsSet(col) && coeff < -threshold) {
|
|
|
|
|
// In this case, at some point the reduced cost will be negative if not
|
|
|
|
|
// already, and the column will be dual-infeasible.
|
|
|
|
|
if (reduced_costs[col] > harris_ratio * -coeff) continue;
|
|
|
|
|
entry = ColWithRatio(col, reduced_costs[col], -coeff);
|
|
|
|
|
} else {
|
2014-07-08 09:27:02 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-10 11:28:22 +01:00
|
|
|
const Fractional hr =
|
|
|
|
|
std::max(minimum_delta / entry.coeff_magnitude,
|
|
|
|
|
entry.ratio + harris_tolerance / entry.coeff_magnitude);
|
|
|
|
|
if (hr < harris_ratio) {
|
|
|
|
|
if (is_boxed[col]) {
|
|
|
|
|
const Fractional delta =
|
|
|
|
|
variables_info_.GetBoundDifference(col) * entry.coeff_magnitude;
|
|
|
|
|
if (delta >= variation_magnitude) {
|
|
|
|
|
harris_ratio = hr;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
harris_ratio = hr;
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-10 11:28:22 +01:00
|
|
|
|
|
|
|
|
breakpoints_.push_back(entry);
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the breakpoints in priority order as suggested by Maros in
|
|
|
|
|
// I. Maros, "A generalized dual phase-2 simplex algorithm", European Journal
|
|
|
|
|
// of Operational Research, 149(1):1-16, 2003.
|
|
|
|
|
// We use directly make_heap() to avoid a copy of breakpoints, benchmark shows
|
|
|
|
|
// that it is slightly faster.
|
2017-11-10 18:31:37 +01:00
|
|
|
std::make_heap(breakpoints_.begin(), breakpoints_.end());
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
// Harris ratio test. Since we process the breakpoints by increasing ratio, we
|
|
|
|
|
// do not need a two-pass algorithm as described in the literature. Each time
|
|
|
|
|
// we process a new breakpoint, we update the harris_ratio of all the
|
|
|
|
|
// processed breakpoints. For the first new breakpoint with a ratio greater
|
|
|
|
|
// than the current harris_ratio we know that:
|
|
|
|
|
// - All the unprocessed breakpoints will have a ratio greater too, so they
|
|
|
|
|
// will not contribute to the minimum Harris ratio.
|
|
|
|
|
// - We thus have the actual harris_ratio.
|
|
|
|
|
// - We have processed all breakpoints with a ratio smaller than it.
|
|
|
|
|
harris_ratio = std::numeric_limits<Fractional>::max();
|
|
|
|
|
|
|
|
|
|
*entering_col = kInvalidCol;
|
|
|
|
|
bound_flip_candidates->clear();
|
2021-03-25 22:38:06 +01:00
|
|
|
Fractional step = 0.0;
|
2014-07-08 09:27:02 +00:00
|
|
|
Fractional best_coeff = -1.0;
|
|
|
|
|
equivalent_entering_choices_.clear();
|
2017-11-10 18:31:37 +01:00
|
|
|
while (!breakpoints_.empty()) {
|
|
|
|
|
const ColWithRatio top = breakpoints_.front();
|
2020-10-22 23:36:58 +02:00
|
|
|
if (top.ratio > harris_ratio) break;
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
// If the column is boxed, we can just switch its bounds and
|
|
|
|
|
// ignore the breakpoint! But we need to see if the entering row still
|
|
|
|
|
// improve the objective. This is called the bound flipping ratio test in
|
|
|
|
|
// the literature. See for instance:
|
|
|
|
|
// http://www.mpi-inf.mpg.de/conferences/adfocs-03/Slides/Bixby_2.pdf
|
|
|
|
|
//
|
|
|
|
|
// For each bound flip, |cost_variation| decreases by
|
|
|
|
|
// |upper_bound - lower_bound| times |coeff|.
|
|
|
|
|
//
|
|
|
|
|
// Note that the actual flipping will be done afterwards by
|
|
|
|
|
// MakeBoxedVariableDualFeasible() in revised_simplex.cc.
|
2022-11-10 11:28:22 +01:00
|
|
|
if (variation_magnitude > 0.0) {
|
2018-02-12 11:35:51 +01:00
|
|
|
if (is_boxed[top.col]) {
|
2017-11-10 18:31:37 +01:00
|
|
|
variation_magnitude -=
|
|
|
|
|
variables_info_.GetBoundDifference(top.col) * top.coeff_magnitude;
|
2022-11-10 11:28:22 +01:00
|
|
|
if (variation_magnitude > 0.0) {
|
2017-11-10 18:31:37 +01:00
|
|
|
bound_flip_candidates->push_back(top.col);
|
|
|
|
|
std::pop_heap(breakpoints_.begin(), breakpoints_.end());
|
|
|
|
|
breakpoints_.pop_back();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(user): We want to maximize both the ratio (objective improvement)
|
|
|
|
|
// and the coeff_magnitude (stable pivot), so we have to make some
|
2019-04-10 10:34:21 -07:00
|
|
|
// trade-offs. Investigate alternative strategies.
|
2014-07-08 09:27:02 +00:00
|
|
|
if (top.coeff_magnitude >= best_coeff) {
|
2019-04-10 10:34:21 -07:00
|
|
|
// Update harris_ratio. Note that because we process ratio in order, the
|
|
|
|
|
// harris ratio can only get smaller if the coeff_magnitude is bigger
|
|
|
|
|
// than the one of the best coefficient.
|
2022-11-10 11:28:22 +01:00
|
|
|
//
|
2019-04-10 10:34:21 -07:00
|
|
|
// If the dual infeasibility is too high, the harris_ratio can be
|
2022-11-10 11:28:22 +01:00
|
|
|
// negative. To avoid this we always allow for a minimum step even if
|
|
|
|
|
// we push some already infeasible variable further away. This is quite
|
|
|
|
|
// important because its helps in the choice of a stable pivot.
|
|
|
|
|
harris_ratio = std::min(
|
|
|
|
|
harris_ratio,
|
|
|
|
|
std::max(minimum_delta / top.coeff_magnitude,
|
|
|
|
|
top.ratio + harris_tolerance / top.coeff_magnitude));
|
2019-04-10 10:34:21 -07:00
|
|
|
|
2021-03-25 22:38:06 +01:00
|
|
|
if (top.coeff_magnitude == best_coeff && top.ratio == step) {
|
2014-07-08 09:27:02 +00:00
|
|
|
DCHECK_NE(*entering_col, kInvalidCol);
|
|
|
|
|
equivalent_entering_choices_.push_back(top.col);
|
|
|
|
|
} else {
|
|
|
|
|
equivalent_entering_choices_.clear();
|
|
|
|
|
best_coeff = top.coeff_magnitude;
|
|
|
|
|
*entering_col = top.col;
|
|
|
|
|
|
|
|
|
|
// Note that the step is not directly used, so it is okay to leave it
|
|
|
|
|
// negative.
|
2021-03-25 22:38:06 +01:00
|
|
|
step = top.ratio;
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove the top breakpoint and maintain the heap structure.
|
|
|
|
|
// This is the same as doing a pop() on a priority_queue.
|
2017-11-10 18:31:37 +01:00
|
|
|
std::pop_heap(breakpoints_.begin(), breakpoints_.end());
|
|
|
|
|
breakpoints_.pop_back();
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Break the ties randomly.
|
|
|
|
|
if (!equivalent_entering_choices_.empty()) {
|
|
|
|
|
equivalent_entering_choices_.push_back(*entering_col);
|
2017-06-12 16:01:54 +02:00
|
|
|
*entering_col =
|
|
|
|
|
equivalent_entering_choices_[std::uniform_int_distribution<int>(
|
2021-04-13 14:31:06 +02:00
|
|
|
0, equivalent_entering_choices_.size() - 1)(random_)];
|
2014-07-08 09:27:02 +00:00
|
|
|
IF_STATS_ENABLED(
|
|
|
|
|
stats_.num_perfect_ties.Add(equivalent_entering_choices_.size()));
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
if (*entering_col == kInvalidCol) return Status::OK();
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2021-03-24 20:59:34 +01:00
|
|
|
// If best_coeff is small and they are potential bound flips, we can take a
|
|
|
|
|
// smaller step but use a good pivot.
|
|
|
|
|
const Fractional pivot_limit = parameters_.minimum_acceptable_pivot();
|
|
|
|
|
if (best_coeff < pivot_limit && !bound_flip_candidates->empty()) {
|
|
|
|
|
// Note that it is okay to leave more candidate than necessary in the
|
|
|
|
|
// returned bound_flip_candidates vector.
|
|
|
|
|
for (int i = bound_flip_candidates->size() - 1; i >= 0; --i) {
|
|
|
|
|
const ColIndex col = (*bound_flip_candidates)[i];
|
2022-11-22 11:04:33 +01:00
|
|
|
if (std::abs(update_coefficients[col]) < pivot_limit) continue;
|
2021-03-24 20:59:34 +01:00
|
|
|
|
|
|
|
|
VLOG(1) << "Used bound flip to avoid bad pivot. Before: " << best_coeff
|
2022-11-22 11:04:33 +01:00
|
|
|
<< " now: " << std::abs(update_coefficients[col]);
|
2021-03-24 20:59:34 +01:00
|
|
|
*entering_col = col;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-08 12:33:16 +02:00
|
|
|
return Status::OK();
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Status EnteringVariable::DualPhaseIChooseEnteringColumn(
|
2021-03-24 20:59:34 +01:00
|
|
|
bool nothing_to_recompute, const UpdateRow& update_row,
|
2021-03-25 22:38:06 +01:00
|
|
|
Fractional cost_variation, ColIndex* entering_col) {
|
2017-04-26 17:30:25 +02:00
|
|
|
GLOP_RETURN_ERROR_IF_NULL(entering_col);
|
2022-11-22 11:04:33 +01:00
|
|
|
const auto update_coefficients = update_row.GetCoefficients().const_view();
|
|
|
|
|
const auto reduced_costs = reduced_costs_->GetReducedCosts().const_view();
|
2014-07-08 09:27:02 +00:00
|
|
|
SCOPED_TIME_STAT(&stats_);
|
|
|
|
|
|
|
|
|
|
// List of breakpoints where a variable change from feasibility to
|
|
|
|
|
// infeasibility or the opposite.
|
2017-11-10 18:31:37 +01:00
|
|
|
breakpoints_.clear();
|
|
|
|
|
breakpoints_.reserve(update_row.GetNonZeroPositions().size());
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2021-03-24 20:59:34 +01:00
|
|
|
const Fractional threshold = nothing_to_recompute
|
|
|
|
|
? parameters_.minimum_acceptable_pivot()
|
|
|
|
|
: parameters_.ratio_test_zero_threshold();
|
2014-07-08 09:27:02 +00:00
|
|
|
const Fractional dual_feasibility_tolerance =
|
|
|
|
|
reduced_costs_->GetDualFeasibilityTolerance();
|
2021-03-25 22:38:06 +01:00
|
|
|
const Fractional harris_tolerance =
|
|
|
|
|
parameters_.harris_tolerance_ratio() * dual_feasibility_tolerance;
|
|
|
|
|
const Fractional minimum_delta =
|
|
|
|
|
parameters_.degenerate_ministep_factor() * dual_feasibility_tolerance;
|
|
|
|
|
|
2020-10-28 13:42:36 +01:00
|
|
|
const DenseBitRow& can_decrease = variables_info_.GetCanDecreaseBitRow();
|
|
|
|
|
const DenseBitRow& can_increase = variables_info_.GetCanIncreaseBitRow();
|
|
|
|
|
const VariableTypeRow& variable_type = variables_info_.GetTypeRow();
|
2021-03-23 17:55:40 +01:00
|
|
|
num_operations_ += 10 * update_row.GetNonZeroPositions().size();
|
2014-07-08 09:27:02 +00:00
|
|
|
for (const ColIndex col : update_row.GetNonZeroPositions()) {
|
|
|
|
|
// Boxed variables shouldn't be in the update position list because they
|
|
|
|
|
// will be dealt with afterwards by MakeBoxedVariableDualFeasible().
|
|
|
|
|
DCHECK_NE(variable_type[col], VariableType::UPPER_AND_LOWER_BOUNDED);
|
|
|
|
|
|
|
|
|
|
// Fixed variable shouldn't be in the update position list.
|
|
|
|
|
DCHECK_NE(variable_type[col], VariableType::FIXED_VARIABLE);
|
|
|
|
|
|
|
|
|
|
// Skip if the coeff is too small to be a numerically stable pivot.
|
2022-11-22 11:04:33 +01:00
|
|
|
if (std::abs(update_coefficients[col]) < threshold) continue;
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
// We will add ratio * coeff to this column. cost_variation makes sure
|
|
|
|
|
// the leaving variable will be dual-feasible (its update coeff is
|
|
|
|
|
// sign(cost_variation) * 1.0).
|
|
|
|
|
//
|
|
|
|
|
// TODO(user): This is the same in DualChooseEnteringColumn(), remove
|
|
|
|
|
// duplication?
|
2022-11-22 11:04:33 +01:00
|
|
|
const Fractional coeff = (cost_variation > 0.0) ? update_coefficients[col]
|
|
|
|
|
: -update_coefficients[col];
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
// Only proceed if there is a transition, note that if reduced_costs[col]
|
2021-03-25 22:38:06 +01:00
|
|
|
// is close to zero, then the variable is counted as dual-feasible.
|
2017-03-28 16:07:49 +02:00
|
|
|
if (std::abs(reduced_costs[col]) <= dual_feasibility_tolerance) {
|
2014-07-08 09:27:02 +00:00
|
|
|
// Continue if the variation goes in the dual-feasible direction.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (coeff > 0 && !can_decrease.IsSet(col)) continue;
|
|
|
|
|
if (coeff < 0 && !can_increase.IsSet(col)) continue;
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2021-03-25 22:38:06 +01:00
|
|
|
// For an already dual-infeasible variable, we allow to push it until
|
|
|
|
|
// the harris_tolerance. But if it is past that or close to it, we also
|
|
|
|
|
// always enforce a minimum push.
|
|
|
|
|
if (coeff * reduced_costs[col] > 0.0) {
|
|
|
|
|
breakpoints_.push_back(ColWithRatio(
|
|
|
|
|
col,
|
|
|
|
|
std::max(minimum_delta,
|
|
|
|
|
harris_tolerance - std::abs(reduced_costs[col])),
|
|
|
|
|
std::abs(coeff)));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-07-08 09:27:02 +00:00
|
|
|
} else {
|
|
|
|
|
// If the two are of the same sign, there is no transition, skip.
|
2021-03-25 22:38:06 +01:00
|
|
|
if (coeff * reduced_costs[col] > 0.0) continue;
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We are sure there is a transition, add it to the set of breakpoints.
|
2021-03-25 22:38:06 +01:00
|
|
|
breakpoints_.push_back(ColWithRatio(
|
|
|
|
|
col, std::abs(reduced_costs[col]) + harris_tolerance, std::abs(coeff)));
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the breakpoints in priority order.
|
2017-11-10 18:31:37 +01:00
|
|
|
std::make_heap(breakpoints_.begin(), breakpoints_.end());
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
// Because of our priority queue, it is easy to choose a sub-optimal step to
|
|
|
|
|
// have a stable pivot. The pivot with the highest magnitude and that reduces
|
|
|
|
|
// the infeasibility the most is chosen.
|
|
|
|
|
Fractional pivot_magnitude = 0.0;
|
|
|
|
|
|
|
|
|
|
// Select the last breakpoint that still improves the infeasibility and has a
|
|
|
|
|
// numerically stable pivot.
|
|
|
|
|
*entering_col = kInvalidCol;
|
2021-03-25 22:38:06 +01:00
|
|
|
Fractional step = -1.0;
|
2017-03-28 16:07:49 +02:00
|
|
|
Fractional improvement = std::abs(cost_variation);
|
2017-11-10 18:31:37 +01:00
|
|
|
while (!breakpoints_.empty()) {
|
|
|
|
|
const ColWithRatio top = breakpoints_.front();
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
// We keep the greatest coeff_magnitude for the same ratio.
|
2021-03-25 22:38:06 +01:00
|
|
|
DCHECK(top.ratio > step ||
|
|
|
|
|
(top.ratio == step && top.coeff_magnitude <= pivot_magnitude));
|
|
|
|
|
if (top.ratio > step && top.coeff_magnitude >= pivot_magnitude) {
|
2014-07-08 09:27:02 +00:00
|
|
|
*entering_col = top.col;
|
2021-03-25 22:38:06 +01:00
|
|
|
step = top.ratio;
|
2014-07-08 09:27:02 +00:00
|
|
|
pivot_magnitude = top.coeff_magnitude;
|
|
|
|
|
}
|
|
|
|
|
improvement -= top.coeff_magnitude;
|
|
|
|
|
|
|
|
|
|
// If the variable is free, then not only do we loose the infeasibility
|
|
|
|
|
// improvment, we also render it worse if we keep going in the same
|
|
|
|
|
// direction.
|
|
|
|
|
if (can_decrease.IsSet(top.col) && can_increase.IsSet(top.col) &&
|
2017-03-28 16:07:49 +02:00
|
|
|
std::abs(reduced_costs[top.col]) > threshold) {
|
2014-07-08 09:27:02 +00:00
|
|
|
improvement -= top.coeff_magnitude;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
if (improvement <= 0.0) break;
|
2017-11-10 18:31:37 +01:00
|
|
|
std::pop_heap(breakpoints_.begin(), breakpoints_.end());
|
|
|
|
|
breakpoints_.pop_back();
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
2017-06-08 12:33:16 +02:00
|
|
|
return Status::OK();
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-28 13:42:36 +01:00
|
|
|
void EnteringVariable::SetParameters(const GlopParameters& parameters) {
|
2014-07-08 09:27:02 +00:00
|
|
|
parameters_ = parameters;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace glop
|
|
|
|
|
} // namespace operations_research
|