huge sync with internal version
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -63,9 +63,6 @@ class CBCInterface : public MPSolverInterface {
|
||||
// TODO(user): separate the solve from the model extraction.
|
||||
virtual void ExtractModel() {}
|
||||
|
||||
// Write model
|
||||
virtual void WriteModel(const string& filename);
|
||||
|
||||
// Query problem type.
|
||||
virtual bool IsContinuous() const { return false; }
|
||||
virtual bool IsLP() const { return false; }
|
||||
@@ -202,16 +199,6 @@ void CBCInterface::SetOptimizationDirection(bool maximize) {
|
||||
}
|
||||
}
|
||||
|
||||
void CBCInterface::WriteModel(const string& filename) {
|
||||
if (HasSuffixString(filename, ".lp")) {
|
||||
osi_.writeLp(filename.c_str(), "");
|
||||
} else {
|
||||
// If filename does not end in ".gz", CBC will
|
||||
// append ".gz" to the filename.
|
||||
osi_.writeMps(filename.c_str(), "");
|
||||
}
|
||||
}
|
||||
|
||||
void CBCInterface::SetVariableBounds(int var_index, double lb, double ub) {
|
||||
InvalidateSolutionSynchronization();
|
||||
if (sync_status_ == MODEL_SYNCHRONIZED) {
|
||||
@@ -349,8 +336,6 @@ MPSolver::ResultStatus CBCInterface::Solve(const MPSolverParameters& param) {
|
||||
sync_status_ = MODEL_SYNCHRONIZED;
|
||||
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
||||
|
||||
WriteModelToPredefinedFiles();
|
||||
|
||||
ResetBestObjectiveBound();
|
||||
|
||||
// Solve
|
||||
@@ -397,35 +382,6 @@ MPSolver::ResultStatus CBCInterface::Solve(const MPSolverParameters& param) {
|
||||
|
||||
VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get());
|
||||
|
||||
// Get the results
|
||||
objective_value_ = model.getObjValue();
|
||||
VLOG(1) << "objective=" << objective_value_;
|
||||
const double* const values = model.bestSolution();
|
||||
if (values != NULL) {
|
||||
// if optimal or feasible solution is found.
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
const int var_index = var->index();
|
||||
const double val = values[var_index];
|
||||
var->set_solution_value(val);
|
||||
VLOG(3) << var->name() << "=" << val;
|
||||
}
|
||||
} else {
|
||||
VLOG(1) << "No feasible solution found.";
|
||||
}
|
||||
|
||||
const double* const row_activities = model.getRowActivity();
|
||||
if (row_activities != NULL) {
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int constraint_index = ct->index();
|
||||
const double row_activity = row_activities[constraint_index];
|
||||
ct->set_activity(row_activity);
|
||||
VLOG(4) << "row " << ct->index()
|
||||
<< ": activity = " << row_activity;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the status: optimal, infeasible, etc.
|
||||
int tmp_status = model.status();
|
||||
|
||||
@@ -466,6 +422,38 @@ MPSolver::ResultStatus CBCInterface::Solve(const MPSolverParameters& param) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (result_status_ == MPSolver::OPTIMAL ||
|
||||
result_status_ == MPSolver::FEASIBLE) {
|
||||
// Get the results
|
||||
objective_value_ = model.getObjValue();
|
||||
VLOG(1) << "objective=" << objective_value_;
|
||||
const double* const values = model.bestSolution();
|
||||
if (values != NULL) {
|
||||
// if optimal or feasible solution is found.
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
const int var_index = var->index();
|
||||
const double val = values[var_index];
|
||||
var->set_solution_value(val);
|
||||
VLOG(3) << var->name() << "=" << val;
|
||||
}
|
||||
} else {
|
||||
VLOG(1) << "No feasible solution found.";
|
||||
}
|
||||
|
||||
const double* const row_activities = model.getRowActivity();
|
||||
if (row_activities != NULL) {
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int constraint_index = ct->index();
|
||||
const double row_activity = row_activities[constraint_index];
|
||||
ct->set_activity(row_activity);
|
||||
VLOG(4) << "row " << ct->index()
|
||||
<< ": activity = " << row_activity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterations_ = model.getIterationCount();
|
||||
nodes_ = model.getNodeCount();
|
||||
best_objective_bound_ = model.getBestPossibleObjValue();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -98,9 +98,6 @@ class CLPInterface : public MPSolverInterface {
|
||||
virtual MPSolver::BasisStatus column_status(int variable_index) const;
|
||||
|
||||
// ----- Misc -----
|
||||
// Write model
|
||||
virtual void WriteModel(const string& filename);
|
||||
|
||||
// Query problem type.
|
||||
virtual bool IsContinuous() const { return true; }
|
||||
virtual bool IsLP() const { return true; }
|
||||
@@ -162,17 +159,6 @@ void CLPInterface::Reset() {
|
||||
ResetExtractionInformation();
|
||||
}
|
||||
|
||||
void CLPInterface::WriteModel(const string& filename) {
|
||||
// CLP does not support the LP format natively. It only supports it
|
||||
// through OsiClpSolverInterface.
|
||||
// TODO(user) : Implement support for .lp format.
|
||||
if (HasSuffixString(filename, ".lp")) {
|
||||
LOG(WARNING) << "CLP does not support the LP format, "
|
||||
<< "writing in MPS format instead.";
|
||||
}
|
||||
clp_->writeMps(filename.c_str());
|
||||
}
|
||||
|
||||
// ------ Model modifications and extraction -----
|
||||
|
||||
// Not cached
|
||||
@@ -244,12 +230,20 @@ void CLPInterface::ClearConstraint(MPConstraint* const constraint) {
|
||||
// Cached
|
||||
void CLPInterface::SetObjectiveCoefficient(const MPVariable* const variable,
|
||||
double coefficient) {
|
||||
sync_status_ = MUST_RELOAD;
|
||||
InvalidateSolutionSynchronization();
|
||||
if (variable->index() != kNoIndex) {
|
||||
clp_->setObjectiveCoefficient(variable->index(), coefficient);
|
||||
} else {
|
||||
sync_status_ = MUST_RELOAD;
|
||||
}
|
||||
}
|
||||
|
||||
// Cached
|
||||
void CLPInterface::SetObjectiveOffset(double value) {
|
||||
sync_status_ = MUST_RELOAD;
|
||||
void CLPInterface::SetObjectiveOffset(double offset) {
|
||||
// Constant term. Use -offset instead of +offset because CLP does
|
||||
// not follow conventions.
|
||||
InvalidateSolutionSynchronization();
|
||||
clp_->setObjectiveOffset(-offset);
|
||||
}
|
||||
|
||||
// Clear objective of all its terms.
|
||||
@@ -329,13 +323,14 @@ void CLPInterface::ExtractNewVariables() {
|
||||
// Add new variables to existing constraints.
|
||||
for (int i = 0; i < last_constraint_index_; i++) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int ct_index = ct->index();
|
||||
for (ConstIter<hash_map<const MPVariable*, double> > it(
|
||||
ct->coefficients_);
|
||||
!it.at_end(); ++it) {
|
||||
const int var_index = it->first->index();
|
||||
DCHECK_NE(kNoIndex, var_index);
|
||||
if (var_index >= last_variable_index_) {
|
||||
clp_->modifyCoefficient(i, var_index, it->second);
|
||||
if (var_index >= last_variable_index_ + 1) { // + 1 because of dummy.
|
||||
clp_->modifyCoefficient(ct_index, var_index, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,8 +443,6 @@ MPSolver::ResultStatus CLPInterface::Solve(const MPSolverParameters& param) {
|
||||
ExtractModel();
|
||||
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
||||
|
||||
WriteModelToPredefinedFiles();
|
||||
|
||||
// Time limit.
|
||||
if (solver_->time_limit()) {
|
||||
VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
|
||||
@@ -468,35 +461,6 @@ MPSolver::ResultStatus CLPInterface::Solve(const MPSolverParameters& param) {
|
||||
clp_->initialSolve(*options_);
|
||||
VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get());
|
||||
|
||||
// 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 var_index = var->index();
|
||||
double val = values[var_index];
|
||||
var->set_solution_value(val);
|
||||
VLOG(3) << var->name() << ": value = " << val;
|
||||
double reduced_cost = reduced_costs[var_index];
|
||||
var->set_reduced_cost(reduced_cost);
|
||||
VLOG(4) << var->name() << ": reduced cost = " << reduced_cost;
|
||||
}
|
||||
const double* const dual_values = clp_->getRowPrice();
|
||||
const double* const row_activities = clp_->getRowActivity();
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int constraint_index = ct->index();
|
||||
const double row_activity = row_activities[constraint_index];
|
||||
ct->set_activity(row_activity);
|
||||
const double dual_value = dual_values[constraint_index];
|
||||
ct->set_dual_value(dual_value);
|
||||
VLOG(4) << "row " << ct->index()
|
||||
<< ": activity = " << row_activity
|
||||
<< " dual value = " << dual_value;
|
||||
}
|
||||
|
||||
// Check the status: optimal, infeasible, etc.
|
||||
int tmp_status = clp_->status();
|
||||
VLOG(1) << "clp result status: " << tmp_status;
|
||||
@@ -518,6 +482,38 @@ MPSolver::ResultStatus CLPInterface::Solve(const MPSolverParameters& param) {
|
||||
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 var_index = var->index();
|
||||
double val = values[var_index];
|
||||
var->set_solution_value(val);
|
||||
VLOG(3) << var->name() << ": value = " << val;
|
||||
double reduced_cost = reduced_costs[var_index];
|
||||
var->set_reduced_cost(reduced_cost);
|
||||
VLOG(4) << var->name() << ": reduced cost = " << reduced_cost;
|
||||
}
|
||||
const double* const dual_values = clp_->getRowPrice();
|
||||
const double* const row_activities = clp_->getRowActivity();
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
const int constraint_index = ct->index();
|
||||
const double row_activity = row_activities[constraint_index];
|
||||
ct->set_activity(row_activity);
|
||||
const double dual_value = dual_values[constraint_index];
|
||||
ct->set_dual_value(dual_value);
|
||||
VLOG(4) << "row " << ct->index()
|
||||
<< ": activity = " << row_activity
|
||||
<< " dual value = " << dual_value;
|
||||
}
|
||||
}
|
||||
|
||||
ResetParameters();
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
return result_status_;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -152,9 +152,6 @@ class GLPKInterface : public MPSolverInterface {
|
||||
virtual bool CheckBestObjectiveBoundExists() const;
|
||||
|
||||
// ----- Misc -----
|
||||
// Write model
|
||||
virtual void WriteModel(const string& filename);
|
||||
|
||||
// Query problem type.
|
||||
virtual bool IsContinuous() const { return IsLP(); }
|
||||
virtual bool IsLP() const { return !mip_; }
|
||||
@@ -221,8 +218,7 @@ class GLPKInterface : public MPSolverInterface {
|
||||
|
||||
// Creates a LP/MIP instance with the specified name and minimization objective.
|
||||
GLPKInterface::GLPKInterface(MPSolver* const solver, bool mip)
|
||||
: MPSolverInterface(solver), lp_(NULL), mip_(mip),
|
||||
mip_callback_info_(NULL) {
|
||||
: MPSolverInterface(solver), lp_(NULL), mip_(mip) {
|
||||
lp_ = glp_create_prob();
|
||||
glp_set_prob_name(lp_, solver_->name_.c_str());
|
||||
glp_set_obj_dir(lp_, GLP_MIN);
|
||||
@@ -245,14 +241,6 @@ void GLPKInterface::Reset() {
|
||||
ResetExtractionInformation();
|
||||
}
|
||||
|
||||
void GLPKInterface::WriteModel(const string& filename) {
|
||||
if (HasSuffixString(filename, ".lp")) {
|
||||
glp_write_lp(lp_, NULL, filename.c_str());
|
||||
} else {
|
||||
glp_write_mps(lp_, GLP_MPS_FILE, NULL, filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// ------ Model modifications and extraction -----
|
||||
|
||||
// Not cached
|
||||
@@ -560,8 +548,6 @@ MPSolver::ResultStatus GLPKInterface::Solve(const MPSolverParameters& param) {
|
||||
ExtractModel();
|
||||
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
||||
|
||||
WriteModelToPredefinedFiles();
|
||||
|
||||
// Configure parameters at every solve, even when the model has not
|
||||
// been changed, in case some of the parameters such as the time
|
||||
// limit have been changed since the last solve.
|
||||
@@ -704,8 +690,11 @@ int64 GLPKInterface::iterations() const {
|
||||
return kUnknownNumberOfIterations;
|
||||
} else {
|
||||
if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfIterations;
|
||||
// return lpx_get_int_parm(lp_, LPX_K_ITCNT); // FIXME
|
||||
#if GLP_MINOR_VERSION >= 49
|
||||
return kUnknownNumberOfIterations;
|
||||
#else
|
||||
return lpx_get_int_parm(lp_, LPX_K_ITCNT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -93,9 +93,6 @@ class GurobiInterface : public MPSolverInterface {
|
||||
virtual MPSolver::BasisStatus column_status(int variable_index) const;
|
||||
|
||||
// ----- Misc -----
|
||||
// Writes model
|
||||
virtual void WriteModel(const string& filename);
|
||||
|
||||
// Queries problem type.
|
||||
virtual bool IsContinuous() const { return IsLP(); }
|
||||
virtual bool IsLP() const { return !mip_; }
|
||||
@@ -154,6 +151,9 @@ class GurobiInterface : public MPSolverInterface {
|
||||
virtual void SetScalingMode(int value);
|
||||
virtual void SetLpAlgorithm(int value);
|
||||
|
||||
virtual bool ReadParameterFile(const string& filename);
|
||||
virtual string ValidFileExtensionForParameterFile() const;
|
||||
|
||||
MPSolver::BasisStatus
|
||||
TransformGRBVarBasisStatus(int gurobi_basis_status) const;
|
||||
MPSolver::BasisStatus
|
||||
@@ -195,10 +195,6 @@ GurobiInterface::~GurobiInterface() {
|
||||
GRBfreeenv(env_);
|
||||
}
|
||||
|
||||
void GurobiInterface::WriteModel(const string& filename) {
|
||||
CHECKED_GUROBI_CALL(GRBwrite(model_, filename.c_str()));
|
||||
}
|
||||
|
||||
// ------ Model modifications and extraction -----
|
||||
|
||||
void GurobiInterface::Reset() {
|
||||
@@ -672,8 +668,6 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
|
||||
VLOG(1) << StringPrintf("Model build in %.3f seconds.", timer.Get());
|
||||
|
||||
WriteModelToPredefinedFiles();
|
||||
|
||||
// Time limit.
|
||||
if (solver_->time_limit()) {
|
||||
VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
|
||||
@@ -682,7 +676,8 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
solver_->time_limit() / 1000.0));
|
||||
}
|
||||
|
||||
|
||||
solver_->SetSolverSpecificParametersAsString(
|
||||
solver_->solver_specific_parameter_string_);
|
||||
SetParameters(param);
|
||||
|
||||
// Solve
|
||||
@@ -695,20 +690,57 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get());
|
||||
}
|
||||
|
||||
// Get the results.
|
||||
int total_num_rows = solver_->constraints_.size();
|
||||
int total_num_cols = solver_->variables_.size();
|
||||
scoped_array<double> values(new double[total_num_cols]);
|
||||
scoped_array<double> dual_values(new double[total_num_rows]);
|
||||
scoped_array<double> slacks(new double[total_num_rows]);
|
||||
scoped_array<double> rhs(new double[total_num_rows]);
|
||||
scoped_array<double> reduced_costs(new double[total_num_cols]);
|
||||
// Get the status.
|
||||
int optimization_status = 0;
|
||||
CHECKED_GUROBI_CALL(GRBgetintattr(model_,
|
||||
GRB_INT_ATTR_STATUS,
|
||||
&optimization_status));
|
||||
if (optimization_status == GRB_OPTIMAL ||
|
||||
optimization_status == GRB_SUBOPTIMAL) {
|
||||
VLOG(1) << StringPrintf("Solution status %d.\n", optimization_status);
|
||||
int solution_count = 0;
|
||||
CHECKED_GUROBI_CALL(GRBgetintattr(model_,
|
||||
GRB_INT_ATTR_SOLCOUNT,
|
||||
&solution_count));
|
||||
|
||||
switch (optimization_status) {
|
||||
case GRB_OPTIMAL:
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
break;
|
||||
case GRB_INFEASIBLE:
|
||||
result_status_ = MPSolver::INFEASIBLE;
|
||||
break;
|
||||
case GRB_UNBOUNDED:
|
||||
result_status_ = MPSolver::UNBOUNDED;
|
||||
break;
|
||||
case GRB_INF_OR_UNBD:
|
||||
// TODO(user,user): We could introduce our own "infeasible or
|
||||
// unbounded" status.
|
||||
result_status_ = MPSolver::INFEASIBLE;
|
||||
break;
|
||||
default: {
|
||||
if (solution_count > 0) {
|
||||
result_status_ = MPSolver::FEASIBLE;
|
||||
} else {
|
||||
// TODO(user,user): We could introduce additional values for the
|
||||
// status: for example, stopped because of time limit.
|
||||
result_status_ = MPSolver::ABNORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (solution_count > 0) {
|
||||
DCHECK(result_status_ == MPSolver::FEASIBLE ||
|
||||
result_status_ == MPSolver::OPTIMAL);
|
||||
// Get the results.
|
||||
const int total_num_rows = solver_->constraints_.size();
|
||||
const int total_num_cols = solver_->variables_.size();
|
||||
|
||||
scoped_array<double> values(new double[total_num_cols]);
|
||||
scoped_array<double> dual_values(new double[total_num_rows]);
|
||||
scoped_array<double> slacks(new double[total_num_rows]);
|
||||
scoped_array<double> rhs(new double[total_num_rows]);
|
||||
scoped_array<double> reduced_costs(new double[total_num_cols]);
|
||||
|
||||
CHECKED_GUROBI_CALL(GRBgetdblattr(model_,
|
||||
GRB_DBL_ATTR_OBJVAL,
|
||||
&objective_value_));
|
||||
@@ -739,13 +771,7 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
total_num_rows,
|
||||
dual_values.get()));
|
||||
}
|
||||
}
|
||||
|
||||
int solution_count = 0;
|
||||
CHECKED_GUROBI_CALL(GRBgetintattr(model_,
|
||||
GRB_INT_ATTR_SOLCOUNT,
|
||||
&solution_count));
|
||||
if (solution_count > 0) {
|
||||
VLOG(1) << "objective = " << objective_value_;
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
@@ -756,6 +782,7 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
VLOG(4) << var->name() << ", reduced cost = " << reduced_costs[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
||||
MPConstraint* const ct = solver_->constraints_[i];
|
||||
ct->set_activity(rhs[i] - slacks[i]);
|
||||
@@ -773,44 +800,26 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
}
|
||||
}
|
||||
|
||||
VLOG(1) << StringPrintf("Solution status %d.\n", optimization_status);
|
||||
|
||||
switch (optimization_status) {
|
||||
case GRB_OPTIMAL:
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
break;
|
||||
case GRB_INFEASIBLE:
|
||||
result_status_ = MPSolver::INFEASIBLE;
|
||||
break;
|
||||
case GRB_UNBOUNDED:
|
||||
result_status_ = MPSolver::UNBOUNDED;
|
||||
break;
|
||||
case GRB_INF_OR_UNBD:
|
||||
// TODO(user,user): We could introduce our own "infeasible or
|
||||
// unbounded" status.
|
||||
result_status_ = MPSolver::INFEASIBLE;
|
||||
break;
|
||||
default: {
|
||||
if (solution_count > 0) {
|
||||
result_status_ = MPSolver::FEASIBLE;
|
||||
} else {
|
||||
// TODO(user,user): We could introduce additional values for the
|
||||
// status: for example, stopped because of time limit.
|
||||
result_status_ = MPSolver::ABNORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
GRBresetparams(GRBgetenv(model_));
|
||||
return result_status_;
|
||||
}
|
||||
|
||||
bool GurobiInterface::ReadParameterFile(const string& filename) {
|
||||
// A non-zero return value indicates that a problem occurred.
|
||||
return GRBreadparams(GRBgetenv(model_), filename.c_str()) == 0;
|
||||
}
|
||||
|
||||
string GurobiInterface::ValidFileExtensionForParameterFile() const {
|
||||
return ".prm";
|
||||
}
|
||||
|
||||
MPSolverInterface* BuildGurobiInterface(bool mip, MPSolver* const solver) {
|
||||
return new GurobiInterface(solver, mip);
|
||||
}
|
||||
|
||||
#undef CHECKED_GUROBI_CALL
|
||||
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // #if defined(USE_GUROBI)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -11,7 +11,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// (Laurent Perron)
|
||||
|
||||
#include "linear_solver/linear_solver.h"
|
||||
|
||||
@@ -33,16 +32,15 @@ using std::isnan;
|
||||
#include "base/scoped_ptr.h"
|
||||
#include "base/stringprintf.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/file.h"
|
||||
#include "base/concise_iterator.h"
|
||||
#include "base/map-util.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/hash.h"
|
||||
#include "linear_solver/linear_solver.pb.h"
|
||||
#include "linear_solver/model_exporter.h"
|
||||
#include "util/accurate_sum.h"
|
||||
|
||||
DEFINE_string(solver_write_model,
|
||||
"",
|
||||
"Path of the file to write the model to.");
|
||||
#include "util/fp_utils.h"
|
||||
|
||||
DEFINE_bool(verify_solution, false,
|
||||
"Systematically verify the solution when calling Solve()"
|
||||
@@ -51,6 +49,10 @@ DEFINE_bool(verify_solution, false,
|
||||
DEFINE_bool(log_verification_errors, true,
|
||||
"If --verify_solution is set: LOG(ERROR) all errors detected"
|
||||
" during the verification of the solution.");
|
||||
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().");
|
||||
|
||||
|
||||
// To compile the open-source code, the anonymous namespace should be
|
||||
// inside the operations_research namespace (This is due to the
|
||||
@@ -68,7 +70,7 @@ void MPConstraint::SetCoefficient(const MPVariable* const var, double coeff) {
|
||||
DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
|
||||
if (var == NULL) return;
|
||||
if (coeff == 0.0) {
|
||||
hash_map<const MPVariable*, double>::iterator it = coefficients_.find(var);
|
||||
CoeffMap::iterator it = coefficients_.find(var);
|
||||
// If setting a coefficient to 0 when this coefficient did not
|
||||
// exist or was already 0, do nothing: skip
|
||||
// interface_->SetCoefficient() and do not store a coefficient in
|
||||
@@ -83,8 +85,7 @@ void MPConstraint::SetCoefficient(const MPVariable* const var, double coeff) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
std::pair<hash_map<const MPVariable*, double>::iterator, bool>
|
||||
insertion_result =
|
||||
std::pair<CoeffMap::iterator, bool> insertion_result =
|
||||
coefficients_.insert(std::make_pair(var, coeff));
|
||||
const double old_value =
|
||||
insertion_result.second ? 0.0 : insertion_result.first->second;
|
||||
@@ -157,7 +158,7 @@ void MPObjective::SetCoefficient(const MPVariable* const var, double coeff) {
|
||||
DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
|
||||
if (var == NULL) return;
|
||||
if (coeff == 0.0) {
|
||||
hash_map<const MPVariable*, double>::iterator it = coefficients_.find(var);
|
||||
CoeffMap::iterator it = coefficients_.find(var);
|
||||
// See the discussion on MPConstraint::SetCoefficient() for 0 coefficients,
|
||||
// the same reasoning applies here.
|
||||
if (it == coefficients_.end() || it->second == 0.0) return;
|
||||
@@ -309,6 +310,46 @@ void* MPSolver::underlying_solver() {
|
||||
return interface_->underlying_solver();
|
||||
}
|
||||
|
||||
// ---- Solver-specific parameters ----
|
||||
|
||||
bool MPSolver::SetSolverSpecificParametersAsString(const string& parameters) {
|
||||
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 string anyway.
|
||||
string extension = interface_->ValidFileExtensionForParameterFile();
|
||||
int32 tid = static_cast<int32>(pthread_self());
|
||||
int32 pid = static_cast<int32>(getpid());
|
||||
int64 now = WallTimer::GetTimeInMicroSeconds();
|
||||
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.c_str())) {
|
||||
LOG(DFATAL) << "Couldn't delete temporary parameters file: "
|
||||
<< filename;
|
||||
}
|
||||
}
|
||||
if (!no_error_so_far) {
|
||||
LOG(WARNING) << "Error in SetSolverSpecificParametersAsString() "
|
||||
<< "for solver type: "
|
||||
<< MPModelRequest::OptimizationProblemType_Name(
|
||||
static_cast<MPModelRequest::OptimizationProblemType>(
|
||||
ProblemType()));
|
||||
}
|
||||
return no_error_so_far;
|
||||
}
|
||||
|
||||
// ----- Solver -----
|
||||
|
||||
#if defined(USE_CLP) || defined(USE_CBC)
|
||||
@@ -327,7 +368,7 @@ extern MPSolverInterface* BuildSCIPInterface(MPSolver* const solver);
|
||||
extern MPSolverInterface* BuildSLMInterface(MPSolver* const solver, bool mip);
|
||||
#endif
|
||||
#if defined(USE_GUROBI)
|
||||
extern MPSolverInterface* BuildGurobiInterface(bool mip, MPSolver* const solver);
|
||||
extern MPSolverInterface* BuildGurobiInterface(MPSolver* const solver, bool mip);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -361,9 +402,9 @@ MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
|
||||
#endif
|
||||
#if defined(USE_GUROBI)
|
||||
case MPSolver::GUROBI_LINEAR_PROGRAMMING:
|
||||
return BuildGurobiInterface(false, solver);
|
||||
return BuildGurobiInterface(solver, false);
|
||||
case MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING:
|
||||
return BuildGurobiInterface(true, solver);
|
||||
return BuildGurobiInterface(solver, true);
|
||||
#endif
|
||||
default:
|
||||
// TODO(user): Revert to the best *available* interface.
|
||||
@@ -373,13 +414,30 @@ MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int NumDigits(int n) {
|
||||
// Number of digits needed to write a non-negative integer in base 10.
|
||||
// Note(user): max(1, log(0) + 1) == max(1, -inf) == 1.
|
||||
#if defined(_MSC_VER)
|
||||
return static_cast<int>(std::max(1.0, log(1.0L * n) / log(10.0L) + 1.0));
|
||||
#else
|
||||
return static_cast<int>(std::max(1.0, log10(n) + 1));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MPSolver::MPSolver(const string& name, OptimizationProblemType problem_type)
|
||||
: name_(name),
|
||||
problem_type_(problem_type),
|
||||
variable_name_to_index_(1),
|
||||
constraint_name_to_index_(1),
|
||||
time_limit_(0.0),
|
||||
write_model_filename_("") {
|
||||
var_and_constraint_names_allow_export_(true) {
|
||||
timer_.Restart();
|
||||
interface_.reset(BuildSolverInterface(this));
|
||||
if (FLAGS_linear_solver_enable_verbose_output) {
|
||||
EnableOutput();
|
||||
}
|
||||
objective_.reset(new MPObjective(interface_.get()));
|
||||
}
|
||||
|
||||
@@ -403,64 +461,27 @@ MPConstraint* MPSolver::LookupConstraintOrNull(
|
||||
return constraints_[it->second];
|
||||
}
|
||||
|
||||
// ----- Names management -----
|
||||
|
||||
bool MPSolver::CheckNameValidity(const string& name) {
|
||||
// CheckNameValidity() is an internal method, and the following test is about
|
||||
// its usage -- we check that it is never *called* on an empty name.
|
||||
if (name.empty()) {
|
||||
LOG(DFATAL) << "CheckNameValidity() should not be passed an empty name.";
|
||||
}
|
||||
// Allow names that conform to the LP and MPS format.
|
||||
const int kMaxNameLength = 255;
|
||||
if (name.size() > kMaxNameLength) {
|
||||
LOG(WARNING) << "Invalid name " << name
|
||||
<< ": length > " << kMaxNameLength << "."
|
||||
<< " Will be unable to write model to file.";
|
||||
return false;
|
||||
}
|
||||
if (name.find_first_of(" +-*<>=:\\") != string::npos) {
|
||||
LOG(WARNING) << "Invalid name " << name
|
||||
<< ": contains forbidden character: +-*<>=:\\ space."
|
||||
<< " Will be unable to write model to file.";
|
||||
return false;
|
||||
}
|
||||
size_t first_occurrence = name.find_first_of(".0123456789");
|
||||
if (first_occurrence != string::npos && first_occurrence == 0) {
|
||||
LOG(WARNING) << "Invalid name " << name
|
||||
<< ": first character should not be . or a number."
|
||||
<< " Will be unable to write model to file.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MPSolver::CheckAllNamesValidity() {
|
||||
for (int i = 0; i < variables_.size(); ++i) {
|
||||
if (!CheckNameValidity(variables_[i]->name())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < constraints_.size(); ++i) {
|
||||
if (!CheckNameValidity(constraints_[i]->name())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----- Methods using protocol buffers -----
|
||||
|
||||
MPSolver::LoadStatus MPSolver::LoadModel(const MPModelProto& input_model) {
|
||||
// TODO(user): clear the previous data in the solver, and re-use the
|
||||
// existing hash maps!
|
||||
hash_map<string, MPVariable*> variables;
|
||||
for (int i = 0; i < input_model.variables_size(); ++i) {
|
||||
const MPVariableProto& var_proto = input_model.variables(i);
|
||||
const string& id = var_proto.id();
|
||||
if (!ContainsKey(variables, id)) {
|
||||
if (var_and_constraint_names_allow_export_) {
|
||||
// TODO(user): unit test this clause and the similar one below.
|
||||
var_and_constraint_names_allow_export_ &=
|
||||
MPModelProtoExporter::CheckNameValidity(id);
|
||||
}
|
||||
MPVariable* variable = MakeNumVar(var_proto.lb(), var_proto.ub(), id);
|
||||
variable->SetInteger(var_proto.integer());
|
||||
variables[id] = variable;
|
||||
} else {
|
||||
LOG(ERROR) << "Multiple definitions of the same variable: '" << id
|
||||
<< "', model '" << input_model.name() << "'";
|
||||
return MPSolver::DUPLICATE_VARIABLE_ID;
|
||||
}
|
||||
}
|
||||
@@ -469,7 +490,11 @@ MPSolver::LoadStatus MPSolver::LoadModel(const MPModelProto& input_model) {
|
||||
for (int i = 0; i < input_model.constraints_size(); ++i) {
|
||||
tmp_variable_set.clear();
|
||||
const MPConstraintProto& ct_proto = input_model.constraints(i);
|
||||
const string& ct_id = ct_proto.has_id() ? ct_proto.id() : "";
|
||||
const string& ct_id = ct_proto.id();
|
||||
if (var_and_constraint_names_allow_export_) {
|
||||
var_and_constraint_names_allow_export_ &=
|
||||
MPModelProtoExporter::CheckNameValidity(ct_id);
|
||||
}
|
||||
MPConstraint* const ct = MakeRowConstraint(ct_proto.lb(),
|
||||
ct_proto.ub(),
|
||||
ct_id);
|
||||
@@ -515,8 +540,9 @@ MPSolver::LoadStatus MPSolver::LoadModel(const MPModelProto& input_model) {
|
||||
return MPSolver::NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void MPSolver::FillSolutionResponse(MPSolutionResponse* response) const {
|
||||
DCHECK(response != NULL);
|
||||
CHECK_NOTNULL(response);
|
||||
if ((response->has_result_status() &&
|
||||
response->result_status() != MPSolutionResponse::NOT_SOLVED) ||
|
||||
response->has_objective_value() ||
|
||||
@@ -573,10 +599,11 @@ void MPSolver::FillSolutionResponse(MPSolutionResponse* response) const {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void MPSolver::SolveWithProtocolBuffers(const MPModelRequest& model_request,
|
||||
MPSolutionResponse* response) {
|
||||
DCHECK(response != NULL);
|
||||
CHECK_NOTNULL(response);
|
||||
const MPModelProto& model = model_request.model();
|
||||
MPSolver solver(model.name(),
|
||||
static_cast<MPSolver::OptimizationProblemType>(
|
||||
@@ -584,8 +611,9 @@ void MPSolver::SolveWithProtocolBuffers(const MPModelRequest& model_request,
|
||||
const MPSolver::LoadStatus loadStatus = solver.LoadModel(model);
|
||||
if (loadStatus != MPSolver::NO_ERROR) {
|
||||
LOG(WARNING) << "Loading model from protocol buffer failed, "
|
||||
<< "load status = " << loadStatus;
|
||||
<< " (" << loadStatus << ")";
|
||||
response->set_result_status(MPSolutionResponse::ABNORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (model_request.has_time_limit_ms()) {
|
||||
@@ -595,6 +623,41 @@ void MPSolver::SolveWithProtocolBuffers(const MPModelRequest& model_request,
|
||||
solver.FillSolutionResponse(response);
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
// Outputs the terms in var_coeff_map to output_term, by sorting the
|
||||
// variables by their indices as returned by the var_name_to_index map.
|
||||
void OutputTermsToProto(
|
||||
const std::vector<MPVariable*>& variables,
|
||||
const hash_map<const MPVariable*, int>& var_name_to_index,
|
||||
const CoeffMap& var_coeff_map,
|
||||
google::protobuf::RepeatedPtrField<MPTermProto>* output_terms) {
|
||||
// Vector linear_term will contain pairs (variable index, coeff), that will
|
||||
// be sorted by variable index.
|
||||
std::vector<pair<int, double> > linear_term;
|
||||
for (CoeffEntry entry : var_coeff_map) {
|
||||
const MPVariable* const var = entry.first;
|
||||
const double coef = entry.second;
|
||||
const int var_index = FindWithDefault(var_name_to_index, var, -1);
|
||||
DCHECK_NE(-1, var_index);
|
||||
linear_term.push_back(pair<int, double>(var_index, coef));
|
||||
}
|
||||
// The cost of sort is expected to be low as constraints usually have very
|
||||
// few terms.
|
||||
sort(linear_term.begin(), linear_term.end());
|
||||
// Now use linear term.
|
||||
for (int k = 0; k < linear_term.size(); ++k) {
|
||||
const pair<int, double>& p = linear_term[k];
|
||||
const int var_index = p.first;
|
||||
const double coef = p.second;
|
||||
const MPVariable* const var = variables[var_index];
|
||||
MPTermProto* term_proto = output_terms->Add();
|
||||
term_proto->set_variable_id(var->name());
|
||||
term_proto->set_coefficient(coef);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MPSolver::ExportModel(MPModelProto* output_model) const {
|
||||
DCHECK(output_model != NULL);
|
||||
if (output_model->variables_size() > 0 ||
|
||||
@@ -612,6 +675,8 @@ void MPSolver::ExportModel(MPModelProto* output_model) const {
|
||||
output_model->clear_name();
|
||||
}
|
||||
|
||||
// Name
|
||||
output_model->set_name(Name());
|
||||
// Variables
|
||||
for (int j = 0; j < variables_.size(); ++j) {
|
||||
const MPVariable* const var = variables_[j];
|
||||
@@ -623,6 +688,17 @@ void MPSolver::ExportModel(MPModelProto* output_model) const {
|
||||
variable_proto->set_integer(var->integer());
|
||||
}
|
||||
|
||||
// Map the variables to their indices. This is needed to output the
|
||||
// variables in the order they were created, which in turn is needed to have
|
||||
// repeatable results with ExportModelAsLpString and ExportModelAsMpsString.
|
||||
// This step is needed as long as the variable indices are given by the
|
||||
// underlying solver at the time of model extraction.
|
||||
// TODO(user): remove this step.
|
||||
hash_map<const MPVariable*, int> var_name_to_index;
|
||||
for (int j = 0; j < variables_.size(); ++j) {
|
||||
var_name_to_index[variables_[j]] = j;
|
||||
}
|
||||
|
||||
// Constraints
|
||||
for (int i = 0; i < constraints_.size(); ++i) {
|
||||
MPConstraint* const constraint = constraints_[i];
|
||||
@@ -632,28 +708,13 @@ void MPSolver::ExportModel(MPModelProto* output_model) const {
|
||||
constraint_proto->set_id(constraint->name());
|
||||
constraint_proto->set_lb(constraint->lb());
|
||||
constraint_proto->set_ub(constraint->ub());
|
||||
for (ConstIter<hash_map<const MPVariable*, double> >
|
||||
it(constraint->coefficients_);
|
||||
!it.at_end(); ++it) {
|
||||
const MPVariable* const var = it->first;
|
||||
const double coef = it->second;
|
||||
MPTermProto* const term = constraint_proto->add_terms();
|
||||
term->set_variable_id(var->name());
|
||||
term->set_coefficient(coef);
|
||||
}
|
||||
OutputTermsToProto(variables_, var_name_to_index, constraint->coefficients_,
|
||||
constraint_proto->mutable_terms());
|
||||
}
|
||||
|
||||
// Objective
|
||||
for (hash_map<const MPVariable*, double>::const_iterator it =
|
||||
objective_->coefficients_.begin();
|
||||
it != objective_->coefficients_.end();
|
||||
++it) {
|
||||
const MPVariable* const var = it->first;
|
||||
const double coef = it->second;
|
||||
MPTermProto* const term = output_model->add_objective_terms();
|
||||
term->set_variable_id(var->name());
|
||||
term->set_coefficient(coef);
|
||||
}
|
||||
// Objective.
|
||||
OutputTermsToProto(variables_, var_name_to_index, objective_->coefficients_,
|
||||
output_model->mutable_objective_terms());
|
||||
output_model->set_maximize(Objective().maximization());
|
||||
output_model->set_objective_offset(Objective().offset());
|
||||
}
|
||||
@@ -757,8 +818,11 @@ MPVariable* MPSolver::MakeVar(
|
||||
double lb, double ub, bool integer, const string& name) {
|
||||
const int var_index = NumVariables();
|
||||
const string fixed_name = name.empty()
|
||||
? StringPrintf("auto_variable_%06d", var_index) : name;
|
||||
CheckNameValidity(fixed_name);
|
||||
? StringPrintf("auto_v_%09d", var_index) : name;
|
||||
if (var_and_constraint_names_allow_export_) {
|
||||
var_and_constraint_names_allow_export_ &=
|
||||
MPModelProtoExporter::CheckNameValidity(fixed_name);
|
||||
}
|
||||
InsertOrDie(&variable_name_to_index_, fixed_name, var_index);
|
||||
MPVariable* v = new MPVariable(lb, ub, integer, fixed_name, interface_.get());
|
||||
variables_.push_back(v);
|
||||
@@ -837,8 +901,11 @@ MPConstraint* MPSolver::MakeRowConstraint(double lb, double ub,
|
||||
const string& name) {
|
||||
const int constraint_index = NumConstraints();
|
||||
const string fixed_name = name.empty()
|
||||
? StringPrintf("auto_constraint_%06d", constraint_index) : name;
|
||||
CheckNameValidity(fixed_name);
|
||||
? StringPrintf("auto_c_%09d", constraint_index) : name;
|
||||
if (var_and_constraint_names_allow_export_) {
|
||||
var_and_constraint_names_allow_export_ &=
|
||||
MPModelProtoExporter::CheckNameValidity(fixed_name);
|
||||
}
|
||||
InsertOrDie(&constraint_name_to_index_, fixed_name, constraint_index);
|
||||
MPConstraint* const constraint =
|
||||
new MPConstraint(lb, ub, fixed_name, interface_.get());
|
||||
@@ -892,6 +959,7 @@ MPSolver::ResultStatus MPSolver::Solve(const MPSolverParameters ¶m) {
|
||||
return interface_->result_status_;
|
||||
}
|
||||
|
||||
|
||||
const MPSolver::ResultStatus status = interface_->Solve(param);
|
||||
if (!FLAGS_verify_solution) return status;
|
||||
if (status != MPSolver::OPTIMAL) {
|
||||
@@ -902,8 +970,7 @@ MPSolver::ResultStatus MPSolver::Solve(const MPSolverParameters ¶m) {
|
||||
}
|
||||
if (VerifySolution(
|
||||
param.GetDoubleParam(MPSolverParameters::PRIMAL_TOLERANCE),
|
||||
FLAGS_log_verification_errors,
|
||||
NULL)) {
|
||||
FLAGS_log_verification_errors)) {
|
||||
return status;
|
||||
} else {
|
||||
return MPSolver::ABNORMAL;
|
||||
@@ -974,11 +1041,9 @@ string PrettyPrintConstraint(const MPConstraint& constraint) {
|
||||
} // namespace
|
||||
|
||||
// TODO(user): split.
|
||||
bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
bool log_errors,
|
||||
double* observed_max_absolute_error) const {
|
||||
bool MPSolver::VerifySolution(double tolerance, bool log_errors) const {
|
||||
double max_observed_error = 0;
|
||||
if (max_absolute_error < 0) max_absolute_error = infinity();
|
||||
if (tolerance < 0) tolerance = infinity();
|
||||
int num_errors = 0;
|
||||
|
||||
// Verify variables.
|
||||
@@ -992,12 +1057,9 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
LOG_IF(ERROR, log_errors) << "NaN value for " << PrettyPrintVar(var);
|
||||
continue;
|
||||
}
|
||||
if (var.lb() <= -infinity() && var.ub() >= infinity()) {
|
||||
continue;
|
||||
}
|
||||
// Check lower bound.
|
||||
if (var.lb() != -infinity()) {
|
||||
if (value < var.lb() - max_absolute_error) {
|
||||
if (value < var.lb() - tolerance) {
|
||||
++num_errors;
|
||||
max_observed_error = std::max(max_observed_error, var.lb() - value);
|
||||
LOG_IF(ERROR, log_errors)
|
||||
@@ -1006,7 +1068,7 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
}
|
||||
// Check upper bound.
|
||||
if (var.ub() != infinity()) {
|
||||
if (value > var.ub() + max_absolute_error) {
|
||||
if (value > var.ub() + tolerance) {
|
||||
++num_errors;
|
||||
max_observed_error = std::max(max_observed_error, value - var.ub());
|
||||
LOG_IF(ERROR, log_errors)
|
||||
@@ -1015,7 +1077,7 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
}
|
||||
// Check integrality.
|
||||
if (var.integer()) {
|
||||
if (fabs(value - round(value)) > max_absolute_error) {
|
||||
if (fabs(value - round(value)) > tolerance) {
|
||||
++num_errors;
|
||||
max_observed_error = std::max(max_observed_error,
|
||||
fabs(value - round(value)));
|
||||
@@ -1047,21 +1109,16 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
<< "NaN value for " << PrettyPrintConstraint(constraint);
|
||||
continue;
|
||||
}
|
||||
// This shouldn't happen normally, but the client could have tweaked the
|
||||
// model in a weird way. Who knows.
|
||||
if (constraint.lb() <= -infinity() && constraint.ub() >= infinity()) {
|
||||
continue;
|
||||
}
|
||||
// Check bounds.
|
||||
if (constraint.lb() != -infinity()) {
|
||||
if (activity < constraint.lb() - max_absolute_error) {
|
||||
if (activity < constraint.lb() - tolerance) {
|
||||
++num_errors;
|
||||
max_observed_error = std::max(max_observed_error,
|
||||
constraint.lb() - activity);
|
||||
LOG_IF(ERROR, log_errors)
|
||||
<< "Activity " << activity << " too low for "
|
||||
<< PrettyPrintConstraint(constraint);
|
||||
} else if (inaccurate_activity < constraint.lb() - max_absolute_error) {
|
||||
} else if (inaccurate_activity < constraint.lb() - tolerance) {
|
||||
LOG_IF(WARNING, log_errors)
|
||||
<< "Activity " << activity << ", computed with the (inaccurate)"
|
||||
<< " standard sum of its terms, is too low for "
|
||||
@@ -1069,14 +1126,14 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
}
|
||||
}
|
||||
if (constraint.ub() != -infinity()) {
|
||||
if (activity > constraint.ub() + max_absolute_error) {
|
||||
if (activity > constraint.ub() + tolerance) {
|
||||
++num_errors;
|
||||
max_observed_error = std::max(max_observed_error,
|
||||
activity - constraint.ub());
|
||||
LOG_IF(ERROR, log_errors)
|
||||
<< "Activity " << activity << " too high for "
|
||||
<< PrettyPrintConstraint(constraint);
|
||||
} else if (inaccurate_activity > constraint.ub() + max_absolute_error) {
|
||||
} else if (inaccurate_activity > constraint.ub() + tolerance) {
|
||||
LOG_IF(WARNING, log_errors)
|
||||
<< "Activity " << activity << ", computed with the (inaccurate)"
|
||||
<< " standard sum of its terms, is too high for "
|
||||
@@ -1088,6 +1145,7 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
// Verify that the objective value wasn't reported incorrectly.
|
||||
const MPObjective& objective = Objective();
|
||||
AccurateSum<double> objective_sum;
|
||||
objective_sum.Add(objective.offset());
|
||||
double inaccurate_objective_value = objective.offset();
|
||||
for (hash_map<const MPVariable*, double>::const_iterator
|
||||
it = objective.coefficients_.begin();
|
||||
@@ -1096,30 +1154,31 @@ bool MPSolver::VerifySolution(double max_absolute_error,
|
||||
objective_sum.Add(term);
|
||||
inaccurate_objective_value += term;
|
||||
}
|
||||
objective_sum.Add(objective.offset());
|
||||
const double actual_objective_value = objective_sum.Value();
|
||||
if (fabs(actual_objective_value - objective.Value()) > max_absolute_error) {
|
||||
if (!AreWithinAbsoluteOrRelativeTolerances(objective.Value(),
|
||||
actual_objective_value,
|
||||
tolerance,
|
||||
tolerance)) {
|
||||
++num_errors;
|
||||
max_observed_error = std::max(
|
||||
max_observed_error, fabs(actual_objective_value - objective.Value()));
|
||||
max_observed_error, fabs(actual_objective_value - objective.Value()));
|
||||
LOG_IF(ERROR, log_errors)
|
||||
<< "Objective value " << objective.Value() << " isn't accurate"
|
||||
<< ", it should be " << actual_objective_value
|
||||
<< " (delta=" << actual_objective_value - objective.Value() << ").";
|
||||
} else if (fabs(inaccurate_objective_value - objective.Value()) >
|
||||
max_absolute_error) {
|
||||
} else if (!AreWithinAbsoluteOrRelativeTolerances(objective.Value(),
|
||||
inaccurate_objective_value,
|
||||
tolerance,
|
||||
tolerance)) {
|
||||
LOG_IF(WARNING, log_errors)
|
||||
<< "Objective value " << objective.Value() << " doesn't correspond"
|
||||
<< " to the value computed with the standard (and therefore inaccurate)"
|
||||
<< " sum of its terms.";
|
||||
}
|
||||
if (observed_max_absolute_error != NULL) {
|
||||
*observed_max_absolute_error = max_observed_error;
|
||||
}
|
||||
if (num_errors > 0) {
|
||||
LOG_IF(ERROR, log_errors)
|
||||
<< "There were " << num_errors << " errors above the threshold ("
|
||||
<< max_absolute_error << "), the largest was " << max_observed_error;
|
||||
<< "There were " << num_errors << " errors above the tolerance ("
|
||||
<< tolerance << "), the largest was " << max_observed_error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1148,6 +1207,22 @@ bool MPSolver::OwnsVariable(const MPVariable* var) const {
|
||||
return variables_[var_index] == var;
|
||||
}
|
||||
|
||||
|
||||
bool MPSolver::ExportModelAsLpFormat(bool obfuscate, string* output) {
|
||||
MPModelProto proto;
|
||||
ExportModel(&proto);
|
||||
MPModelProtoExporter exporter(proto);
|
||||
return exporter.ExportModelAsLpFormat(obfuscate, output);
|
||||
}
|
||||
|
||||
bool MPSolver::ExportModelAsMpsFormat(
|
||||
bool fixed_format, bool obfuscate, string* output) {
|
||||
MPModelProto proto;
|
||||
ExportModel(&proto);
|
||||
MPModelProtoExporter exporter(proto);
|
||||
return exporter.ExportModelAsMpsFormat(fixed_format, obfuscate, output);
|
||||
}
|
||||
|
||||
// ---------- MPSolverInterface ----------
|
||||
|
||||
const int MPSolverInterface::kDummyVariableIndex = 0;
|
||||
@@ -1160,25 +1235,6 @@ MPSolverInterface::MPSolverInterface(MPSolver* const solver)
|
||||
|
||||
MPSolverInterface::~MPSolverInterface() {}
|
||||
|
||||
void MPSolverInterface::WriteModelToPredefinedFiles() {
|
||||
// TODO(user): make this method return a boolean.
|
||||
if (!FLAGS_solver_write_model.empty()) {
|
||||
if (!solver_->CheckAllNamesValidity()) {
|
||||
LOG(DFATAL) << "Invalid name. Unable to write model to file";
|
||||
return;
|
||||
}
|
||||
WriteModel(FLAGS_solver_write_model);
|
||||
}
|
||||
const string filename = solver_->write_model_filename();
|
||||
if (!filename.empty()) {
|
||||
if (!solver_->CheckAllNamesValidity()) {
|
||||
LOG(DFATAL) << "Invalid name. Unable to write model to file";
|
||||
return;
|
||||
}
|
||||
WriteModel(filename);
|
||||
}
|
||||
}
|
||||
|
||||
void MPSolverInterface::ExtractModel() {
|
||||
switch (sync_status_) {
|
||||
case MUST_RELOAD: {
|
||||
@@ -1319,6 +1375,15 @@ void MPSolverInterface::SetIntegerParamToUnsupportedValue(
|
||||
<< " to an unsupported value: " << value;
|
||||
}
|
||||
|
||||
bool MPSolverInterface::ReadParameterFile(const string& filename) {
|
||||
LOG(WARNING) << "ReadParameterFile() not supported by this solver.";
|
||||
return false;
|
||||
}
|
||||
|
||||
string MPSolverInterface::ValidFileExtensionForParameterFile() const {
|
||||
return ".tmp";
|
||||
}
|
||||
|
||||
// ---------- MPSolverParameters ----------
|
||||
|
||||
const double MPSolverParameters::kDefaultRelativeMipGap = 1e-4;
|
||||
@@ -1510,3 +1575,4 @@ int MPSolverParameters::GetIntegerParam(
|
||||
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -163,6 +163,7 @@ class MPSolverInterface;
|
||||
class MPSolverParameters;
|
||||
class MPVariable;
|
||||
|
||||
|
||||
// This mathematical programming (MP) solver class is the main class
|
||||
// though which users build and solve problems.
|
||||
class MPSolver {
|
||||
@@ -321,23 +322,26 @@ class MPSolver {
|
||||
ResultStatus Solve(const MPSolverParameters& param);
|
||||
|
||||
// Advanced usage:
|
||||
// Verifies the *correctness* of solution: all variables must be within
|
||||
// their domain, all constraints must be satisfied, and the reported
|
||||
// Verifies the *correctness* of the solution: all variables must be within
|
||||
// their domains, all constraints must be satisfied, and the reported
|
||||
// objective value must be accurate.
|
||||
// Usage:
|
||||
// - This can only be called after Solve() was called.
|
||||
// - If "max_absolute_error" is negative, it will be set to infinity().
|
||||
// - "tolerance" is interpreted as an absolute error threshold.
|
||||
// - For the objective value only, if the absolute error is too large,
|
||||
// the tolerance is interpreted as a relative error threshold instead.
|
||||
// - If "log_errors" is true, every single violation will be logged.
|
||||
// - The observer maximum absolute error is output if
|
||||
// "observed_max_absolute_error" is not NULL.
|
||||
// - If "tolerance" is negative, it will be set to infinity().
|
||||
//
|
||||
// Most users should just set the --verify_solution flag and not bother
|
||||
// using this method directly.
|
||||
bool VerifySolution(double max_absolute_error,
|
||||
bool log_errors,
|
||||
double* observed_max_absolute_error) const;
|
||||
bool VerifySolution(double tolerance, bool log_errors) const;
|
||||
|
||||
// Advanced usage: resets extracted model to solve from scratch.
|
||||
// Advanced usage: resets extracted model to solve from scratch. This won't
|
||||
// reset the parameters that were set with
|
||||
// SetSolverSpecificParametersAsString() or set_time_limit() or even clear the
|
||||
// linear program. It will just make sure that next Solve() will be as if
|
||||
// everything was reconstructed from scratch.
|
||||
void Reset();
|
||||
|
||||
// ----- Methods using protocol buffers -----
|
||||
@@ -367,6 +371,7 @@ class MPSolver {
|
||||
static void SolveWithProtocolBuffers(const MPModelRequest& model_request,
|
||||
MPSolutionResponse* response);
|
||||
|
||||
|
||||
// Exports model to protocol buffer.
|
||||
void ExportModel(MPModelProto* output_model) const;
|
||||
|
||||
@@ -400,8 +405,25 @@ class MPSolver {
|
||||
// VerifySolution() for that.
|
||||
bool LoadSolutionFromProto(const MPSolutionResponse& response);
|
||||
|
||||
// ----- Export model to files or strings -----
|
||||
|
||||
// Shortcuts to the homonymous MPModelProtoExporter methods, via
|
||||
// exporting to a MPModelProto with ExportModel() (see above).
|
||||
bool ExportModelAsLpFormat(bool obfuscated, string* model_str);
|
||||
bool ExportModelAsMpsFormat(
|
||||
bool fixed_format, bool obfuscated, string* model_str);
|
||||
|
||||
// ----- Misc -----
|
||||
|
||||
// Advanced usage: 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.
|
||||
//
|
||||
// TODO(user): Currently SCIP will always return true even if the format is
|
||||
// wrong (you can check the log if you suspect an issue there). This seems to
|
||||
// be a bug in SCIP though.
|
||||
bool SetSolverSpecificParametersAsString(const string& parameters);
|
||||
|
||||
// Advanced usage: possible basis status values for a variable and the
|
||||
// slack variable of a linear constraint.
|
||||
enum BasisStatus {
|
||||
@@ -426,14 +448,6 @@ class MPSolver {
|
||||
// stdout.
|
||||
void EnableOutput();
|
||||
|
||||
void set_write_model_filename(const string &filename) {
|
||||
write_model_filename_ = filename;
|
||||
}
|
||||
|
||||
string write_model_filename() const {
|
||||
return write_model_filename_;
|
||||
}
|
||||
|
||||
void set_time_limit(int64 time_limit_milliseconds) {
|
||||
DCHECK_GE(time_limit_milliseconds, 0);
|
||||
time_limit_ = time_limit_milliseconds;
|
||||
@@ -456,10 +470,12 @@ class MPSolver {
|
||||
// discrete problems.
|
||||
int64 nodes() const;
|
||||
|
||||
// Checks the validity of a variable or constraint name.
|
||||
bool CheckNameValidity(const string& name);
|
||||
// Checks the validity of all variables and constraints names.
|
||||
bool CheckAllNamesValidity();
|
||||
// True iff all variable and constraint names allow exporting the model
|
||||
// to a file (or as a proto).
|
||||
// See MPModelProtoExporter::CheckNameValidity() in ./model_exporter.h.
|
||||
bool var_and_constraint_names_allow_export() {
|
||||
return var_and_constraint_names_allow_export_;
|
||||
}
|
||||
|
||||
// Returns a string describing the underlying solver and its version.
|
||||
string SolverVersion() const;
|
||||
@@ -562,15 +578,24 @@ class MPSolver {
|
||||
// Time limit in milliseconds (0 = no limit).
|
||||
int64 time_limit_;
|
||||
|
||||
// Name of the file where the solver writes out the model when Solve
|
||||
// is called. If empty, no file is written.
|
||||
string write_model_filename_;
|
||||
// See the homonymous getter above.
|
||||
bool var_and_constraint_names_allow_export_;
|
||||
|
||||
WallTimer timer_;
|
||||
|
||||
// Permanent storage for SetSolverSpecificParametersAsString().
|
||||
string solver_specific_parameter_string_;
|
||||
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MPSolver);
|
||||
};
|
||||
|
||||
// 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_) { ... }
|
||||
typedef hash_map<const MPVariable*, double> CoeffMap;
|
||||
typedef std::pair<const MPVariable*, double> CoeffEntry;
|
||||
|
||||
// A class to express a linear objective.
|
||||
class MPObjective {
|
||||
public:
|
||||
@@ -633,12 +658,12 @@ class MPObjective {
|
||||
// At construction, an MPObjective has no terms (which is equivalent
|
||||
// on having a coefficient of 0 for all variables), and an offset of 0.
|
||||
explicit MPObjective(MPSolverInterface* const interface)
|
||||
: interface_(interface), offset_(0.0) {}
|
||||
: interface_(interface), coefficients_(1), offset_(0.0) {}
|
||||
|
||||
MPSolverInterface* const interface_;
|
||||
|
||||
// Mapping var -> coefficient.
|
||||
hash_map<const MPVariable*, double> coefficients_;
|
||||
CoeffMap coefficients_;
|
||||
// Constant term.
|
||||
double offset_;
|
||||
|
||||
@@ -744,6 +769,18 @@ class MPConstraint {
|
||||
// Sets both the lower and upper bounds.
|
||||
void SetBounds(double lb, double ub);
|
||||
|
||||
// Advanced usage: returns true if the constraint is "lazy" (see below).
|
||||
bool is_lazy() const { return is_lazy_; }
|
||||
// Advanced usage: sets the constraint "laziness".
|
||||
// *** This is only supported for SCIP and has no effect on other solvers. ***
|
||||
// When 'laziness' is true, the constraint is only considered by the Linear
|
||||
// Programming solver if its current solution violates the constraint.
|
||||
// In this case, the constraint is definitively added to the problem.
|
||||
// This may be useful in some MIP problems, and may have a dramatic impact
|
||||
// on performance.
|
||||
// For more info see: http://tinyurl.com/lazy-constraints.
|
||||
void set_is_lazy(bool laziness) { is_lazy_ = laziness; }
|
||||
|
||||
// Returns the constraint's activity in the current solution:
|
||||
// sum over all terms of (coefficient * variable value)
|
||||
double activity() const;
|
||||
@@ -778,8 +815,8 @@ class MPConstraint {
|
||||
double ub,
|
||||
const string& name,
|
||||
MPSolverInterface* const interface)
|
||||
: lb_(lb), ub_(ub), name_(name), index_(-1), dual_value_(0.0),
|
||||
activity_(0.0), interface_(interface) {}
|
||||
: coefficients_(1), lb_(lb), ub_(ub), name_(name), is_lazy_(false),
|
||||
index_(-1), dual_value_(0.0), activity_(0.0), interface_(interface) {}
|
||||
|
||||
void set_index(int index) { index_ = index; }
|
||||
void set_activity(double activity) { activity_ = activity; }
|
||||
@@ -791,14 +828,21 @@ class MPConstraint {
|
||||
bool ContainsNewVariables();
|
||||
|
||||
// Mapping var -> coefficient.
|
||||
hash_map<const MPVariable*, double> coefficients_;
|
||||
CoeffMap coefficients_;
|
||||
|
||||
// The lower bound for the linear constraint.
|
||||
double lb_;
|
||||
|
||||
// The upper bound for the linear constraint.
|
||||
double ub_;
|
||||
|
||||
// Name.
|
||||
const string name_;
|
||||
|
||||
// True if the constraint is "lazy", i.e. the constraint is added to the
|
||||
// underlying Linear Programming solver only if it is violated.
|
||||
// By default this parameter is 'false'.
|
||||
bool is_lazy_;
|
||||
int index_;
|
||||
double dual_value_;
|
||||
double activity_;
|
||||
@@ -807,6 +851,7 @@ class MPConstraint {
|
||||
};
|
||||
|
||||
|
||||
|
||||
// This class stores parameter settings for LP and MIP solvers.
|
||||
// Some parameters are marked as advanced: do not change their values
|
||||
// unless you know what you are doing!
|
||||
@@ -998,8 +1043,7 @@ class MPSolverInterface {
|
||||
|
||||
// ----- Solve -----
|
||||
// Solves problem with specified parameter values. Returns true if the
|
||||
// solution is optimal. Calls WriteModelToPredefinedFiles
|
||||
// to allow the user to write the model to a file.
|
||||
// solution is optimal.
|
||||
virtual MPSolver::ResultStatus Solve(const MPSolverParameters& param) = 0;
|
||||
|
||||
// ----- Model modifications and extraction -----
|
||||
@@ -1080,9 +1124,6 @@ class MPSolverInterface {
|
||||
virtual bool CheckBestObjectiveBoundExists() const;
|
||||
|
||||
// ----- Misc -----
|
||||
// Writes model to a file.
|
||||
virtual void WriteModel(const string& filename) = 0;
|
||||
|
||||
// Queries problem type. For simplicity, the distinction between
|
||||
// continuous and discrete is based on the declaration of the user
|
||||
// when the solver is created (example: GLPK_LINEAR_PROGRAMMING
|
||||
@@ -1156,16 +1197,6 @@ class MPSolverInterface {
|
||||
// objective offset.
|
||||
static const int kDummyVariableIndex;
|
||||
|
||||
// Writes out the model to a file specified by the
|
||||
// --solver_write_model command line argument or
|
||||
// MPSolver::set_write_model_filename.
|
||||
// The file is written by each solver interface (CBC, CLP, GLPK, SCIP) and
|
||||
// each behaves a little differently.
|
||||
// If filename ends in ".lp", then the file is written in the
|
||||
// LP format (except for the CLP solver that does not support the LP
|
||||
// format). In all other cases it is written in the MPS format.
|
||||
void WriteModelToPredefinedFiles();
|
||||
|
||||
// Extracts model stored in MPSolver.
|
||||
void ExtractModel();
|
||||
// Extracts the variables that have not been extracted yet.
|
||||
@@ -1202,6 +1233,15 @@ class MPSolverInterface {
|
||||
virtual void SetDualTolerance(double value) = 0;
|
||||
virtual void SetPresolveMode(int value) = 0;
|
||||
|
||||
// Reads a solver-specific file of parameters and set them.
|
||||
// Returns true if there was no errors.
|
||||
virtual bool ReadParameterFile(const string& filename);
|
||||
|
||||
// Returns a file extension like ".tmp", this is needed because some solvers
|
||||
// require a given extension for the ReadParameterFile() filename and we need
|
||||
// to know it to generate a temporary parameter file.
|
||||
virtual string ValidFileExtensionForParameterFile() const;
|
||||
|
||||
// Sets the scaling mode.
|
||||
virtual void SetScalingMode(int value) = 0;
|
||||
virtual void SetLpAlgorithm(int value) = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
%include base/base.swig
|
||||
%include util/data.swig
|
||||
// Swig doesn't like module initializers.
|
||||
#define DECLARE_MODULE_INITIALIZER(x);
|
||||
|
||||
// Include the file we want to wrap a first time.
|
||||
%{
|
||||
@@ -372,9 +374,29 @@ class LinearConstraint(object):
|
||||
# See ../python/linear_programming.py for example on how to use the nice
|
||||
# extended python API provided below.
|
||||
%extend MPSolver {
|
||||
string ExportModelAsLpFormat(bool obfuscated) {
|
||||
string output;
|
||||
if (!self->ExportModelAsLpFormat(obfuscated, &output)) return "";
|
||||
return output;
|
||||
}
|
||||
|
||||
string ExportModelAsMpsFormat(bool fixed_format, bool obfuscated) {
|
||||
string output;
|
||||
if (!self->ExportModelAsMpsFormat(fixed_format, obfuscated, &output)) {
|
||||
return "";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
%pythoncode {
|
||||
def Add(self, constraint, name=''):
|
||||
return constraint.Extract(self, name)
|
||||
if isinstance(constraint, bool):
|
||||
if constraint:
|
||||
return self.RowConstraint(0, 0, name)
|
||||
else:
|
||||
return self.RowConstraint(1, 1, name)
|
||||
else:
|
||||
return constraint.Extract(self, name)
|
||||
|
||||
def Sum(self, expr_array):
|
||||
result = SumArray(expr_array)
|
||||
@@ -435,6 +457,8 @@ namespace operations_research {
|
||||
%rename (setCoefficient) MPConstraint::SetCoefficient;
|
||||
%rename (setLb) MPConstraint::SetLB;
|
||||
%rename (setUb) MPConstraint::SetUB;
|
||||
%rename (setIsLazy) MPConstraint::set_is_lazy;
|
||||
%rename (isLazy) MPConstraint::is_lazy;
|
||||
|
||||
// Rename rules on MPObjective.
|
||||
%rename (addOffset) MPObjective::AddOffset;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -107,9 +107,6 @@ class SCIPInterface : public MPSolverInterface {
|
||||
}
|
||||
|
||||
// ----- Misc -----
|
||||
// Write model
|
||||
virtual void WriteModel(const string& filename);
|
||||
|
||||
// Query problem type.
|
||||
virtual bool IsContinuous() const { return false; }
|
||||
virtual bool IsLP() const { return false; }
|
||||
@@ -140,6 +137,9 @@ class SCIPInterface : public MPSolverInterface {
|
||||
virtual void SetScalingMode(int value);
|
||||
virtual void SetLpAlgorithm(int value);
|
||||
|
||||
virtual bool ReadParameterFile(const string& filename);
|
||||
virtual string ValidFileExtensionForParameterFile() const;
|
||||
|
||||
void CreateSCIP();
|
||||
void DeleteSCIP();
|
||||
|
||||
@@ -201,21 +201,6 @@ void SCIPInterface::DeleteSCIP() {
|
||||
scip_ = NULL;
|
||||
}
|
||||
|
||||
void SCIPInterface::WriteModel(const string& filename) {
|
||||
#if (SCIP_VERSION < 300)
|
||||
// The message handler also controls how the model is written to a file.
|
||||
// If it is NULL, then nothing is written to the file.
|
||||
ORTOOLS_SCIP_CALL(SCIPsetDefaultMessagehdlr());
|
||||
#endif
|
||||
ORTOOLS_SCIP_CALL(SCIPwriteOrigProblem(scip_, filename.c_str(), NULL, false));
|
||||
// Restore mesage handler to its original value.
|
||||
#if (SCIP_VERSION < 300)
|
||||
if (quiet_) {
|
||||
ORTOOLS_SCIP_CALL(SCIPsetMessagehdlr(NULL));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------ Model modifications and extraction -----
|
||||
|
||||
// Not cached
|
||||
@@ -504,8 +489,6 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) {
|
||||
ExtractModel();
|
||||
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
||||
|
||||
WriteModelToPredefinedFiles();
|
||||
|
||||
// Time limit.
|
||||
if (solver_->time_limit()) {
|
||||
VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
|
||||
@@ -515,6 +498,10 @@ 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).
|
||||
solver_->SetSolverSpecificParametersAsString(
|
||||
solver_->solver_specific_parameter_string_);
|
||||
SetParameters(param);
|
||||
|
||||
// Solve.
|
||||
@@ -678,6 +665,14 @@ void SCIPInterface::SetLpAlgorithm(int value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SCIPInterface::ReadParameterFile(const string& filename) {
|
||||
return SCIPreadParams(scip_, filename.c_str()) == SCIP_OKAY;
|
||||
}
|
||||
|
||||
string SCIPInterface::ValidFileExtensionForParameterFile() const {
|
||||
return ".set";
|
||||
}
|
||||
|
||||
MPSolverInterface* BuildSCIPInterface(MPSolver* const solver) {
|
||||
return new SCIPInterface(solver);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2012 Google
|
||||
// Copyright 2010-2013 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
Reference in New Issue
Block a user