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:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user