fix issue132

This commit is contained in:
Laurent Perron
2015-12-18 16:43:52 +01:00
parent 1e8b93ced8
commit 404580ff0e
11 changed files with 222 additions and 201 deletions

View File

@@ -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

View File

@@ -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_;
};

View File

@@ -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_);
}

View File

@@ -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);
};

View File

@@ -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, &params, file::Defaults()).ok()) {
return false;
}
const bool ok = google::protobuf::TextFormat::MergeFromString(params, &parameters_);
bool BopInterface::SetSolverSpecificParametersAsString(
const std::string& parameters) {
const bool ok = google::protobuf::TextFormat::MergeFromString(parameters, &parameters_);
bop_solver_.SetParameters(parameters_);
return ok;
}

View File

@@ -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;

View File

@@ -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(

View File

@@ -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, &params, file::Defaults()).ok()) {
return false;
}
const bool ok = google::protobuf::TextFormat::MergeFromString(params, &parameters_);
const bool ok = google::protobuf::TextFormat::MergeFromString(parameters, &parameters_);
lp_solver_.SetParameters(parameters_);
return ok;
#endif

View File

@@ -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;

View File

@@ -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);

View File

@@ -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.