Changed status data structure returned by solve() in the linear solver wrapper. Prepared code to accept solution hints in the linear solver wrapper.

This commit is contained in:
Vincent Furnon
2015-06-17 16:29:25 +02:00
parent b619beb858
commit d828b1fcbe
9 changed files with 262 additions and 122 deletions

View File

@@ -78,7 +78,7 @@ void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) {
MPSolver::SolveWithProto(model_request, &solution_response);
// The problem has an optimal solution.
CHECK_EQ(MPSolutionResponse::OPTIMAL, solution_response.status());
CHECK_EQ(MPSOLVER_OPTIMAL, solution_response.status());
LOG(INFO) << "objective = " << solution_response.objective_value();
for (int j = 0; j < numVars; ++j) {

View File

@@ -162,8 +162,11 @@ void Run() {
printf("%-12s: '%s'\n", "File", FLAGS_input.c_str());
// Load the proto into the solver.
MPSolver::LoadStatus load_status = solver.LoadModelFromProto(model_proto);
CHECK(load_status == MPSolver::NO_ERROR);
std::string error_message;
const MPSolverResponseStatus status =
solver.LoadModelFromProto(model_proto, &error_message);
CHECK_EQ(MPSOLVER_MODEL_IS_VALID, status)
<< MPSolverResponseStatus_Name(status) << ": " << error_message;
printf("%-12s: %d x %d\n", "Dimension", solver.NumConstraints(),
solver.NumVariables());
@@ -177,9 +180,8 @@ void Run() {
}
printf("%-12s: %s\n", "Status",
MPSolutionResponse::Status_Name(
static_cast<MPSolutionResponse::Status>(solve_status))
.c_str());
MPSolverResponseStatus_Name(
static_cast<MPSolverResponseStatus>(solve_status)).c_str());
printf("%-12s: %15.15e\n", "Objective", solver.Objective().Value());
printf("%-12s: %-6.4g\n", "Time", solving_time_in_sec);
}

View File

@@ -52,6 +52,7 @@ DYNAMIC_GRAPH_DEPS = $(DYNAMIC_GRAPH_LIBS) \
DYNAMIC_LP_DEPS = \
$(GEN_DIR)/linear_solver/linear_solver.pb.h \
$(GEN_DIR)/glop/parameters.pb.h \
$(DYNAMIC_LP_LIBS) \
$(DYNAMIC_SAT_LIBS) \
$(DYNAMIC_SPLIT_LIBS) \
@@ -202,6 +203,8 @@ STATIC_BOP_DEPS = $(STATIC_BOP_LIBS) \
$(STATIC_ALGORITHMS_LIBS)
STATIC_LP_DEPS = $(STATIC_LP_LIBS) \
$(GEN_DIR)/linear_solver/linear_solver.pb.h \
$(GEN_DIR)/glop/parameters.pb.h \
$(STATIC_BOP_DEPS) \
$(STATIC_SPLIT_LIBS) \
$(STATIC_BASE_DEPS)
@@ -538,7 +541,7 @@ $(OBJ_DIR)/constraint_solver/gcc.$O:$(SRC_DIR)/constraint_solver/gcc.cc
$(OBJ_DIR)/constraint_solver/graph_constraints.$O:$(SRC_DIR)/constraint_solver/graph_constraints.cc
$(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/graph_constraints.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Sgraph_constraints.$O
$(OBJ_DIR)/constraint_solver/hybrid.$O:$(SRC_DIR)/constraint_solver/hybrid.cc
$(OBJ_DIR)/constraint_solver/hybrid.$O:$(SRC_DIR)/constraint_solver/hybrid.cc $(GEN_DIR)/linear_solver/linear_solver.pb.h $(GEN_DIR)/glop/parameters.pb.h
$(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/hybrid.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Shybrid.$O
$(OBJ_DIR)/constraint_solver/interval.$O:$(SRC_DIR)/constraint_solver/interval.cc

View File

@@ -101,6 +101,7 @@ DECLARE_bool(log_prefix);
// Poor man version of LOG_EVERY_N
#define LOG_EVERY_N(severity, n) LOG(severity)
#define LOG_EVERY_N_SEC(severity, n) LOG(severity)
namespace operations_research {
class DateLogger {

View File

@@ -97,8 +97,11 @@ void Solve(MPSolver::OptimizationProblemType type, const std::string& file_name,
MPSolverParameters::SCALING_OFF);
{
ScopedWallTime timer(&(result->loading_time_in_sec));
const MPSolver::LoadStatus load_status = solver.LoadModelFromProto(proto);
CHECK(load_status == MPSolver::NO_ERROR);
std::string error_message;
const MPSolverResponseStatus load_status =
solver.LoadModelFromProto(proto, &error_message);
CHECK_EQ(MPSOLVER_MODEL_IS_VALID, load_status)
<< MPSolverResponseStatus_Name(load_status) << ": " << error_message;
}
{
ScopedWallTime timer(&(result->solving_time_in_sec));

View File

@@ -29,7 +29,16 @@
#include "base/logging.h"
#include "base/stringprintf.h"
#include "base/timer.h"
#ifndef ANDROID_JNI
#include "base/file.h"
#endif
#ifdef ANDROID_JNI
#include "base/numbers.h"
#endif /// ANDROID_JNI
#include "base/map_util.h"
#include "base/stl_util.h"
#include "base/hash.h"
@@ -50,6 +59,11 @@ DEFINE_bool(linear_solver_enable_verbose_output, false,
"If set, enables verbose output for the solver. Setting this flag"
" is the same as calling MPSolver::EnableOutput().");
DEFINE_bool(mpsolver_bypass_model_validation, false,
"If set, the user-provided Model won't be verified before Solve()."
" Invalid models will typically trigger various error responses"
" from the underlying solvers; sometimes crashes.");
// To compile the open-source code, the anonymous namespace should be
// inside the operations_research namespace (This is due to the
@@ -57,6 +71,17 @@ DEFINE_bool(linear_solver_enable_verbose_output, false,
// operations_research namespace in open_source/base).
namespace operations_research {
#ifdef ANDROID_JNI
// Enum -> std::string conversions are not present in MessageLite that is being used
// on Android.
std::string MPSolverResponseStatus_Name(int status) {
return SimpleItoa(status);
}
std::string MPModelRequest_SolverType_Name(int type) {
return SimpleItoa(type);
}
#endif // ANDROID_JNI
double MPConstraint::GetCoefficient(const MPVariable* const var) const {
DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
@@ -268,6 +293,12 @@ 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;
@@ -310,6 +341,7 @@ bool MPSolver::SetSolverSpecificParametersAsString(const std::string& parameters
static_cast<MPModelRequest::SolverType>(ProblemType()));
}
return no_error_so_far;
#endif
}
// ----- Solver -----
@@ -343,7 +375,9 @@ extern MPSolverInterface* BuildGurobiInterface(bool mip,
extern MPSolverInterface* BuildCplexInterface(bool mip,
MPSolver* const solver);
#endif
#ifdef ANDROID_JNI
extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver);
#endif
namespace {
MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
@@ -481,19 +515,34 @@ MPConstraint* MPSolver::LookupConstraintOrNull(const std::string& constraint_nam
// ----- Methods using protocol buffers -----
MPSolver::LoadStatus MPSolver::LoadModelFromProto(
const MPModelProto& input_model) {
MPSolverResponseStatus MPSolver::LoadModelFromProto(
const MPModelProto& input_model, std::string* error_message) {
CHECK(error_message != nullptr);
const std::string error = "";
if (!error.empty()) {
*error_message = error;
LOG_IF(INFO, OutputIsEnabled())
<< "Invalid model given to LoadModelFromProto(): " << error;
if (FLAGS_mpsolver_bypass_model_validation) {
LOG_IF(INFO, OutputIsEnabled())
<< "Ignoring the model error(s) because of"
<< " --mpsolver_bypass_model_validation.";
} else {
return error.find("Infeasible") == std::string::npos ? MPSOLVER_MODEL_INVALID
: MPSOLVER_INFEASIBLE;
}
}
// The variable and constraint names are dropped, because we allow
// duplicate names in the proto (they're not considered as 'ids'),
// unlike the MPSolver C++ API which crashes if there are duplicate names.
// Passing empty names makes the MPSolver generate unique names.
//
// TODO(user): This limits the number of variables and constraints to 10^9:
// we should fix that.
MPObjective* const objective = MutableObjective();
for (int i = 0; i < input_model.variable_size(); ++i) {
const MPVariableProto& var_proto = input_model.variable(i);
// TODO(user): The variable name var_proto.name() is lost at this point.
// This is because MPSolver has no notion of name, just of ids, and these
// must be unique which is not necessarily the case of names in the new
// proto. MakeNumVar() will just assign an automated variable id when the
// the given name argument is empty.
//
// Note(user): The auto assigned id limits the number of variables to 10^9.
MPVariable* variable = MakeNumVar(var_proto.lower_bound(),
var_proto.upper_bound(), /*name=*/"");
variable->SetInteger(var_proto.is_integer());
@@ -503,24 +552,9 @@ MPSolver::LoadStatus MPSolver::LoadModelFromProto(
for (int i = 0; i < input_model.constraint_size(); ++i) {
const MPConstraintProto& ct_proto = input_model.constraint(i);
MPConstraint* const ct = MakeRowConstraint(
ct_proto.lower_bound(), ct_proto.upper_bound(), ct_proto.name());
ct_proto.lower_bound(), ct_proto.upper_bound(), /*name=*/"");
ct->set_is_lazy(ct_proto.is_lazy());
if (ct_proto.var_index_size() != ct_proto.coefficient_size()) {
LOG(ERROR) << "In constraint #" << i << " (name: '" << ct_proto.name()
<< "'):"
<< " var_index_size() != coefficient_size()"
<< ct_proto.DebugString();
// TODO(user): add new error type to MPSolver and return it here.
// TODO(user): add unit test of this error.
return MPSolver::UNKNOWN_VARIABLE_ID;
}
for (int j = 0; j < ct_proto.var_index_size(); ++j) {
if (ct_proto.var_index(j) >= variables_.size() ||
ct_proto.var_index(j) < 0) {
LOG(ERROR) << "Variable index out of bound in constraint named "
<< ct_proto.name() << ".";
return MPSolver::UNKNOWN_VARIABLE_ID;
}
ct->SetCoefficient(variables_[ct_proto.var_index(j)],
ct_proto.coefficient(j));
}
@@ -529,26 +563,37 @@ MPSolver::LoadStatus MPSolver::LoadModelFromProto(
if (input_model.has_objective_offset()) {
objective->SetOffset(input_model.objective_offset());
}
return MPSolver::NO_ERROR;
// Stores any hints about where to start the solve.
solution_hint_.clear();
for (int i = 0; i < input_model.solution_hint().var_index_size(); ++i) {
solution_hint_.push_back(
std::make_pair(variables_[input_model.solution_hint().var_index(i)],
input_model.solution_hint().var_value(i)));
}
return MPSOLVER_MODEL_IS_VALID;
}
namespace {
MPSolutionResponse::Status ResultStatusToMPSolutionResponse(
MPSolverResponseStatus ResultStatusToMPSolverResponseStatus(
MPSolver::ResultStatus status) {
switch (status) {
case MPSolver::OPTIMAL:
return MPSolutionResponse::OPTIMAL;
return MPSOLVER_OPTIMAL;
case MPSolver::FEASIBLE:
return MPSolutionResponse::FEASIBLE;
return MPSOLVER_FEASIBLE;
case MPSolver::INFEASIBLE:
return MPSolutionResponse::INFEASIBLE;
return MPSOLVER_INFEASIBLE;
case MPSolver::UNBOUNDED:
return MPSolutionResponse::UNBOUNDED;
return MPSOLVER_UNBOUNDED;
case MPSolver::ABNORMAL:
return MPSolutionResponse::ABNORMAL;
default:
return MPSolutionResponse::UNKNOWN;
return MPSOLVER_ABNORMAL;
case MPSolver::MODEL_INVALID:
return MPSOLVER_MODEL_INVALID;
case MPSolver::NOT_SOLVED:
return MPSOLVER_NOT_SOLVED;
}
return MPSOLVER_UNKNOWN_STATUS;
}
} // namespace
@@ -556,7 +601,7 @@ void MPSolver::FillSolutionResponseProto(MPSolutionResponse* response) const {
CHECK_NOTNULL(response);
response->Clear();
response->set_status(
ResultStatusToMPSolutionResponse(interface_->result_status_));
ResultStatusToMPSolverResponseStatus(interface_->result_status_));
if (interface_->result_status_ == MPSolver::OPTIMAL ||
interface_->result_status_ == MPSolver::FEASIBLE) {
response->set_objective_value(Objective().Value());
@@ -577,13 +622,13 @@ void MPSolver::SolveWithProto(const MPModelRequest& model_request,
const MPModelProto& model = model_request.model();
MPSolver solver(model.name(), static_cast<MPSolver::OptimizationProblemType>(
model_request.solver_type()));
const MPSolver::LoadStatus loadStatus = solver.LoadModelFromProto(model);
if (loadStatus != MPSolver::NO_ERROR) {
LOG(WARNING) << "Loading model from protocol buffer failed, "
<< "load status = "
<< Error::Code_Name(static_cast<Error::Code>(loadStatus))
<< " (" << loadStatus << ")";
response->set_status(MPSolutionResponse::ABNORMAL);
std::string error_message;
response->set_status(solver.LoadModelFromProto(model, &error_message));
if (response->status() != MPSOLVER_MODEL_IS_VALID) {
LOG_EVERY_N_SEC(WARNING, 1.0)
<< "Loading model from protocol buffer failed, load status = "
<< MPSolverResponseStatus_Name(response->status()) << " ("
<< response->status() << "); Error: " << error_message;
return;
}
if (model_request.has_solver_time_limit_seconds()) {
@@ -661,8 +706,8 @@ void MPSolver::ExportModelToProto(MPModelProto* output_model) const {
bool MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response) {
interface_->result_status_ = static_cast<ResultStatus>(response.status());
if (response.status() != MPSolutionResponse::OPTIMAL &&
response.status() != MPSolutionResponse::FEASIBLE) {
if (response.status() != MPSOLVER_OPTIMAL &&
response.status() != MPSOLVER_FEASIBLE) {
LOG(ERROR)
<< "Cannot load a solution unless its status is OPTIMAL or FEASIBLE.";
return false;
@@ -725,10 +770,6 @@ void MPSolver::Clear() {
void MPSolver::Reset() { interface_->Reset(); }
void MPSolver::EnableOutput() { interface_->set_quiet(false); }
void MPSolver::SuppressOutput() { interface_->set_quiet(true); }
bool MPSolver::InterruptSolve() { return interface_->InterruptSolve(); }
MPVariable* MPSolver::MakeVar(double lb, double ub, bool integer,
@@ -854,6 +895,9 @@ MPSolver::ResultStatus MPSolver::Solve() {
MPSolver::ResultStatus MPSolver::Solve(const MPSolverParameters& param) {
// Special case for infeasible constraints so that all solvers have
// the same behavior.
// TODO(user): replace this by model extraction to proto + proto validation
// (the proto has very low overhead compared to the wrapper, both in
// performance and memory, so it's ok).
if (HasInfeasibleConstraints()) {
interface_->result_status_ = MPSolver::INFEASIBLE;
return interface_->result_status_;
@@ -1072,6 +1116,12 @@ bool MPSolver::VerifySolution(double tolerance, bool log_errors) const {
return true;
}
bool MPSolver::OutputIsEnabled() const { return !interface_->quiet(); }
void MPSolver::EnableOutput() { interface_->set_quiet(false); }
void MPSolver::SuppressOutput() { interface_->set_quiet(false); }
int64 MPSolver::iterations() const { return interface_->iterations(); }
int64 MPSolver::nodes() const { return interface_->nodes(); }
@@ -1091,6 +1141,7 @@ bool MPSolver::OwnsVariable(const MPVariable* var) const {
return variables_[var_index] == var;
}
#ifndef ANDROID_JNI
bool MPSolver::ExportModelAsLpFormat(bool obfuscate, std::string* output) {
MPModelProto proto;
ExportModelToProto(&proto);
@@ -1105,6 +1156,7 @@ bool MPSolver::ExportModelAsMpsFormat(bool fixed_format, bool obfuscate,
MPModelProtoExporter exporter(proto);
return exporter.ExportModelAsMpsFormat(fixed_format, obfuscate, output);
}
#endif
// ---------- MPSolverInterface ----------
@@ -1216,7 +1268,7 @@ void MPSolverInterface::InvalidateSolutionSynchronization() {
double MPSolverInterface::ComputeExactConditionNumber() const {
// Override this method in interfaces that actually support it.
LOG(DFATAL) << "ComputeExactConditionNumber not implemented for "
<< MPModelRequest::SolverType_Name(
<< MPModelRequest_SolverType_Name(
static_cast<MPModelRequest::SolverType>(
solver_->ProblemType()));
return 0.0;

View File

@@ -138,11 +138,14 @@
#include <map>
#include "base/unique_ptr.h"
#include <string>
#include <utility>
#include <vector>
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/timer.h"
#include "glop/parameters.pb.h"
#include "linear_solver/linear_solver.pb.h"
namespace operations_research {
@@ -306,12 +309,13 @@ class MPSolver {
// underlying solvers exactly mean, especially for feasible and
// infeasible.
enum ResultStatus {
OPTIMAL, // optimal.
FEASIBLE, // feasible, or stopped by limit.
INFEASIBLE, // proven infeasible.
UNBOUNDED, // proven unbounded.
ABNORMAL, // abnormal, i.e., error of some kind.
NOT_SOLVED // not been solved yet.
OPTIMAL, // optimal.
FEASIBLE, // feasible, or stopped by limit.
INFEASIBLE, // proven infeasible.
UNBOUNDED, // proven unbounded.
ABNORMAL, // abnormal, i.e., error of some kind.
MODEL_INVALID, // the model is trivially invalid (NaN coefficients, etc).
NOT_SOLVED = 6 // not been solved yet.
};
// Solves the problem using default parameter values.
@@ -351,16 +355,12 @@ class MPSolver {
// ----- Methods using protocol buffers -----
// The status of loading the problem from a protocol buffer.
enum LoadStatus {
NO_ERROR = 0, // no error has been encountered.
// Skip value '1' to stay consistent with the .proto.
DUPLICATE_VARIABLE_ID = 2, // error: two variables have the same id.
UNKNOWN_VARIABLE_ID = 3, // error: a variable has an unknown id.
};
// Loads model from protocol buffer.
LoadStatus LoadModelFromProto(const MPModelProto& input_model);
// Loads model from protocol buffer. Returns MPSOLVER_MODEL_IS_VALID if the
// model is valid, and another status otherwise (currently only
// MPSOLVER_MODEL_INVALID and MPSOLVER_INFEASIBLE). If the model isn't
// valid, populate "error_message".
MPSolverResponseStatus LoadModelFromProto(const MPModelProto& input_model,
std::string* error_message);
// Encodes the current solution in a solution response protocol buffer.
// Only nonzero variable values are stored in order to reduce the
@@ -413,13 +413,13 @@ class MPSolver {
bool LoadSolutionFromProto(const MPSolutionResponse& response);
// ----- Export model to files or strings -----
#ifndef ANDROID_JNI
// Shortcuts to the homonymous MPModelProtoExporter methods, via
// exporting to a MPModelProto with ExportModelToProto() (see above).
bool ExportModelAsLpFormat(bool obfuscated, std::string* model_str);
bool ExportModelAsMpsFormat(bool fixed_format, bool obfuscated,
std::string* model_str);
#endif
// ----- Misc -----
// Advanced usage: pass solver specific parameters in text format. The format
@@ -444,14 +444,15 @@ class MPSolver {
// Infinity. You can use -MPSolver::infinity() for negative infinity.
static double infinity() { return std::numeric_limits<double>::infinity(); }
// Suppresses all output from the underlying solver.
void SuppressOutput();
// Enables a reasonably verbose output from the underlying
// solver. The level of verbosity and the location of this output
// depends on the underlying solver. In most cases, it is sent to
// stdout.
// Controls (or queries) the amount of output produced by the underlying
// solver. The output can surface to LOGs, or to stdout or stderr, depending
// on the implementation. The amount of output will greatly vary with each
// implementation and each problem.
//
// Output is suppressed by default.
bool OutputIsEnabled() const;
void EnableOutput();
void SuppressOutput();
void set_time_limit(int64 time_limit_milliseconds) {
DCHECK_GE(time_limit_milliseconds, 0);
@@ -591,7 +592,14 @@ class MPSolver {
DISALLOW_COPY_AND_ASSIGN(MPSolver);
};
#if !defined(SWIG)
#ifndef ANDROID_JNI
inline std::ostream& operator<<(std::ostream& os,
MPSolver::ResultStatus status) {
return os << MPSolverResponseStatus_Name(
static_cast<MPSolverResponseStatus>(status));
}
#endif
// The data structure used to store the coefficients of the contraints and of
// the objective. Also define a type to facilitate iteration over them with:
// for (CoeffEntry entry : coefficients_) { ... }
@@ -604,7 +612,6 @@ class CoeffMap : public hash_map<const MPVariable*, double> {
{
}
};
#endif // SWIG
typedef std::pair<const MPVariable*, double> CoeffEntry;

View File

@@ -97,12 +97,20 @@ message MPConstraintProto {
optional bool is_lazy = 5 [default = false];
}
// This message encode a partial (or full) assignment of the variables of a
// MPModelProto problem. The indices in var_index should be unique and valid
// variable indices of the associated problem.
message PartialVariableAssignment {
repeated int32 var_index = 1 [packed = true];
repeated double var_value = 2 [packed = true];
}
// MPModelProto contains all the information for a Linear Programming model.
message MPModelProto {
// True if the problem is a maximization problem. Minimize by default.
optional bool maximize = 1 [default = false];
// Offset for the objective function.
// Offset for the objective function. Must be finite.
optional double objective_offset = 2 [default = 0.0];
// All the variables appearing in the model.
@@ -113,6 +121,19 @@ message MPModelProto {
// Name of the model.
optional string name = 5 [default = ""];
// Solution hint.
//
// If a feasible or almost-feasible solution to the problem is already known,
// it may be helpful to pass it to the solver so that it can be used. A solver
// that supports this feature will try to use this information to create its
// initial feasible solution.
//
// Note that it may not always be faster to give a hint like this to the
// solver. There is also no guarantee that the solver will use this hint or
// try to return a solution "close" to this assignment in case of multiple
// optimal solutions.
optional PartialVariableAssignment solution_hint = 6;
}
message MPModelRequest {
@@ -153,30 +174,91 @@ message MPModelRequest {
optional double solver_time_limit_seconds = 3;
}
// Status returned by the solver. They follow a hierarchical nomenclature, to
// allow us to add more enum values in the future. Clients should use
// InCategory() to match these enums, with the following C++ pseudo-code:
//
// bool InCategory(MPSolverResponseStatus status, MPSolverResponseStatus cat) {
// if (cat == MPSOLVER_OPTIMAL) return status == MPSOLVER_OPTIMAL;
// while (status > cat) status <<= 4;
// return status == cat;
// }
enum MPSolverResponseStatus {
// Normal responses -- the model was valid, and the solver ran.
// These statuses should be "somewhat" repeatable, modulo the fact that the
// solver's time limit makes it undeterministic, and could change a FEASIBLE
// model to an OPTIMAL and vice-versa (the others, except NOT_SOLVED, should
// normally be deterministic). Also, the solver libraries can be buggy.
// The solver found the proven optimal solution. This is what should be
// returned in most cases.
//
// WARNING: for historical reason, the value is zero, which means that this
// value can't have any subcategories.
MPSOLVER_OPTIMAL = 0x0;
// The solver had enough time to find some solution that satisfies all
// constraints, but it did not prove optimality (which means it may or may
// not have reached the optimal).
//
// This can happen for large LP models (Linear Programming), and is a frequent
// response for time-limited MIPs (Mixed Integer Programming). In the MIP
// case, the difference between the solution 'objective_value' and
// 'best_objective_bound' fields of the MPSolutionResponse will give an
// indication of how far this solution is from the optimal one.
MPSOLVER_FEASIBLE = 0x1;
// The model does not have any solution, according to the solver (which
// "proved" it, with the caveat that numerical proofs aren't actual proofs),
// or based on trivial considerations (eg. a variable whose lower bound is
// strictly greater than its upper bound).
MPSOLVER_INFEASIBLE = 0x2;
// There exist solutions that make the magnitude of the objective value
// as large as wanted (i.e. -infinity (resp. +infinity) for a minimization
// (resp. maximization) problem.
MPSOLVER_UNBOUNDED = 0x3;
// An error (most probably numerical) occured.
// One likely cause for such errors is a large numerical range among variable
// coefficients (eg. 1e-16, 1e20), in which case one should try to shrink it.
MPSOLVER_ABNORMAL = 0x4;
// The solver did not have a chance to diagnose the model in one of the
// categories above.
MPSOLVER_NOT_SOLVED = 0x6;
// Like "NOT_SOLVED", but typically used by model validation functions
// returning a "model status", to enhance readability of the client code.
MPSOLVER_MODEL_IS_VALID = 0x61;
// Special value: the solver status could not be properly translated and is
// unknown.
MPSOLVER_UNKNOWN_STATUS = 0x63;
// Model errors. These are always deterministic and repeatable.
// They should be accompanied with a string description of the error.
MPSOLVER_MODEL_INVALID = 0x5;
// Something is wrong with the fields "solution_hint_var_index" and/or
// "solution_hint_var_value".
MPSOLVER_MODEL_INVALID_SOLUTION_HINT = 0x54;
// Implementation error: the requested solver implementation is not
// available (see MPModelRequest.solver_type).
// The linear solver binary was probably not linked with the required library,
// eg //linear_solver:linear_solver_scip for SCIP.
MPSOLVER_SOLVER_TYPE_UNAVAILABLE = 0x7;
// OBSOLETE: DO NOT USE.
// TODO(user): Remove those.
MPSOLVER_MODEL_INVALID_NUM_VARS_AND_NUM_COEFFS_DIFFER_IN_CONSTRAINT = 0x51;
MPSOLVER_MODEL_INVALID_VAR_INDEX_OUT_OF_RANGE_IN_CONSTRAINT = 0x52;
MPSOLVER_MODEL_INVALID_DUPLICATE_VAR_IN_CONSTRAINT = 0x53;
};
message MPSolutionResponse {
// Result of the optimization.
enum Status {
// The solver found the proven optimal solution. This is what should be
// returned in most cases.
OPTIMAL = 0;
// The solver had enough time to find some solution that satisfied all
// constraints, but it did not reach the optimal.
FEASIBLE = 1;
// The model does not have any solution.
INFEASIBLE = 2;
// There exists solutions that make the magnitude of the objective value
// as large as wanted (i.e. -infinity (resp. +infinity) for a minimization
// (resp. maximization) problem.
UNBOUNDED = 3;
// An error occured.
ABNORMAL = 4;
// Problem was not solved because model was malformed.
MODEL_INVALID = 5;
// Unknown. As of 2013-02, this only happens when the solver did not
// have enough time to diagnose one of the above states.
UNKNOWN = 99;
}
optional Status status = 1 [default = UNKNOWN];
optional /*required*/ MPSolverResponseStatus status = 1
[default = MPSOLVER_UNKNOWN_STATUS];
// Objective value corresponding to the "variable_value" below, taking into
// account the source "objective_offset" and "objective_coefficient".
@@ -201,14 +283,3 @@ message MPSolutionResponse {
// These are set iff 'status' is OPTIMAL or FEASIBLE.
repeated double dual_value = 4 [packed = true];
}
// Error codes used in some proto-based APIs.
// TODO(user): tidy this up and unify it with MPSolutionResponse.Status.
message Error {
enum Code {
NO_ERROR = 0;
INVALID_PROBLEM_TYPE = 1;
DUPLICATE_VARIABLE_ID = 2;
UNKNOWN_VARIABLE_ID = 3;
}
}

View File

@@ -179,7 +179,8 @@ void SCIPInterface::CreateSCIP() {
// Default clock type (1: CPU user seconds, 2: wall clock time). We use
// wall clock time because getting CPU user seconds involves calling
// times() which is very expensive.
ORTOOLS_SCIP_CALL(SCIPsetIntParam(scip_, "timing/clocktype", 2));
ORTOOLS_SCIP_CALL(SCIPsetIntParam(scip_, "timing/clocktype",
/* Wall clock time */ 2));
ORTOOLS_SCIP_CALL(SCIPcreateProb(scip_, solver_->name_.c_str(), NULL, NULL,
NULL, NULL, NULL, NULL, NULL));
ORTOOLS_SCIP_CALL(SCIPsetObjsense(