[GLOP] allow using approximate basis as starting basis
This commit is contained in:
@@ -123,6 +123,7 @@ RevisedSimplex::RevisedSimplex()
|
||||
void RevisedSimplex::ClearStateForNextSolve() {
|
||||
SCOPED_TIME_STAT(&function_stats_);
|
||||
solution_state_.statuses.clear();
|
||||
variable_starting_values_.clear();
|
||||
}
|
||||
|
||||
void RevisedSimplex::LoadStateForNextSolve(const BasisState& state) {
|
||||
@@ -131,6 +132,11 @@ void RevisedSimplex::LoadStateForNextSolve(const BasisState& state) {
|
||||
solution_state_has_been_set_externally_ = true;
|
||||
}
|
||||
|
||||
void RevisedSimplex::SetStartingVariableValuesForNextSolve(
|
||||
const DenseRow& values) {
|
||||
variable_starting_values_ = values;
|
||||
}
|
||||
|
||||
void RevisedSimplex::NotifyThatMatrixIsUnchangedForNextSolve() {
|
||||
notify_that_matrix_is_unchanged_ = true;
|
||||
}
|
||||
@@ -164,6 +170,7 @@ Status RevisedSimplex::Solve(const LinearProgram& lp, TimeLimit* time_limit) {
|
||||
// case we abort without resetting it, setting this allow us to still use the
|
||||
// previous state info, but we will double-check everything.
|
||||
solution_state_has_been_set_externally_ = true;
|
||||
variable_starting_values_.clear();
|
||||
|
||||
if (VLOG_IS_ON(1)) {
|
||||
ComputeNumberOfEmptyRows();
|
||||
@@ -1370,6 +1377,11 @@ Status RevisedSimplex::Initialize(const LinearProgram& lp) {
|
||||
for (const ColIndex col : variables_info_.GetIsBasicBitRow()) {
|
||||
candidates.push_back(col);
|
||||
}
|
||||
if (log_info) {
|
||||
LOG(INFO) << "The warm-start state contains " << candidates.size()
|
||||
<< " candidates for the basis (num_rows = " << num_rows_
|
||||
<< ").";
|
||||
}
|
||||
|
||||
// Optimization: Try to factorize it right away if we have the correct
|
||||
// number of element. Ideally the other path below would no require a
|
||||
@@ -1390,7 +1402,14 @@ Status RevisedSimplex::Initialize(const LinearProgram& lp) {
|
||||
|
||||
if (solve_from_scratch) {
|
||||
basis_ = basis_factorization_.ComputeInitialBasis(candidates);
|
||||
variables_info_.CorrectBasicStatus(basis_);
|
||||
const int num_super_basic =
|
||||
variables_info_.CorrectBasicStatus(basis_, variable_starting_values_);
|
||||
if (log_info) {
|
||||
LOG(INFO) << "The initial basis did not use " << num_super_basic
|
||||
<< " BASIC columns from the initial state and used "
|
||||
<< (num_rows_ - (candidates.size() - num_super_basic))
|
||||
<< " slack variables that were not marked BASIC.";
|
||||
}
|
||||
|
||||
if (InitializeFirstBasis(basis_).ok()) {
|
||||
solve_from_scratch = false;
|
||||
|
||||
@@ -154,6 +154,11 @@ class RevisedSimplex {
|
||||
// Uses the given state as a warm-start for the next Solve() call.
|
||||
void LoadStateForNextSolve(const BasisState& state);
|
||||
|
||||
// Advanced usage. While constructing the initial basis, if this is called
|
||||
// then we will use these values to decide the status of the BASIC variables
|
||||
// that are not kept in the initial basis.
|
||||
void SetStartingVariableValuesForNextSolve(const DenseRow& values);
|
||||
|
||||
// Advanced usage. Tells the next Solve() that the matrix inside the linear
|
||||
// program will not change compared to the one used the last time Solve() was
|
||||
// called. This allows to bypass the somewhat costly check of comparing both
|
||||
@@ -627,6 +632,9 @@ class RevisedSimplex {
|
||||
BasisState solution_state_;
|
||||
bool solution_state_has_been_set_externally_;
|
||||
|
||||
// If this is cleared, we assume they are none.
|
||||
DenseRow variable_starting_values_;
|
||||
|
||||
// Flag used by NotifyThatMatrixIsUnchangedForNextSolve() and changing
|
||||
// the behavior of Initialize().
|
||||
bool notify_that_matrix_is_unchanged_ = false;
|
||||
|
||||
@@ -160,17 +160,34 @@ void VariablesInfo::InitializeFromBasisState(ColIndex first_slack_col,
|
||||
}
|
||||
}
|
||||
|
||||
void VariablesInfo::CorrectBasicStatus(const RowToColMapping& basis) {
|
||||
int VariablesInfo::CorrectBasicStatus(const RowToColMapping& basis,
|
||||
const DenseRow& starting_values) {
|
||||
const ColIndex num_cols = lower_bounds_.size();
|
||||
is_basic_.ClearAndResize(num_cols);
|
||||
for (const ColIndex col : basis) {
|
||||
UpdateToBasicStatus(col);
|
||||
}
|
||||
int num_no_longer_in_basis = 0;
|
||||
for (ColIndex col(0); col < num_cols; ++col) {
|
||||
if (!is_basic_[col] && variable_status_[col] == VariableStatus::BASIC) {
|
||||
UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
|
||||
++num_no_longer_in_basis;
|
||||
if (col < starting_values.size() &&
|
||||
variable_type_[col] == VariableType::UPPER_AND_LOWER_BOUNDED) {
|
||||
const Fractional diff_ub =
|
||||
std::abs(upper_bounds_[col] - starting_values[col]);
|
||||
const Fractional diff_lb =
|
||||
std::abs(lower_bounds_[col] - starting_values[col]);
|
||||
if (diff_lb < diff_ub) {
|
||||
UpdateToNonBasicStatus(col, VariableStatus::AT_LOWER_BOUND);
|
||||
} else {
|
||||
UpdateToNonBasicStatus(col, VariableStatus::AT_UPPER_BOUND);
|
||||
}
|
||||
} else {
|
||||
UpdateToNonBasicStatus(col, DefaultVariableStatus(col));
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_no_longer_in_basis;
|
||||
}
|
||||
|
||||
void VariablesInfo::InitializeToDefaultStatus() {
|
||||
|
||||
@@ -80,9 +80,16 @@ class VariablesInfo {
|
||||
void InitializeFromBasisState(ColIndex first_slack, ColIndex num_new_cols,
|
||||
const BasisState& state);
|
||||
|
||||
// Reset to the default status any column not listed in the basis and make
|
||||
// sure all the one listed are marked as basic.
|
||||
void CorrectBasicStatus(const RowToColMapping& basis);
|
||||
// Resets to the default status any column with a BASIC status not listed in
|
||||
// the basis. Returns their number.
|
||||
//
|
||||
// If starting_values is provided, then instead of the default status, we
|
||||
// will use the bounds closest to starting_values[col] for the BASIC variable
|
||||
// not in the basic.
|
||||
//
|
||||
// Also makes sure all the column listed in basis are marked as basic.
|
||||
int CorrectBasicStatus(const RowToColMapping& basis,
|
||||
const DenseRow& starting_values);
|
||||
|
||||
// Sets all variables status to their lowest magnitude bounds. Note that there
|
||||
// will be no basic variable after this is called.
|
||||
|
||||
@@ -94,6 +94,29 @@ Fractional LpScalingHelper::VariableScalingFactor(ColIndex col) const {
|
||||
return scaler_.ColUnscalingFactor(col) * bound_scaling_factor_;
|
||||
}
|
||||
|
||||
Fractional LpScalingHelper::ScaleVariableValue(ColIndex col,
|
||||
Fractional value) const {
|
||||
return value * scaler_.ColUnscalingFactor(col) * bound_scaling_factor_;
|
||||
}
|
||||
|
||||
Fractional LpScalingHelper::ScaleReducedCost(ColIndex col,
|
||||
Fractional value) const {
|
||||
// The reduced cost move like the objective and the col scale.
|
||||
return value / scaler_.ColUnscalingFactor(col) * objective_scaling_factor_;
|
||||
}
|
||||
|
||||
Fractional LpScalingHelper::ScaleDualValue(RowIndex row,
|
||||
Fractional value) const {
|
||||
// The dual value move like the objective and the inverse of the row scale.
|
||||
return value * (scaler_.RowUnscalingFactor(row) * objective_scaling_factor_);
|
||||
}
|
||||
|
||||
Fractional LpScalingHelper::ScaleConstraintActivity(RowIndex row,
|
||||
Fractional value) const {
|
||||
// The activity move with the row_scale and the bound_scaling_factor.
|
||||
return value / scaler_.RowUnscalingFactor(row) * bound_scaling_factor_;
|
||||
}
|
||||
|
||||
Fractional LpScalingHelper::UnscaleVariableValue(ColIndex col,
|
||||
Fractional value) const {
|
||||
// Just the opposite of ScaleVariableValue().
|
||||
|
||||
@@ -57,9 +57,11 @@ class LpScalingHelper {
|
||||
// Clear all scaling coefficients.
|
||||
void Clear();
|
||||
|
||||
// A variable value in the original domain must be multiplied by this factor
|
||||
// to be in the scaled domain.
|
||||
Fractional VariableScalingFactor(ColIndex col) const;
|
||||
// Transforms value from unscaled domain to the scaled one.
|
||||
Fractional ScaleVariableValue(ColIndex col, Fractional value) const;
|
||||
Fractional ScaleReducedCost(ColIndex col, Fractional value) const;
|
||||
Fractional ScaleDualValue(RowIndex row, Fractional value) const;
|
||||
Fractional ScaleConstraintActivity(RowIndex row, Fractional value) const;
|
||||
|
||||
// Transforms corresponding value from the scaled domain to the original one.
|
||||
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const;
|
||||
@@ -76,6 +78,10 @@ class LpScalingHelper {
|
||||
void UnscaleColumnRightSolve(const RowToColMapping& basis, ColIndex col,
|
||||
ScatteredColumn* right_inverse) const;
|
||||
|
||||
// A variable value in the original domain must be multiplied by this factor
|
||||
// to be in the scaled domain.
|
||||
Fractional VariableScalingFactor(ColIndex col) const;
|
||||
|
||||
// Visible for testing. All objective coefficients of the original LP where
|
||||
// multiplied by this factor. Nothing else changed.
|
||||
Fractional BoundsScalingFactor() const { return bound_scaling_factor_; }
|
||||
|
||||
Reference in New Issue
Block a user