fix issue132
This commit is contained in:
@@ -75,7 +75,6 @@ BopSolver::BopSolver(const LinearBooleanProblem& problem)
|
||||
: problem_(problem),
|
||||
problem_state_(problem),
|
||||
parameters_(),
|
||||
external_boolean_as_limit_(nullptr),
|
||||
stats_("BopSolver") {
|
||||
SCOPED_TIME_STAT(&stats_);
|
||||
CHECK_OK(sat::ValidateBooleanProblem(problem));
|
||||
@@ -86,7 +85,6 @@ BopSolver::~BopSolver() { IF_STATS_ENABLED(VLOG(1) << stats_.StatString()); }
|
||||
BopSolveStatus BopSolver::Solve() {
|
||||
std::unique_ptr<TimeLimit> time_limit =
|
||||
TimeLimit::FromParameters(parameters_);
|
||||
time_limit->RegisterExternalBooleanAsLimit(external_boolean_as_limit_);
|
||||
return SolveWithTimeLimit(time_limit.get());
|
||||
}
|
||||
|
||||
@@ -145,7 +143,6 @@ BopSolveStatus BopSolver::InternalMultithreadSolver(TimeLimit* time_limit) {
|
||||
BopSolveStatus BopSolver::Solve(const BopSolution& first_solution) {
|
||||
std::unique_ptr<TimeLimit> time_limit =
|
||||
TimeLimit::FromParameters(parameters_);
|
||||
time_limit->RegisterExternalBooleanAsLimit(external_boolean_as_limit_);
|
||||
return SolveWithTimeLimit(first_solution, time_limit.get());
|
||||
}
|
||||
|
||||
@@ -185,11 +182,6 @@ double BopSolver::GetScaledGap() const {
|
||||
std::abs(problem_state_.solution().GetScaledCost());
|
||||
}
|
||||
|
||||
void BopSolver::RegisterExternalBooleanAsLimit(
|
||||
const bool* external_boolean_as_limit) {
|
||||
external_boolean_as_limit_ = external_boolean_as_limit;
|
||||
}
|
||||
|
||||
void BopSolver::UpdateParameters() {
|
||||
if (parameters_.solver_optimizer_sets_size() == 0) {
|
||||
// No user defined optimizers, use the default std::string to define the
|
||||
|
||||
@@ -73,11 +73,6 @@ class BopSolver {
|
||||
BopSolveStatus Solve(const BopSolution& first_solution);
|
||||
|
||||
// Runs the solver with an external time limit.
|
||||
// NOTE(user): These methods do not use the external boolean registered as
|
||||
// a limit within this class, but the caller can register the same boolean
|
||||
// directly into the time limit object to achieve the same effect. Eventually,
|
||||
// we should remove the explicit 'external_boolean_as_limit_' from this class,
|
||||
// and use only the boolean registered in the time limit object.
|
||||
BopSolveStatus SolveWithTimeLimit(TimeLimit* time_limit);
|
||||
BopSolveStatus SolveWithTimeLimit(const BopSolution& first_solution,
|
||||
TimeLimit* time_limit);
|
||||
@@ -93,11 +88,6 @@ class BopSolver {
|
||||
double GetScaledBestBound() const;
|
||||
double GetScaledGap() const;
|
||||
|
||||
// Sets an external limit to stop the search when the Boolean value becomes
|
||||
// true. Note that the Solve() call may still linger for a while depending on
|
||||
// the conditions.
|
||||
void RegisterExternalBooleanAsLimit(const bool* external_boolean_as_limit);
|
||||
|
||||
private:
|
||||
void UpdateParameters();
|
||||
BopSolveStatus InternalMonothreadSolver(TimeLimit* time_limit);
|
||||
@@ -106,7 +96,6 @@ class BopSolver {
|
||||
const LinearBooleanProblem& problem_;
|
||||
ProblemState problem_state_;
|
||||
BopParameters parameters_;
|
||||
const bool* external_boolean_as_limit_;
|
||||
|
||||
mutable StatsGroup stats_;
|
||||
};
|
||||
|
||||
@@ -1025,20 +1025,29 @@ void RunOneBop(const BopParameters& parameters, int problem_index,
|
||||
} // anonymous namespace
|
||||
|
||||
IntegralSolver::IntegralSolver()
|
||||
: parameters_(),
|
||||
variable_values_(),
|
||||
objective_value_(0.0),
|
||||
interrupt_solve_(false) {}
|
||||
: parameters_(), variable_values_(), objective_value_(0.0) {}
|
||||
|
||||
BopSolveStatus IntegralSolver::Solve(const LinearProgram& linear_problem) {
|
||||
return Solve(linear_problem, DenseRow());
|
||||
}
|
||||
|
||||
BopSolveStatus IntegralSolver::SolveWithTimeLimit(
|
||||
const LinearProgram& linear_problem, TimeLimit* time_limit) {
|
||||
return SolveWithTimeLimit(linear_problem, DenseRow(), time_limit);
|
||||
}
|
||||
|
||||
BopSolveStatus IntegralSolver::Solve(
|
||||
const LinearProgram& linear_problem,
|
||||
const DenseRow& user_provided_intial_solution) {
|
||||
interrupt_solve_ = false;
|
||||
std::unique_ptr<TimeLimit> time_limit =
|
||||
TimeLimit::FromParameters(parameters_);
|
||||
return SolveWithTimeLimit(linear_problem, user_provided_intial_solution,
|
||||
time_limit.get());
|
||||
}
|
||||
|
||||
BopSolveStatus IntegralSolver::SolveWithTimeLimit(
|
||||
const LinearProgram& linear_problem,
|
||||
const DenseRow& user_provided_intial_solution, TimeLimit* time_limit) {
|
||||
// We make a copy so that we can clear it if the presolve is active.
|
||||
DenseRow initial_solution = user_provided_intial_solution;
|
||||
if (initial_solution.size() > 0) {
|
||||
@@ -1050,9 +1059,6 @@ BopSolveStatus IntegralSolver::Solve(
|
||||
// Some code path requires to copy the given linear_problem. When this
|
||||
// happens, we will simply change the target of this pointer.
|
||||
LinearProgram const* lp = &linear_problem;
|
||||
std::unique_ptr<TimeLimit> time_limit =
|
||||
TimeLimit::FromParameters(parameters_);
|
||||
time_limit->RegisterExternalBooleanAsLimit(&interrupt_solve_);
|
||||
|
||||
|
||||
BopSolveStatus status;
|
||||
@@ -1070,8 +1076,8 @@ BopSolveStatus IntegralSolver::Solve(
|
||||
BopSolveStatus::INVALID_PROBLEM);
|
||||
|
||||
for (int i = 0; i < num_sub_problems; ++i) {
|
||||
RunOneBop(parameters_, i, initial_solution, time_limit.get(),
|
||||
&decomposer, &(variable_values[i]), &(objective_values[i]),
|
||||
RunOneBop(parameters_, i, initial_solution, time_limit, &decomposer,
|
||||
&(variable_values[i]), &(objective_values[i]),
|
||||
&(best_bounds[i]), &(statuses[i]));
|
||||
}
|
||||
|
||||
@@ -1096,11 +1102,11 @@ BopSolveStatus IntegralSolver::Solve(
|
||||
CheckSolution(*lp, variable_values_);
|
||||
} else {
|
||||
status =
|
||||
InternalSolve(*lp, parameters_, initial_solution, time_limit.get(),
|
||||
InternalSolve(*lp, parameters_, initial_solution, time_limit,
|
||||
&variable_values_, &objective_value_, &best_bound_);
|
||||
}
|
||||
} else {
|
||||
status = InternalSolve(*lp, parameters_, initial_solution, time_limit.get(),
|
||||
status = InternalSolve(*lp, parameters_, initial_solution, time_limit,
|
||||
&variable_values_, &objective_value_, &best_bound_);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "bop/bop_parameters.pb.h"
|
||||
#include "bop/bop_types.h"
|
||||
#include "lp_data/lp_data.h"
|
||||
#include "util/time_limit.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace bop {
|
||||
@@ -39,12 +40,17 @@ class IntegralSolver {
|
||||
// Solves the given linear program and returns the solve status.
|
||||
BopSolveStatus Solve(const glop::LinearProgram& linear_problem)
|
||||
MUST_USE_RESULT;
|
||||
BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram& linear_problem,
|
||||
TimeLimit* time_limit) MUST_USE_RESULT;
|
||||
|
||||
// Same as Solve() but starts from the given solution.
|
||||
// TODO(user): Change the API to accept a partial solution instead since the
|
||||
// underlying solver supports it.
|
||||
BopSolveStatus Solve(const glop::LinearProgram& linear_problem,
|
||||
const glop::DenseRow& initial_solution) MUST_USE_RESULT;
|
||||
BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram& linear_problem,
|
||||
const glop::DenseRow& initial_solution,
|
||||
TimeLimit* time_limit) MUST_USE_RESULT;
|
||||
|
||||
// Returns the objective value of the solution with its offset.
|
||||
glop::Fractional objective_value() const { return objective_value_; }
|
||||
@@ -56,17 +62,11 @@ class IntegralSolver {
|
||||
// solution is found.
|
||||
const glop::DenseRow& variable_values() const { return variable_values_; }
|
||||
|
||||
// Interrupts the current Solve() execution.
|
||||
// Note that the Solve() call may still linger for a while depending on the
|
||||
// conditions.
|
||||
void InterruptSolve() { interrupt_solve_ = true; }
|
||||
|
||||
private:
|
||||
BopParameters parameters_;
|
||||
glop::DenseRow variable_values_;
|
||||
glop::Fractional objective_value_;
|
||||
glop::Fractional best_bound_;
|
||||
bool interrupt_solve_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(IntegralSolver);
|
||||
};
|
||||
|
||||
@@ -102,7 +102,7 @@ class BopInterface : public MPSolverInterface {
|
||||
void SetPresolveMode(int value) override;
|
||||
void SetScalingMode(int value) override;
|
||||
void SetLpAlgorithm(int value) override;
|
||||
bool ReadParameterFile(const std::string& filename) override;
|
||||
bool SetSolverSpecificParametersAsString(const std::string& parameters) override;
|
||||
|
||||
private:
|
||||
void NonIncrementalChange();
|
||||
@@ -113,6 +113,7 @@ class BopInterface : public MPSolverInterface {
|
||||
std::vector<MPSolver::BasisStatus> row_status_;
|
||||
bop::BopParameters parameters_;
|
||||
double best_objective_bound_;
|
||||
bool interrupt_solver_;
|
||||
};
|
||||
|
||||
BopInterface::BopInterface(MPSolver* const solver)
|
||||
@@ -121,7 +122,8 @@ BopInterface::BopInterface(MPSolver* const solver)
|
||||
bop_solver_(),
|
||||
column_status_(),
|
||||
row_status_(),
|
||||
parameters_() {}
|
||||
parameters_(),
|
||||
interrupt_solver_(false) {}
|
||||
|
||||
BopInterface::~BopInterface() {}
|
||||
|
||||
@@ -159,10 +161,14 @@ MPSolver::ResultStatus BopInterface::Solve(const MPSolverParameters& param) {
|
||||
solver_->SetSolverSpecificParametersAsString(
|
||||
solver_->solver_specific_parameter_string_);
|
||||
bop_solver_.SetParameters(parameters_);
|
||||
std::unique_ptr<TimeLimit> time_limit =
|
||||
TimeLimit::FromParameters(parameters_);
|
||||
time_limit->RegisterExternalBooleanAsLimit(&interrupt_solver_);
|
||||
const bop::BopSolveStatus status =
|
||||
initial_solution.empty()
|
||||
? bop_solver_.Solve(linear_program_)
|
||||
: bop_solver_.Solve(linear_program_, initial_solution);
|
||||
? bop_solver_.SolveWithTimeLimit(linear_program_, time_limit.get())
|
||||
: bop_solver_.SolveWithTimeLimit(linear_program_, initial_solution,
|
||||
time_limit.get());
|
||||
|
||||
// The solution must be marked as synchronized even when no solution exists.
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
@@ -195,6 +201,7 @@ MPSolver::ResultStatus BopInterface::Solve(const MPSolverParameters& param) {
|
||||
void BopInterface::Reset() {
|
||||
ResetExtractionInformation();
|
||||
linear_program_.Clear();
|
||||
interrupt_solver_ = false;
|
||||
}
|
||||
|
||||
void BopInterface::SetOptimizationDirection(bool maximize) {
|
||||
@@ -275,7 +282,7 @@ std::string BopInterface::SolverVersion() const {
|
||||
}
|
||||
|
||||
bool BopInterface::InterruptSolve() {
|
||||
bop_solver_.InterruptSolve();
|
||||
interrupt_solver_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -362,12 +369,9 @@ void BopInterface::SetPresolveMode(int value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool BopInterface::ReadParameterFile(const std::string& filename) {
|
||||
std::string params;
|
||||
if (!file::GetContents(filename, ¶ms, file::Defaults()).ok()) {
|
||||
return false;
|
||||
}
|
||||
const bool ok = google::protobuf::TextFormat::MergeFromString(params, ¶meters_);
|
||||
bool BopInterface::SetSolverSpecificParametersAsString(
|
||||
const std::string& parameters) {
|
||||
const bool ok = google::protobuf::TextFormat::MergeFromString(parameters, ¶meters_);
|
||||
bop_solver_.SetParameters(parameters_);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -401,7 +401,11 @@ MPSolver::ResultStatus CBCInterface::Solve(const MPSolverParameters& param) {
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result_status_ = MPSolver::FEASIBLE;
|
||||
if (model.bestSolution() != NULL) {
|
||||
result_status_ = MPSolver::FEASIBLE;
|
||||
} else {
|
||||
result_status_ = MPSolver::NOT_SOLVED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result_status_ = MPSolver::ABNORMAL;
|
||||
|
||||
@@ -402,106 +402,112 @@ void CLPInterface::ExtractObjective() {
|
||||
|
||||
// Extracts model and solve the LP/MIP. Returns the status of the search.
|
||||
MPSolver::ResultStatus CLPInterface::Solve(const MPSolverParameters& param) {
|
||||
WallTimer timer;
|
||||
timer.Start();
|
||||
try {
|
||||
WallTimer timer;
|
||||
timer.Start();
|
||||
|
||||
if (param.GetIntegerParam(MPSolverParameters::INCREMENTALITY) ==
|
||||
MPSolverParameters::INCREMENTALITY_OFF) {
|
||||
Reset();
|
||||
}
|
||||
if (param.GetIntegerParam(MPSolverParameters::INCREMENTALITY) ==
|
||||
MPSolverParameters::INCREMENTALITY_OFF) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
// Set log level.
|
||||
CoinMessageHandler message_handler;
|
||||
clp_->passInMessageHandler(&message_handler);
|
||||
if (quiet_) {
|
||||
message_handler.setLogLevel(1, 0);
|
||||
clp_->setLogLevel(0);
|
||||
} else {
|
||||
message_handler.setLogLevel(1, 1);
|
||||
clp_->setLogLevel(1);
|
||||
}
|
||||
// Set log level.
|
||||
CoinMessageHandler message_handler;
|
||||
clp_->passInMessageHandler(&message_handler);
|
||||
if (quiet_) {
|
||||
message_handler.setLogLevel(1, 0);
|
||||
clp_->setLogLevel(0);
|
||||
} else {
|
||||
message_handler.setLogLevel(1, 1);
|
||||
clp_->setLogLevel(1);
|
||||
}
|
||||
|
||||
// Special case if the model is empty since CLP is not able to
|
||||
// handle this special case by itself.
|
||||
if (solver_->variables_.size() == 0 && solver_->constraints_.size() == 0) {
|
||||
// Special case if the model is empty since CLP is not able to
|
||||
// handle this special case by itself.
|
||||
if (solver_->variables_.size() == 0 && solver_->constraints_.size() == 0) {
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
objective_value_ = solver_->Objective().offset();
|
||||
return result_status_;
|
||||
}
|
||||
|
||||
ExtractModel();
|
||||
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
||||
|
||||
// Time limit.
|
||||
if (solver_->time_limit() != 0) {
|
||||
VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
|
||||
clp_->setMaximumSeconds(solver_->time_limit_in_secs());
|
||||
} else {
|
||||
clp_->setMaximumSeconds(-1.0);
|
||||
}
|
||||
|
||||
// Start from a fresh set of default parameters and set them to
|
||||
// specified values.
|
||||
options_.reset(new ClpSolve);
|
||||
SetParameters(param);
|
||||
|
||||
// Solve
|
||||
timer.Restart();
|
||||
clp_->initialSolve(*options_);
|
||||
VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get());
|
||||
|
||||
// Check the status: optimal, infeasible, etc.
|
||||
int tmp_status = clp_->status();
|
||||
VLOG(1) << "clp result status: " << tmp_status;
|
||||
switch (tmp_status) {
|
||||
case CLP_SIMPLEX_FINISHED:
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
break;
|
||||
case CLP_SIMPLEX_INFEASIBLE:
|
||||
result_status_ = MPSolver::INFEASIBLE;
|
||||
break;
|
||||
case CLP_SIMPLEX_UNBOUNDED:
|
||||
result_status_ = MPSolver::UNBOUNDED;
|
||||
break;
|
||||
case CLP_SIMPLEX_STOPPED:
|
||||
result_status_ = MPSolver::FEASIBLE;
|
||||
break;
|
||||
default:
|
||||
result_status_ = MPSolver::ABNORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result_status_ == MPSolver::OPTIMAL ||
|
||||
result_status_ == MPSolver::FEASIBLE) {
|
||||
// Get the results
|
||||
objective_value_ = clp_->objectiveValue();
|
||||
VLOG(1) << "objective=" << objective_value_;
|
||||
const double* const values = clp_->getColSolution();
|
||||
const double* const reduced_costs = clp_->getReducedCost();
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
const int clp_var_index = MPSolverVarIndexToClpVarIndex(var->index());
|
||||
const double val = values[clp_var_index];
|
||||
var->set_solution_value(val);
|
||||
VLOG(3) << var->name() << ": value = " << val;
|
||||
double reduced_cost = reduced_costs[clp_var_index];
|
||||
var->set_reduced_cost(reduced_cost);
|
||||
VLOG(4) << var->name() << ": reduced cost = " << reduced_cost;
|
||||
}
|
||||
const double* const dual_values = clp_->getRowPrice();
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int constraint_index = ct->index();
|
||||
const double dual_value = dual_values[constraint_index];
|
||||
ct->set_dual_value(dual_value);
|
||||
VLOG(4) << "row " << ct->index() << " dual value = " << dual_value;
|
||||
}
|
||||
}
|
||||
|
||||
ResetParameters();
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
objective_value_ = solver_->Objective().offset();
|
||||
return result_status_;
|
||||
} catch (CoinError e) {
|
||||
LOG(WARNING) << "Caught exception in Coin LP: " << e.message();
|
||||
result_status_ = MPSolver::ABNORMAL;
|
||||
return result_status_;
|
||||
}
|
||||
|
||||
ExtractModel();
|
||||
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
||||
|
||||
// Time limit.
|
||||
if (solver_->time_limit() != 0) {
|
||||
VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
|
||||
clp_->setMaximumSeconds(solver_->time_limit_in_secs());
|
||||
} else {
|
||||
clp_->setMaximumSeconds(-1.0);
|
||||
}
|
||||
|
||||
// Start from a fresh set of default parameters and set them to
|
||||
// specified values.
|
||||
options_.reset(new ClpSolve);
|
||||
SetParameters(param);
|
||||
|
||||
// Solve
|
||||
timer.Restart();
|
||||
clp_->initialSolve(*options_);
|
||||
VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get());
|
||||
|
||||
// Check the status: optimal, infeasible, etc.
|
||||
int tmp_status = clp_->status();
|
||||
VLOG(1) << "clp result status: " << tmp_status;
|
||||
switch (tmp_status) {
|
||||
case CLP_SIMPLEX_FINISHED:
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
break;
|
||||
case CLP_SIMPLEX_INFEASIBLE:
|
||||
result_status_ = MPSolver::INFEASIBLE;
|
||||
break;
|
||||
case CLP_SIMPLEX_UNBOUNDED:
|
||||
result_status_ = MPSolver::UNBOUNDED;
|
||||
break;
|
||||
case CLP_SIMPLEX_STOPPED:
|
||||
result_status_ = MPSolver::FEASIBLE;
|
||||
break;
|
||||
default:
|
||||
result_status_ = MPSolver::ABNORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result_status_ == MPSolver::OPTIMAL ||
|
||||
result_status_ == MPSolver::FEASIBLE) {
|
||||
// Get the results
|
||||
objective_value_ = clp_->objectiveValue();
|
||||
VLOG(1) << "objective=" << objective_value_;
|
||||
const double* const values = clp_->getColSolution();
|
||||
const double* const reduced_costs = clp_->getReducedCost();
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
const int clp_var_index = MPSolverVarIndexToClpVarIndex(var->index());
|
||||
const double val = values[clp_var_index];
|
||||
var->set_solution_value(val);
|
||||
VLOG(3) << var->name() << ": value = " << val;
|
||||
double reduced_cost = reduced_costs[clp_var_index];
|
||||
var->set_reduced_cost(reduced_cost);
|
||||
VLOG(4) << var->name() << ": reduced cost = " << reduced_cost;
|
||||
}
|
||||
const double* const dual_values = clp_->getRowPrice();
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int constraint_index = ct->index();
|
||||
const double dual_value = dual_values[constraint_index];
|
||||
ct->set_dual_value(dual_value);
|
||||
VLOG(4) << "row " << ct->index() << " dual value = " << dual_value;
|
||||
}
|
||||
}
|
||||
|
||||
ResetParameters();
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
return result_status_;
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus CLPInterface::TransformCLPBasisStatus(
|
||||
|
||||
@@ -161,7 +161,7 @@ class GLOPInterface : public MPSolverInterface {
|
||||
void SetPresolveMode(int value) override;
|
||||
void SetScalingMode(int value) override;
|
||||
void SetLpAlgorithm(int value) override;
|
||||
bool ReadParameterFile(const std::string& filename) override;
|
||||
bool SetSolverSpecificParametersAsString(const std::string& parameters) override;
|
||||
|
||||
private:
|
||||
void NonIncrementalChange();
|
||||
@@ -471,15 +471,16 @@ void GLOPInterface::SetLpAlgorithm(int value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GLOPInterface::ReadParameterFile(const std::string& filename) {
|
||||
bool GLOPInterface::SetSolverSpecificParametersAsString(
|
||||
const std::string& parameters) {
|
||||
#ifdef ANDROID_JNI
|
||||
// NOTE(user): Android build uses protocol buffers in lite mode, and
|
||||
// parsing data from text format is not supported there. To allow solver
|
||||
// specific parameters from std::string on Android, we first need to switch to
|
||||
// non-lite version of protocol buffers.
|
||||
return false;
|
||||
#else
|
||||
std::string params;
|
||||
if (!file::GetContents(filename, ¶ms, file::Defaults()).ok()) {
|
||||
return false;
|
||||
}
|
||||
const bool ok = google::protobuf::TextFormat::MergeFromString(params, ¶meters_);
|
||||
const bool ok = google::protobuf::TextFormat::MergeFromString(parameters, ¶meters_);
|
||||
lp_solver_.SetParameters(parameters_);
|
||||
return ok;
|
||||
#endif
|
||||
|
||||
@@ -291,55 +291,8 @@ void* MPSolver::underlying_solver() { return interface_->underlying_solver(); }
|
||||
// ---- Solver-specific parameters ----
|
||||
|
||||
bool MPSolver::SetSolverSpecificParametersAsString(const std::string& parameters) {
|
||||
#ifdef ANDROID_JNI
|
||||
// This is not implemented on Android because there is no default /tmp and a
|
||||
// pointer to the Java environment is require to query for the application
|
||||
// folder or the location of external storage (if any).
|
||||
return false;
|
||||
#else
|
||||
if (parameters.empty()) return true;
|
||||
solver_specific_parameter_string_ = parameters;
|
||||
|
||||
// Note(user): this method needs to return a success/failure boolean
|
||||
// immediately, so we also perform the actual parameter parsing right away.
|
||||
// Some implementations will keep them forever and won't need to re-parse
|
||||
// them; some (eg. SCIP, Gurobi) need to re-parse the parameters every time
|
||||
// they do Solve(). We just store the parameters std::string anyway.
|
||||
std::string extension = interface_->ValidFileExtensionForParameterFile();
|
||||
#if defined(__linux)
|
||||
int32 tid = static_cast<int32>(pthread_self());
|
||||
#else // defined(__linux__)
|
||||
int32 tid = 123;
|
||||
#endif // defined(__linux__)
|
||||
#if !defined(_MSC_VER)
|
||||
int32 pid = static_cast<int32>(getpid());
|
||||
#else // _MSC_VER
|
||||
int32 pid = 456;
|
||||
#endif // _MSC_VER
|
||||
int64 now = base::GetCurrentTimeNanos();
|
||||
std::string filename = StringPrintf("/tmp/parameters-tempfile-%x-%d-%llx%s",
|
||||
tid, pid, now, extension.c_str());
|
||||
bool no_error_so_far = true;
|
||||
if (no_error_so_far) {
|
||||
no_error_so_far =
|
||||
file::SetContents(filename, parameters, file::Defaults()).ok();
|
||||
}
|
||||
if (no_error_so_far) {
|
||||
no_error_so_far = interface_->ReadParameterFile(filename);
|
||||
// We need to clean up the file even if ReadParameterFile() returned
|
||||
// false. In production we can continue even if the deletion failed.
|
||||
if (!file::Delete(filename, file::Defaults()).ok()) {
|
||||
LOG(DFATAL) << "Couldn't delete temporary parameters file: " << filename;
|
||||
}
|
||||
}
|
||||
if (!no_error_so_far) {
|
||||
LOG(WARNING) << "Error in SetSolverSpecificParametersAsString() "
|
||||
<< "for solver type: "
|
||||
<< MPModelRequest::SolverType_Name(
|
||||
static_cast<MPModelRequest::SolverType>(ProblemType()));
|
||||
}
|
||||
return no_error_so_far;
|
||||
#endif
|
||||
return interface_->SetSolverSpecificParametersAsString(parameters);
|
||||
}
|
||||
|
||||
// ----- Solver -----
|
||||
@@ -1350,6 +1303,59 @@ void MPSolverInterface::SetIntegerParamToUnsupportedValue(
|
||||
<< " to an unsupported value: " << value;
|
||||
}
|
||||
|
||||
bool MPSolverInterface::SetSolverSpecificParametersAsString(
|
||||
const std::string& parameters) {
|
||||
#ifdef ANDROID_JNI
|
||||
// This is not implemented on Android because there is no default /tmp and a
|
||||
// pointer to the Java environment is require to query for the application
|
||||
// folder or the location of external storage (if any).
|
||||
return false;
|
||||
#else
|
||||
if (parameters.empty()) return true;
|
||||
|
||||
// Note(user): this method needs to return a success/failure boolean
|
||||
// immediately, so we also perform the actual parameter parsing right away.
|
||||
// Some implementations will keep them forever and won't need to re-parse
|
||||
// them; some (eg. SCIP, Gurobi) need to re-parse the parameters every time
|
||||
// they do Solve(). We just store the parameters std::string anyway.
|
||||
std::string extension = ValidFileExtensionForParameterFile();
|
||||
#if defined(__linux)
|
||||
int32 tid = static_cast<int32>(pthread_self());
|
||||
#else // defined(__linux__)
|
||||
int32 tid = 123;
|
||||
#endif // defined(__linux__)
|
||||
#if !defined(_MSC_VER)
|
||||
int32 pid = static_cast<int32>(getpid());
|
||||
#else // _MSC_VER
|
||||
int32 pid = 456;
|
||||
#endif // _MSC_VER
|
||||
int64 now = base::GetCurrentTimeNanos();
|
||||
std::string filename = StringPrintf("/tmp/parameters-tempfile-%x-%d-%llx%s",
|
||||
tid, pid, now, extension.c_str());
|
||||
bool no_error_so_far = true;
|
||||
if (no_error_so_far) {
|
||||
no_error_so_far =
|
||||
file::SetContents(filename, parameters, file::Defaults()).ok();
|
||||
}
|
||||
if (no_error_so_far) {
|
||||
no_error_so_far = ReadParameterFile(filename);
|
||||
// We need to clean up the file even if ReadParameterFile() returned
|
||||
// false. In production we can continue even if the deletion failed.
|
||||
if (!file::Delete(filename, file::Defaults()).ok()) {
|
||||
LOG(DFATAL) << "Couldn't delete temporary parameters file: " << filename;
|
||||
}
|
||||
}
|
||||
if (!no_error_so_far) {
|
||||
LOG(WARNING) << "Error in SetSolverSpecificParametersAsString() "
|
||||
<< "for solver type: "
|
||||
<< MPModelRequest::SolverType_Name(
|
||||
static_cast<MPModelRequest::SolverType>(
|
||||
solver_->ProblemType()));
|
||||
}
|
||||
return no_error_so_far;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MPSolverInterface::ReadParameterFile(const std::string& filename) {
|
||||
LOG(WARNING) << "ReadParameterFile() not supported by this solver.";
|
||||
return false;
|
||||
|
||||
@@ -1285,6 +1285,16 @@ class MPSolverInterface {
|
||||
virtual void SetDualTolerance(double value) = 0;
|
||||
virtual void SetPresolveMode(int value) = 0;
|
||||
|
||||
// Pass solver specific parameters in text format. The format is
|
||||
// solver-specific and is the same as the corresponding solver configuration
|
||||
// file format. Returns true if the operation was successful.
|
||||
//
|
||||
// The default implementation of this method stores the parameters in a
|
||||
// temporary file and calls ReadParameterFile to import the parameter file
|
||||
// into the solver. Solvers that support passing the parameters directly can
|
||||
// override this method to skip the temporary file logic.
|
||||
virtual bool SetSolverSpecificParametersAsString(const std::string& parameters);
|
||||
|
||||
// Reads a solver-specific file of parameters and set them.
|
||||
// Returns true if there was no errors.
|
||||
virtual bool ReadParameterFile(const std::string& filename);
|
||||
|
||||
@@ -488,11 +488,14 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) {
|
||||
ORTOOLS_SCIP_CALL(SCIPresetParam(scip_, "limits/time"));
|
||||
}
|
||||
|
||||
// TODO(user): clarify the differences and the precedence between the two
|
||||
// SetParameter*() API (file-based and generic, parameter-based).
|
||||
// We first set our internal MPSolverParameters from param and then set
|
||||
// any user specified internal solver, ie. SCIP, parameters via
|
||||
// solver_specific_parameter_string_.
|
||||
// Default MPSolverParameters can override custom parameters (for example for
|
||||
// presolving) and therefore we apply MPSolverParameters first.
|
||||
SetParameters(param);
|
||||
solver_->SetSolverSpecificParametersAsString(
|
||||
solver_->solver_specific_parameter_string_);
|
||||
SetParameters(param);
|
||||
|
||||
// Use the solution hint if any.
|
||||
// Note that SCIP will only use this if it is a feasible solution.
|
||||
|
||||
Reference in New Issue
Block a user