Updated GLOP preprocessing code.

This commit is contained in:
Vincent Furnon
2015-07-28 15:33:01 +02:00
parent 8b7f6692e7
commit 2fa0bd91ee
4 changed files with 231 additions and 201 deletions

View File

@@ -140,28 +140,30 @@ ProblemStatus LPSolver::Solve(const LinearProgram& lp) {
// Make an internal copy of the problem for the preprocessing.
VLOG(1) << "Initial problem: " << lp.GetDimensionString();
VLOG(1) << "Objective stats: " << lp.GetObjectiveStatsString();
initial_num_entries_ = lp.num_entries();
initial_num_rows_ = lp.num_constraints();
initial_num_cols_ = lp.num_variables();
current_linear_program_.PopulateFromLinearProgram(lp);
// Preprocess.
status_ = ProblemStatus::INIT;
RunPreprocessors(time_limit);
MainLpPreprocessor preprocessor;
parameters_.set_max_time_in_seconds(time_limit.GetTimeLeft());
preprocessor.SetParameters(parameters_);
const bool postsolve_is_needed = preprocessor.Run(&current_linear_program_);
// At this point, we need to initialize a ProblemSolution with the correct
// size and status.
ProblemSolution solution(current_linear_program_.num_constraints(),
current_linear_program_.num_variables());
solution.status = status_;
solution.status = preprocessor.status();
// TODO(user): find a cleaner way to pass around the time left.
parameters_.set_max_time_in_seconds(time_limit.GetTimeLeft());
RunRevisedSimplexIfNeeded(&solution);
PostprocessSolution(&solution);
if (postsolve_is_needed) preprocessor.RecoverSolution(&solution);
return LoadAndVerifySolution(lp, solution);
}
void LPSolver::Clear() {
ResizeSolution(RowIndex(0), ColIndex(0));
preprocessors_.clear();
revised_simplex_.reset(nullptr);
}
@@ -194,7 +196,7 @@ ProblemStatus LPSolver::LoadAndVerifySolution(const LinearProgram& lp,
dual_values_ = solution.dual_values;
variable_statuses_ = solution.variable_statuses;
constraint_statuses_ = solution.constraint_statuses;
status_ = solution.status;
ProblemStatus status = solution.status;
// Objective before eventually moving the primal/dual values inside their
// bounds.
@@ -209,7 +211,7 @@ ProblemStatus LPSolver::LoadAndVerifySolution(const LinearProgram& lp,
ProblemObjectiveValue(lp, dual_objective_value));
// Eventually move the primal/dual values inside their bounds.
if (status_ == ProblemStatus::OPTIMAL &&
if (status == ProblemStatus::OPTIMAL &&
parameters_.provide_strong_optimal_guarantee()) {
MovePrimalValuesWithinBounds(lp);
MoveDualValuesWithinBounds(lp);
@@ -269,7 +271,7 @@ ProblemStatus LPSolver::LoadAndVerifySolution(const LinearProgram& lp,
const double objective_error_ub = ComputeMaxExpectedObjectiveError(lp);
VLOG(1) << "Objective error <= " << objective_error_ub;
if (status_ == ProblemStatus::OPTIMAL &&
if (status == ProblemStatus::OPTIMAL &&
parameters_.provide_strong_optimal_guarantee()) {
// If the primal/dual values were moved to the bounds, then the primal/dual
// infeasibilities should be exactly zero (but not the residuals).
@@ -283,41 +285,40 @@ ProblemStatus LPSolver::LoadAndVerifySolution(const LinearProgram& lp,
}
if (rhs_perturbation_is_too_large) {
VLOG(1) << "The needed rhs perturbation is too large !!";
status_ = ProblemStatus::IMPRECISE;
status = ProblemStatus::IMPRECISE;
}
if (cost_perturbation_is_too_large) {
VLOG(1) << "The needed cost perturbation is too large !!";
status_ = ProblemStatus::IMPRECISE;
status = ProblemStatus::IMPRECISE;
}
}
// Note that we compare the values without offset nor scaling. We also need to
// compare them before we move the primal/dual values, otherwise we lose some
// precision since the values are modified independently of each other.
if (status_ == ProblemStatus::OPTIMAL) {
if (status == ProblemStatus::OPTIMAL) {
if (std::abs(primal_objective_value - dual_objective_value) >
objective_error_ub) {
VLOG(1) << "The objective gap of the final solution is too large.";
status_ = ProblemStatus::IMPRECISE;
status = ProblemStatus::IMPRECISE;
}
}
if ((status_ == ProblemStatus::OPTIMAL ||
status_ == ProblemStatus::PRIMAL_FEASIBLE) &&
if ((status == ProblemStatus::OPTIMAL ||
status == ProblemStatus::PRIMAL_FEASIBLE) &&
(primal_residual_is_too_large || primal_infeasibility_is_too_large)) {
VLOG(1) << "The primal infeasibility of the final solution is too large.";
status_ = ProblemStatus::IMPRECISE;
status = ProblemStatus::IMPRECISE;
}
if ((status_ == ProblemStatus::OPTIMAL ||
status_ == ProblemStatus::DUAL_FEASIBLE) &&
if ((status == ProblemStatus::OPTIMAL ||
status == ProblemStatus::DUAL_FEASIBLE) &&
(dual_residual_is_too_large || dual_infeasibility_is_too_large)) {
VLOG(1) << "The dual infeasibility of the final solution is too large.";
status_ = ProblemStatus::IMPRECISE;
status = ProblemStatus::IMPRECISE;
}
may_have_multiple_solutions_ =
(status_ == ProblemStatus::OPTIMAL) ?
IsOptimalSolutionOnFacet(lp) : false;
return status_;
(status == ProblemStatus::OPTIMAL) ? IsOptimalSolutionOnFacet(lp) : false;
return status;
}
bool LPSolver::IsOptimalSolutionOnFacet(const LinearProgram& lp) {
@@ -432,115 +433,6 @@ void LPSolver::ResizeSolution(RowIndex num_rows, ColIndex num_cols) {
constraint_statuses_.resize(num_rows, ConstraintStatus::FREE);
}
#define RUN_PREPROCESSOR(name) \
RunAndPushIfRelevant(std::unique_ptr<Preprocessor>(new name()), #name, \
time_limit)
// TODO(user): This should probably be moved to a MainLpPreprocessor class so it
// can be used in other places.
void LPSolver::RunPreprocessors(const TimeLimit& time_limit) {
if (parameters_.use_preprocessing()) {
RUN_PREPROCESSOR(ShiftVariableBoundsPreprocessor);
// We run it a few times because running one preprocessor may allow another
// one to remove more stuff.
const int kMaxNumPasses = 20;
for (int i = 0; i < kMaxNumPasses; ++i) {
const int old_stack_size = preprocessors_.size();
RUN_PREPROCESSOR(FixedVariablePreprocessor);
RUN_PREPROCESSOR(SingletonPreprocessor);
RUN_PREPROCESSOR(ForcingAndImpliedFreeConstraintPreprocessor);
RUN_PREPROCESSOR(FreeConstraintPreprocessor);
RUN_PREPROCESSOR(UnconstrainedVariablePreprocessor);
RUN_PREPROCESSOR(DoubletonEqualityRowPreprocessor);
RUN_PREPROCESSOR(ImpliedFreePreprocessor);
RUN_PREPROCESSOR(DoubletonFreeColumnPreprocessor);
// Abort early if none of the preprocessors did something. Technically
// this is true if none of the preprocessors above needs postsolving,
// which has exactly the same meaning for these particular preprocessors.
if (preprocessors_.size() == old_stack_size) {
// We use i here because the last pass did nothing.
VLOG(1) << "Reached fixed point after presolve pass #" << i;
break;
}
}
RUN_PREPROCESSOR(EmptyColumnPreprocessor);
RUN_PREPROCESSOR(EmptyConstraintPreprocessor);
// TODO(user): Run them in the loop above if the effect on the running time
// is good. This needs more investigation.
RUN_PREPROCESSOR(ProportionalColumnPreprocessor);
RUN_PREPROCESSOR(ProportionalRowPreprocessor);
// If DualizerPreprocessor was run, we need to do some extra preprocessing.
// This is because it currently adds a lot of zero-cost singleton columns.
const int old_stack_size = preprocessors_.size();
// TODO(user): We probably want to scale the costs before and after this
// preprocessor so that the rhs/objective of the dual are with a good
// magnitude.
RUN_PREPROCESSOR(DualizerPreprocessor);
if (old_stack_size != preprocessors_.size()) {
RUN_PREPROCESSOR(SingletonPreprocessor);
RUN_PREPROCESSOR(FreeConstraintPreprocessor);
RUN_PREPROCESSOR(UnconstrainedVariablePreprocessor);
RUN_PREPROCESSOR(EmptyColumnPreprocessor);
RUN_PREPROCESSOR(EmptyConstraintPreprocessor);
}
}
// These are implemented as preprocessors, but are not controlled by the
// use_preprocessing() parameter.
RUN_PREPROCESSOR(SingletonColumnSignPreprocessor);
RUN_PREPROCESSOR(ScalingPreprocessor);
}
#undef RUN_PREPROCESSOR
void LPSolver::RunAndPushIfRelevant(std::unique_ptr<Preprocessor> preprocessor,
const std::string& name,
const TimeLimit& time_limit) {
RETURN_IF_NULL(preprocessor);
WallTimer timer;
timer.Start();
parameters_.set_max_time_in_seconds(time_limit.GetTimeLeft());
preprocessor->SetParameters(parameters_);
if (status_ == ProblemStatus::INIT) {
// No need to run the preprocessor if current_linear_program_ is empty.
// TODO(user): without this test, the code is failing as of 2013-03-18.
if (current_linear_program_.num_variables() == 0 &&
current_linear_program_.num_constraints() == 0) {
status_ = ProblemStatus::OPTIMAL;
} else if (preprocessor->Run(&current_linear_program_)) {
const EntryIndex new_num_entries = current_linear_program_.num_entries();
VLOG(1) << StringPrintf(
"%s(%fs): %d(%d) rows, %d(%d) columns, %lld(%lld) entries.",
name.c_str(), timer.Get(),
current_linear_program_.num_constraints().value(),
(current_linear_program_.num_constraints() - initial_num_rows_)
.value(),
current_linear_program_.num_variables().value(),
(current_linear_program_.num_variables() - initial_num_cols_).value(),
// static_cast<int64> is needed because the Android port uses int32.
static_cast<int64>(new_num_entries.value()),
static_cast<int64>(new_num_entries.value() -
initial_num_entries_.value()));
status_ = preprocessor->status();
preprocessors_.push_back(std::move(preprocessor));
return;
} else {
// Even if a preprocessor returns false (i.e. no need for postsolve), it
// can detect an issue with the problem.
status_ = preprocessor->status();
if (status_ != ProblemStatus::INIT) {
VLOG(1) << name << " detected that the problem is "
<< GetProblemStatusString(status_);
}
}
}
}
void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution* solution) {
// Note that the transpose matrix is no longer needed at this point.
// This helps reduce the peak memory usage of the solver.
@@ -575,13 +467,6 @@ void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution* solution) {
}
}
void LPSolver::PostprocessSolution(ProblemSolution* solution) {
while (!preprocessors_.empty()) {
preprocessors_.back()->StoreSolution(solution);
preprocessors_.pop_back();
}
}
namespace {
void LogVariableStatusError(ColIndex col, Fractional value,

View File

@@ -138,22 +138,10 @@ class LPSolver {
void MovePrimalValuesWithinBounds(const LinearProgram& lp);
void MoveDualValuesWithinBounds(const LinearProgram& lp);
// Runs all preprocessors in sequence.
void RunPreprocessors(const TimeLimit& time_limit);
// Runs the given preprocessor and pushes it when relevant (i.e. when it did
// something) on the preprocessors_ stack.
void RunAndPushIfRelevant(std::unique_ptr<Preprocessor> preprocessor,
const std::string& name, const TimeLimit& time_limit);
// Runs the revised simplex algorithm if needed (i.e. if the program was not
// already solved by the preprocessors).
void RunRevisedSimplexIfNeeded(ProblemSolution* solution);
// Postprocess the solution by calling the StoreSolution() of the
// preprocessors in the reverse order in which their where applied.
void PostprocessSolution(ProblemSolution* solution);
// Checks that the returned solution values and statuses are consistent.
// Returns true if this is the case. See the code for the exact check
// performed.
@@ -219,20 +207,16 @@ class LPSolver {
double ComputeReducedCostInfeasibility(const LinearProgram& lp,
bool* is_too_large);
// Dimension of the linear program given to the last Solve().
// This is used for displaying purpose only.
EntryIndex initial_num_entries_;
RowIndex initial_num_rows_;
ColIndex initial_num_cols_;
// On a call to Solve(), this is initialized to an exact copy of the given
// linear program. It is later modified by the preprocessors and then solved
// by the revised simplex.
//
// This is not efficient memory-wise but allows to check optimality with
// respect to the given LinearProgram that is guaranteed to not have been
// modified. It also allows for a nicer Solve() API with a const
// LinearProgram& input.
LinearProgram current_linear_program_;
// Stack of preprocessors currently applied to the current linear program.
std::vector<std::unique_ptr<Preprocessor>> preprocessors_;
// The revised simplex solver.
std::unique_ptr<RevisedSimplex> revised_simplex_;
@@ -241,7 +225,6 @@ class LPSolver {
// The current ProblemSolution.
// TODO(user): use a ProblemSolution directly?
ProblemStatus status_;
DenseRow primal_values_;
DenseColumn dual_values_;
VariableStatusRow variable_statuses_;

View File

@@ -41,6 +41,132 @@ Preprocessor::Preprocessor()
: status_(ProblemStatus::INIT), parameters_(), in_mip_context_(false) {}
Preprocessor::~Preprocessor() {}
// --------------------------------------------------------
// MainLpPreprocessor
// --------------------------------------------------------
#define RUN_PREPROCESSOR(name) \
RunAndPushIfRelevant(std::unique_ptr<Preprocessor>(new name()), #name, \
&time_limit, lp)
bool MainLpPreprocessor::Run(LinearProgram* lp) {
RETURN_VALUE_IF_NULL(lp, false);
TimeLimit time_limit(parameters_.max_time_in_seconds());
initial_num_rows_ = lp->num_constraints();
initial_num_cols_ = lp->num_variables();
initial_num_entries_ = lp->num_entries();
if (parameters_.use_preprocessing()) {
RUN_PREPROCESSOR(ShiftVariableBoundsPreprocessor);
// We run it a few times because running one preprocessor may allow another
// one to remove more stuff.
const int kMaxNumPasses = 20;
for (int i = 0; i < kMaxNumPasses; ++i) {
const int old_stack_size = preprocessors_.size();
RUN_PREPROCESSOR(FixedVariablePreprocessor);
RUN_PREPROCESSOR(SingletonPreprocessor);
RUN_PREPROCESSOR(ForcingAndImpliedFreeConstraintPreprocessor);
RUN_PREPROCESSOR(FreeConstraintPreprocessor);
RUN_PREPROCESSOR(UnconstrainedVariablePreprocessor);
RUN_PREPROCESSOR(DoubletonEqualityRowPreprocessor);
RUN_PREPROCESSOR(ImpliedFreePreprocessor);
RUN_PREPROCESSOR(DoubletonFreeColumnPreprocessor);
// Abort early if none of the preprocessors did something. Technically
// this is true if none of the preprocessors above needs postsolving,
// which has exactly the same meaning for these particular preprocessors.
if (preprocessors_.size() == old_stack_size) {
// We use i here because the last pass did nothing.
VLOG(1) << "Reached fixed point after presolve pass #" << i;
break;
}
}
RUN_PREPROCESSOR(EmptyColumnPreprocessor);
RUN_PREPROCESSOR(EmptyConstraintPreprocessor);
// TODO(user): Run them in the loop above if the effect on the running time
// is good. This needs more investigation.
RUN_PREPROCESSOR(ProportionalColumnPreprocessor);
RUN_PREPROCESSOR(ProportionalRowPreprocessor);
// If DualizerPreprocessor was run, we need to do some extra preprocessing.
// This is because it currently adds a lot of zero-cost singleton columns.
const int old_stack_size = preprocessors_.size();
// TODO(user): We probably want to scale the costs before and after this
// preprocessor so that the rhs/objective of the dual are with a good
// magnitude.
RUN_PREPROCESSOR(DualizerPreprocessor);
if (old_stack_size != preprocessors_.size()) {
RUN_PREPROCESSOR(SingletonPreprocessor);
RUN_PREPROCESSOR(FreeConstraintPreprocessor);
RUN_PREPROCESSOR(UnconstrainedVariablePreprocessor);
RUN_PREPROCESSOR(EmptyColumnPreprocessor);
RUN_PREPROCESSOR(EmptyConstraintPreprocessor);
}
}
// These are implemented as preprocessors, but are not controlled by the
// use_preprocessing() parameter.
RUN_PREPROCESSOR(SingletonColumnSignPreprocessor);
RUN_PREPROCESSOR(ScalingPreprocessor);
return !preprocessors_.empty();
}
#undef RUN_PREPROCESSOR
void MainLpPreprocessor::RunAndPushIfRelevant(
std::unique_ptr<Preprocessor> preprocessor, const std::string& name,
TimeLimit* time_limit, LinearProgram* lp) {
RETURN_IF_NULL(preprocessor);
if (status_ != ProblemStatus::INIT || time_limit->LimitReached()) return;
WallTimer timer;
timer.Start();
parameters_.set_max_time_in_seconds(time_limit->GetTimeLeft());
preprocessor->SetParameters(parameters_);
// No need to run the preprocessor if the lp is empty.
// TODO(user): without this test, the code is failing as of 2013-03-18.
if (lp->num_variables() == 0 && lp->num_constraints() == 0) {
status_ = ProblemStatus::OPTIMAL;
return;
}
if (preprocessor->Run(lp)) {
const EntryIndex new_num_entries = lp->num_entries();
VLOG(1) << StringPrintf(
"%s(%fs): %d(%d) rows, %d(%d) columns, %lld(%lld) entries.",
name.c_str(), timer.Get(), lp->num_constraints().value(),
(lp->num_constraints() - initial_num_rows_).value(),
lp->num_variables().value(),
(lp->num_variables() - initial_num_cols_).value(),
// static_cast<int64> is needed because the Android port uses int32.
static_cast<int64>(new_num_entries.value()),
static_cast<int64>(new_num_entries.value() -
initial_num_entries_.value()));
status_ = preprocessor->status();
preprocessors_.push_back(std::move(preprocessor));
return;
} else {
// Even if a preprocessor returns false (i.e. no need for postsolve), it
// can detect an issue with the problem.
status_ = preprocessor->status();
if (status_ != ProblemStatus::INIT) {
VLOG(1) << name << " detected that the problem is "
<< GetProblemStatusString(status_);
}
}
}
void MainLpPreprocessor::RecoverSolution(ProblemSolution* solution) const {
while (!preprocessors_.empty()) {
preprocessors_.back()->RecoverSolution(solution);
preprocessors_.pop_back();
}
}
// --------------------------------------------------------
// ColumnDeletionHelper
// --------------------------------------------------------
@@ -249,7 +375,7 @@ bool EmptyColumnPreprocessor::Run(LinearProgram* linear_program) {
return !column_deletion_helper_.IsEmpty();
}
void EmptyColumnPreprocessor::StoreSolution(ProblemSolution* solution) const {
void EmptyColumnPreprocessor::RecoverSolution(ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
column_deletion_helper_.RestoreDeletedColumns(solution);
}
@@ -530,7 +656,7 @@ bool ProportionalColumnPreprocessor::Run(LinearProgram* lp) {
return !column_deletion_helper_.IsEmpty();
}
void ProportionalColumnPreprocessor::StoreSolution(
void ProportionalColumnPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
column_deletion_helper_.RestoreDeletedColumns(solution);
@@ -818,7 +944,7 @@ bool ProportionalRowPreprocessor::Run(LinearProgram* lp) {
return !row_deletion_helper_.IsEmpty();
}
void ProportionalRowPreprocessor::StoreSolution(
void ProportionalRowPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
row_deletion_helper_.RestoreDeletedRows(solution);
@@ -912,7 +1038,8 @@ bool FixedVariablePreprocessor::Run(LinearProgram* lp) {
return !column_deletion_helper_.IsEmpty();
}
void FixedVariablePreprocessor::StoreSolution(ProblemSolution* solution) const {
void FixedVariablePreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
column_deletion_helper_.RestoreDeletedColumns(solution);
}
@@ -1058,7 +1185,7 @@ bool ForcingAndImpliedFreeConstraintPreprocessor::Run(LinearProgram* lp) {
return !column_deletion_helper_.IsEmpty();
}
void ForcingAndImpliedFreeConstraintPreprocessor::StoreSolution(
void ForcingAndImpliedFreeConstraintPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
column_deletion_helper_.RestoreDeletedColumns(solution);
@@ -1325,7 +1452,7 @@ bool ImpliedFreePreprocessor::Run(LinearProgram* lp) {
return num_implied_free_variables > 0;
}
void ImpliedFreePreprocessor::StoreSolution(ProblemSolution* solution) const {
void ImpliedFreePreprocessor::RecoverSolution(ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
const ColIndex num_cols = solution->variable_statuses.size();
for (ColIndex col(0); col < num_cols; ++col) {
@@ -1454,7 +1581,7 @@ bool DoubletonFreeColumnPreprocessor::Run(LinearProgram* lp) {
return false;
}
void DoubletonFreeColumnPreprocessor::StoreSolution(
void DoubletonFreeColumnPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
row_deletion_helper_.RestoreDeletedRows(solution);
for (const RestoreInfo& r : Reverse(restore_stack_)) {
@@ -1627,7 +1754,7 @@ bool UnconstrainedVariablePreprocessor::Run(LinearProgram* lp) {
return false;
} else {
// We can remove this column and all its constraints! We just need to
// choose a variable value during the call to StoreSolution() that
// choose a variable value during the call to RecoverSolution() that
// makes all the constraint satisfiable. Test this on bnl2.mps.
if (!in_mip_context_) {
// TODO(user): this also works if the variable is integer, but we
@@ -1663,7 +1790,7 @@ bool UnconstrainedVariablePreprocessor::Run(LinearProgram* lp) {
return !column_deletion_helper_.IsEmpty() || !row_deletion_helper_.IsEmpty();
}
void UnconstrainedVariablePreprocessor::StoreSolution(
void UnconstrainedVariablePreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
column_deletion_helper_.RestoreDeletedColumns(solution);
@@ -1746,7 +1873,7 @@ bool FreeConstraintPreprocessor::Run(LinearProgram* linear_program) {
return !row_deletion_helper_.IsEmpty();
}
void FreeConstraintPreprocessor::StoreSolution(
void FreeConstraintPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
row_deletion_helper_.RestoreDeletedRows(solution);
@@ -1791,7 +1918,7 @@ bool EmptyConstraintPreprocessor::Run(LinearProgram* lp) {
return !row_deletion_helper_.IsEmpty();
}
void EmptyConstraintPreprocessor::StoreSolution(
void EmptyConstraintPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
row_deletion_helper_.RestoreDeletedRows(solution);
@@ -2361,7 +2488,7 @@ bool SingletonPreprocessor::Run(LinearProgram* lp) {
return !column_deletion_helper_.IsEmpty() || !row_deletion_helper_.IsEmpty();
}
void SingletonPreprocessor::StoreSolution(ProblemSolution* solution) const {
void SingletonPreprocessor::RecoverSolution(ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
// Note that the two deletion helpers must restore 0.0 values in the positions
@@ -2482,7 +2609,7 @@ bool RemoveNearZeroEntriesPreprocessor::Run(LinearProgram* lp) {
return false;
}
void RemoveNearZeroEntriesPreprocessor::StoreSolution(
void RemoveNearZeroEntriesPreprocessor::RecoverSolution(
ProblemSolution* solution) const {}
// --------------------------------------------------------
@@ -2517,7 +2644,7 @@ bool SingletonColumnSignPreprocessor::Run(LinearProgram* linear_program) {
return !changed_columns_.empty();
}
void SingletonColumnSignPreprocessor::StoreSolution(
void SingletonColumnSignPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
for (int i = 0; i < changed_columns_.size(); ++i) {
@@ -2700,7 +2827,7 @@ bool DoubletonEqualityRowPreprocessor::Run(LinearProgram* lp) {
return !column_deletion_helper_.IsEmpty();
}
void DoubletonEqualityRowPreprocessor::StoreSolution(
void DoubletonEqualityRowPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
column_deletion_helper_.RestoreDeletedColumns(solution);
@@ -2894,7 +3021,7 @@ bool DualizerPreprocessor::Run(LinearProgram* lp) {
// Note(user): This assumes that LinearProgram.PopulateFromDual() uses
// the first ColIndex and RowIndex for the rows and columns of the given
// problem.
void DualizerPreprocessor::StoreSolution(ProblemSolution* solution) const {
void DualizerPreprocessor::RecoverSolution(ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
DenseRow new_primal_values(primal_num_cols_, 0.0);
@@ -3094,7 +3221,7 @@ bool ShiftVariableBoundsPreprocessor::Run(LinearProgram* lp) {
return true;
}
void ShiftVariableBoundsPreprocessor::StoreSolution(
void ShiftVariableBoundsPreprocessor::RecoverSolution(
ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
const ColIndex num_cols = solution->variable_statuses.size();
@@ -3167,7 +3294,7 @@ bool ScalingPreprocessor::Run(LinearProgram* lp) {
return true;
}
void ScalingPreprocessor::StoreSolution(ProblemSolution* solution) const {
void ScalingPreprocessor::RecoverSolution(ProblemSolution* solution) const {
RETURN_IF_NULL(solution);
scaler_.ScaleRowVector(false, &(solution->primal_values));
scaler_.ScaleColumnVector(false, &(solution->dual_values));

View File

@@ -21,13 +21,14 @@
#ifndef OR_TOOLS_GLOP_PREPROCESSOR_H_
#define OR_TOOLS_GLOP_PREPROCESSOR_H_
#include "base/unique_ptr.h"
#include "glop/parameters.pb.h"
#include "glop/revised_simplex.h"
#include "lp_data/lp_data.h"
#include "lp_data/lp_types.h"
#include "lp_data/matrix_scaler.h"
namespace operations_research {
namespace glop {
@@ -44,7 +45,7 @@ class Preprocessor {
virtual ~Preprocessor();
// Runs the preprocessor by modifying the given linear program. Returns true
// if a postsolve step will be needed (i.e. StoreSolution() is not the
// if a postsolve step will be needed (i.e. RecoverSolution() is not the
// identity function). Also updates status_ to something different from
// ProblemStatus::INIT if the problem was solved (including bad statuses
// like ProblemStatus::ABNORMAL, ProblemStatus::INFEASIBLE, etc.).
@@ -53,7 +54,7 @@ class Preprocessor {
// Stores the optimal solution of the linear program that was passed to
// Run(). The given solution needs to be set to the optimal solution of the
// linear program "modified" by Run().
virtual void StoreSolution(ProblemSolution* solution) const = 0;
virtual void RecoverSolution(ProblemSolution* solution) const = 0;
// Returns the status of the preprocessor.
// A status different from ProblemStatus::INIT means that the problem is
@@ -82,6 +83,40 @@ class Preprocessor {
DISALLOW_COPY_AND_ASSIGN(Preprocessor);
};
// --------------------------------------------------------
// MainLpPreprocessor
// --------------------------------------------------------
// This is the main LP preprocessor responsible for calling all the other
// preprocessors in this file, possibly more than once.
class MainLpPreprocessor : public Preprocessor {
public:
MainLpPreprocessor() {}
~MainLpPreprocessor() override {}
bool Run(LinearProgram* linear_program) override;
void RecoverSolution(ProblemSolution* solution) const override;
private:
// Runs the given preprocessor and push it on preprocessors_ for the postsolve
// step when needed.
void RunAndPushIfRelevant(std::unique_ptr<Preprocessor> preprocessor,
const std::string& name, TimeLimit* time_limit,
LinearProgram* lp);
// Stack of preprocessors currently applied to the lp that needs postsolve.
//
// TODO(user): This is mutable so that the preprocessor can be freed as soon
// as their RecoverSolution() is called. Make RecoverSolution() non-const or
// remove this optimization?
mutable std::vector<std::unique_ptr<Preprocessor>> preprocessors_;
// Initial dimension of the lp given to Run(), for displaying purpose.
EntryIndex initial_num_entries_;
RowIndex initial_num_rows_;
ColIndex initial_num_cols_;
DISALLOW_COPY_AND_ASSIGN(MainLpPreprocessor);
};
// --------------------------------------------------------
// ColumnDeletionHelper
// --------------------------------------------------------
@@ -185,7 +220,7 @@ class EmptyColumnPreprocessor : public Preprocessor {
EmptyColumnPreprocessor() {}
~EmptyColumnPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
ColumnDeletionHelper column_deletion_helper_;
@@ -210,7 +245,7 @@ class ProportionalColumnPreprocessor : public Preprocessor {
ProportionalColumnPreprocessor() {}
~ProportionalColumnPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
private:
@@ -248,7 +283,7 @@ class ProportionalRowPreprocessor : public Preprocessor {
ProportionalRowPreprocessor() {}
~ProportionalRowPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
// Informations about proportional rows, only filled for such rows.
@@ -341,7 +376,7 @@ class SingletonPreprocessor : public Preprocessor {
SingletonPreprocessor() {}
~SingletonPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
private:
@@ -422,7 +457,7 @@ class FixedVariablePreprocessor : public Preprocessor {
FixedVariablePreprocessor() {}
~FixedVariablePreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
ColumnDeletionHelper column_deletion_helper_;
@@ -454,7 +489,7 @@ class ForcingAndImpliedFreeConstraintPreprocessor : public Preprocessor {
ForcingAndImpliedFreeConstraintPreprocessor() {}
~ForcingAndImpliedFreeConstraintPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
bool lp_is_maximization_problem_;
@@ -496,12 +531,12 @@ class ImpliedFreePreprocessor : public Preprocessor {
ImpliedFreePreprocessor() {}
~ImpliedFreePreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
private:
// This preprocessor adds fixed offsets to some variables. We remember those
// here to un-offset them in StoreSolution().
// here to un-offset them in RecoverSolution().
DenseRow variable_offsets_;
// This preprocessor causes some variables who would normally be
@@ -542,7 +577,7 @@ class DoubletonFreeColumnPreprocessor : public Preprocessor {
DoubletonFreeColumnPreprocessor() {}
~DoubletonFreeColumnPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
private:
@@ -587,7 +622,7 @@ class UnconstrainedVariablePreprocessor : public Preprocessor {
UnconstrainedVariablePreprocessor() {}
~UnconstrainedVariablePreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
// Removes the given variable and all the rows in which it appears: If a
// variable is unconstrained with a zero cost, then all the constraints in
@@ -625,7 +660,7 @@ class FreeConstraintPreprocessor : public Preprocessor {
FreeConstraintPreprocessor() {}
~FreeConstraintPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
RowDeletionHelper row_deletion_helper_;
@@ -641,7 +676,7 @@ class EmptyConstraintPreprocessor : public Preprocessor {
EmptyConstraintPreprocessor() {}
~EmptyConstraintPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
RowDeletionHelper row_deletion_helper_;
@@ -663,7 +698,7 @@ class RemoveNearZeroEntriesPreprocessor : public Preprocessor {
RemoveNearZeroEntriesPreprocessor() {}
~RemoveNearZeroEntriesPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
DISALLOW_COPY_AND_ASSIGN(RemoveNearZeroEntriesPreprocessor);
@@ -681,7 +716,7 @@ class SingletonColumnSignPreprocessor : public Preprocessor {
SingletonColumnSignPreprocessor() {}
~SingletonColumnSignPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
std::vector<ColIndex> changed_columns_;
@@ -699,7 +734,7 @@ class DoubletonEqualityRowPreprocessor : public Preprocessor {
DoubletonEqualityRowPreprocessor() {}
~DoubletonEqualityRowPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
private:
@@ -764,7 +799,7 @@ class DualizerPreprocessor : public Preprocessor {
DualizerPreprocessor() {}
~DualizerPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final {
LOG(FATAL) << "In the presence of integer variables, "
<< "there is no notion of a dual problem.";
@@ -821,7 +856,7 @@ class ShiftVariableBoundsPreprocessor : public Preprocessor {
ShiftVariableBoundsPreprocessor() {}
~ShiftVariableBoundsPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
private:
// Contains for each variable by how much its bounds where shifted during
@@ -845,7 +880,7 @@ class ScalingPreprocessor : public Preprocessor {
ScalingPreprocessor() {}
~ScalingPreprocessor() final {}
bool Run(LinearProgram* linear_program) final;
void StoreSolution(ProblemSolution* solution) const final;
void RecoverSolution(ProblemSolution* solution) const final;
void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
private: