2014-07-09 10:01:48 +00:00
|
|
|
// Copyright 2010-2014 Google
|
2012-10-28 09:07:15 +00:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
2014-07-09 15:18:27 +00:00
|
|
|
|
2015-04-29 19:01:13 +02:00
|
|
|
#if defined(USE_GUROBI)
|
|
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstddef>
|
2012-10-28 09:07:15 +00:00
|
|
|
#include "base/hash.h"
|
|
|
|
|
#include <limits>
|
2013-12-05 09:24:58 +00:00
|
|
|
#include "base/unique_ptr.h"
|
2012-10-28 09:07:15 +00:00
|
|
|
#include <string>
|
|
|
|
|
#include <utility>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "base/commandlineflags.h"
|
|
|
|
|
#include "base/integral_types.h"
|
|
|
|
|
#include "base/logging.h"
|
|
|
|
|
#include "base/stringprintf.h"
|
|
|
|
|
#include "base/timer.h"
|
2013-10-10 15:23:20 +00:00
|
|
|
#include "base/map_util.h"
|
2012-10-28 09:07:15 +00:00
|
|
|
#include "linear_solver/linear_solver.h"
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
2014-01-08 12:01:58 +00:00
|
|
|
#include "gurobi_c.h"
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
#define CHECKED_GUROBI_CALL(x) CHECK_EQ(0, x)
|
|
|
|
|
|
2014-12-16 11:35:09 +00:00
|
|
|
DEFINE_int32(num_gurobi_threads, 4, "Number of threads available for Gurobi.");
|
|
|
|
|
|
2012-10-28 09:07:15 +00:00
|
|
|
namespace operations_research {
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
class GurobiInterface : public MPSolverInterface {
|
2012-10-28 09:07:15 +00:00
|
|
|
public:
|
|
|
|
|
// Constructor that takes a name for the underlying GRB solver.
|
2013-01-10 17:00:09 +00:00
|
|
|
explicit GurobiInterface(MPSolver* const solver, bool mip);
|
2015-04-16 16:21:56 +02:00
|
|
|
~GurobiInterface() override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// Sets the optimization direction (min/max).
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetOptimizationDirection(bool maximize) override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// ----- Solve -----
|
2013-01-10 17:00:09 +00:00
|
|
|
// Solves the problem using the parameter values specified.
|
2015-04-16 16:21:56 +02:00
|
|
|
MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// ----- Model modifications and extraction -----
|
|
|
|
|
// Resets extracted model
|
2015-04-16 16:21:56 +02:00
|
|
|
void Reset() override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
// Modifies bounds.
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetVariableBounds(int var_index, double lb, double ub) override;
|
|
|
|
|
void SetVariableInteger(int var_index, bool integer) override;
|
|
|
|
|
void SetConstraintBounds(int row_index, double lb, double ub) override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
// Adds Constraint incrementally.
|
2015-04-16 16:21:56 +02:00
|
|
|
void AddRowConstraint(MPConstraint* const ct) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Adds variable incrementally.
|
2015-04-16 16:21:56 +02:00
|
|
|
void AddVariable(MPVariable* const var) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Changes a coefficient in a constraint.
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetCoefficient(MPConstraint* const constraint,
|
|
|
|
|
const MPVariable* const variable, double new_value,
|
|
|
|
|
double old_value) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Clears a constraint from all its terms.
|
2015-04-16 16:21:56 +02:00
|
|
|
void ClearConstraint(MPConstraint* const constraint) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Changes a coefficient in the linear objective
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetObjectiveCoefficient(const MPVariable* const variable,
|
|
|
|
|
double coefficient) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Changes the constant term in the linear objective.
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetObjectiveOffset(double value) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Clears the objective from all its terms.
|
2015-04-16 16:21:56 +02:00
|
|
|
void ClearObjective() override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// ------ Query statistics on the solution and the solve ------
|
2013-01-10 17:00:09 +00:00
|
|
|
// Number of simplex or interior-point iterations
|
2015-04-16 16:21:56 +02:00
|
|
|
int64 iterations() const override;
|
2012-10-28 09:07:15 +00:00
|
|
|
// Number of branch-and-bound nodes. Only available for discrete problems.
|
2015-04-16 16:21:56 +02:00
|
|
|
int64 nodes() const override;
|
2012-10-28 09:07:15 +00:00
|
|
|
// Best objective bound. Only available for discrete problems.
|
2015-04-16 16:21:56 +02:00
|
|
|
double best_objective_bound() const override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// Returns the basis status of a row.
|
2015-04-16 16:21:56 +02:00
|
|
|
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
2012-10-28 09:07:15 +00:00
|
|
|
// Returns the basis status of a column.
|
2015-04-16 16:21:56 +02:00
|
|
|
MPSolver::BasisStatus column_status(int variable_index) const override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// ----- Misc -----
|
2013-01-10 17:00:09 +00:00
|
|
|
// Queries problem type.
|
2015-04-16 16:21:56 +02:00
|
|
|
bool IsContinuous() const override { return IsLP(); }
|
|
|
|
|
bool IsLP() const override { return !mip_; }
|
|
|
|
|
bool IsMIP() const override { return mip_; }
|
2012-10-28 09:07:15 +00:00
|
|
|
|
2015-04-16 16:21:56 +02:00
|
|
|
void ExtractNewVariables() override;
|
|
|
|
|
void ExtractNewConstraints() override;
|
|
|
|
|
void ExtractObjective() override;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
2015-04-16 16:21:56 +02:00
|
|
|
std::string SolverVersion() const override {
|
2012-10-28 09:07:15 +00:00
|
|
|
int major, minor, technical;
|
|
|
|
|
GRBversion(&major, &minor, &technical);
|
2014-01-08 12:01:58 +00:00
|
|
|
return StringPrintf("Gurobi library version %d.%d.%d\n", major, minor,
|
2012-10-28 09:07:15 +00:00
|
|
|
technical);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 16:21:56 +02:00
|
|
|
void* underlying_solver() override { return reinterpret_cast<void*>(model_); }
|
2012-10-28 09:07:15 +00:00
|
|
|
|
2015-04-16 16:21:56 +02:00
|
|
|
double ComputeExactConditionNumber() const override {
|
2013-01-10 17:00:09 +00:00
|
|
|
if (!IsContinuous()) {
|
2014-01-08 12:01:58 +00:00
|
|
|
LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
|
|
|
|
|
<< " GUROBI_MIXED_INTEGER_PROGRAMMING";
|
2013-01-10 17:00:09 +00:00
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(user,user): Not yet working.
|
2014-01-08 12:01:58 +00:00
|
|
|
LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
|
|
|
|
|
<< " GUROBI_LINEAR_PROGRAMMING";
|
|
|
|
|
return 0.0;
|
2013-01-10 17:00:09 +00:00
|
|
|
|
|
|
|
|
// double cond = 0.0;
|
|
|
|
|
// const int status = GRBgetdblattr(model_, GRB_DBL_ATTR_KAPPA, &cond);
|
|
|
|
|
// if (0 == status) {
|
|
|
|
|
// return cond;
|
|
|
|
|
// } else {
|
|
|
|
|
// LOG(DFATAL) << "Condition number only available for "
|
|
|
|
|
// << "continuous problems";
|
|
|
|
|
// return 0.0;
|
|
|
|
|
// }
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2013-01-10 17:00:09 +00:00
|
|
|
// Sets all parameters in the underlying solver.
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetParameters(const MPSolverParameters& param) override;
|
2013-01-10 17:00:09 +00:00
|
|
|
// Sets each parameter in the underlying solver.
|
2015-04-16 16:21:56 +02:00
|
|
|
void SetRelativeMipGap(double value) override;
|
|
|
|
|
void SetPrimalTolerance(double value) override;
|
|
|
|
|
void SetDualTolerance(double value) override;
|
|
|
|
|
void SetPresolveMode(int value) override;
|
|
|
|
|
void SetScalingMode(int value) override;
|
|
|
|
|
void SetLpAlgorithm(int value) override;
|
|
|
|
|
|
|
|
|
|
bool ReadParameterFile(const std::string& filename) override;
|
|
|
|
|
std::string ValidFileExtensionForParameterFile() const override;
|
2013-06-11 14:49:45 +00:00
|
|
|
|
2014-01-08 12:01:58 +00:00
|
|
|
MPSolver::BasisStatus TransformGRBVarBasisStatus(int gurobi_basis_status)
|
|
|
|
|
const;
|
|
|
|
|
MPSolver::BasisStatus TransformGRBConstraintBasisStatus(
|
|
|
|
|
int gurobi_basis_status, int constraint_index) const;
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
GRBmodel* model_;
|
|
|
|
|
GRBenv* env_;
|
|
|
|
|
bool mip_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Creates a LP/MIP instance with the specified name and minimization objective.
|
2013-01-10 17:00:09 +00:00
|
|
|
GurobiInterface::GurobiInterface(MPSolver* const solver, bool mip)
|
2014-01-08 12:01:58 +00:00
|
|
|
: MPSolverInterface(solver), model_(0), env_(0), mip_(mip) {
|
|
|
|
|
if (GRBloadenv(&env_, NULL) != 0 || env_ == NULL) {
|
2013-01-10 17:00:09 +00:00
|
|
|
LOG(FATAL) << "Error: could not create environment";
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
2013-01-10 17:00:09 +00:00
|
|
|
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBnewmodel(env_, &model_, solver_->name_.c_str(),
|
|
|
|
|
0, // numvars
|
|
|
|
|
NULL, // obj
|
|
|
|
|
NULL, // lb
|
|
|
|
|
NULL, // ub
|
|
|
|
|
NULL, // vtype
|
2013-01-10 17:00:09 +00:00
|
|
|
NULL)); // varnanes
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintattr(model_, GRB_INT_ATTR_MODELSENSE, maximize_ ? -1 : 1));
|
2014-12-16 11:35:09 +00:00
|
|
|
|
|
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintparam(env_, GRB_INT_PAR_THREADS, FLAGS_num_gurobi_threads));
|
2013-01-10 17:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GurobiInterface::~GurobiInterface() {
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBfreemodel(model_));
|
2012-10-28 09:07:15 +00:00
|
|
|
GRBfreeenv(env_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------ Model modifications and extraction -----
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::Reset() {
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBfreemodel(model_));
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBnewmodel(env_, &model_, solver_->name_.c_str(),
|
|
|
|
|
0, // numvars
|
|
|
|
|
NULL, // obj
|
|
|
|
|
NULL, // lb
|
|
|
|
|
NULL, // ub
|
|
|
|
|
NULL, // vtype
|
2013-01-10 17:00:09 +00:00
|
|
|
NULL)); // varnames
|
2012-10-28 09:07:15 +00:00
|
|
|
ResetExtractionInformation();
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetOptimizationDirection(bool maximize) {
|
|
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
// TODO(user,user): Fix, not yet working.
|
|
|
|
|
// InvalidateSolutionSynchronization();
|
|
|
|
|
// CHECKED_GUROBI_CALL(GRBsetintattr(model_,
|
|
|
|
|
// GRB_INT_ATTR_MODELSENSE,
|
|
|
|
|
// maximize_ ? -1 : 1));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetVariableBounds(int var_index, double lb, double ub) {
|
|
|
|
|
sync_status_ = MUST_RELOAD;
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Modifies integrality of an extracted variable.
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetVariableInteger(int index, bool integer) {
|
2012-10-28 09:07:15 +00:00
|
|
|
char current_type;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBgetcharattrelement(model_, GRB_CHAR_ATTR_VTYPE, index, ¤t_type));
|
|
|
|
|
|
|
|
|
|
if ((integer &&
|
|
|
|
|
(current_type == GRB_INTEGER || current_type == GRB_BINARY)) ||
|
2015-04-29 19:01:13 +02:00
|
|
|
(!integer && current_type == GRB_CONTINUOUS)) {
|
2012-10-28 09:07:15 +00:00
|
|
|
return;
|
2015-04-29 19:01:13 +02:00
|
|
|
}
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
InvalidateSolutionSynchronization();
|
|
|
|
|
if (sync_status_ == MODEL_SYNCHRONIZED) {
|
|
|
|
|
char type_var;
|
|
|
|
|
if (integer) {
|
2013-01-10 17:00:09 +00:00
|
|
|
type_var = GRB_INTEGER;
|
2012-10-28 09:07:15 +00:00
|
|
|
} else {
|
2013-01-10 17:00:09 +00:00
|
|
|
type_var = GRB_CONTINUOUS;
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetcharattrelement(model_, GRB_CHAR_ATTR_VTYPE, index, type_var));
|
2012-10-28 09:07:15 +00:00
|
|
|
} else {
|
|
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetConstraintBounds(int index, double lb, double ub) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::AddRowConstraint(MPConstraint* const ct) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::AddVariable(MPVariable* const ct) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetCoefficient(MPConstraint* const constraint,
|
|
|
|
|
const MPVariable* const variable,
|
2014-01-08 12:01:58 +00:00
|
|
|
double new_value, double old_value) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::ClearConstraint(MPConstraint* const constraint) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetObjectiveCoefficient(const MPVariable* const variable,
|
|
|
|
|
double coefficient) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetObjectiveOffset(double value) {
|
2012-10-28 09:07:15 +00:00
|
|
|
sync_status_ = MUST_RELOAD;
|
2013-01-10 17:00:09 +00:00
|
|
|
// TODO(user,user): make it work.
|
|
|
|
|
// InvalidateSolutionSynchronization();
|
|
|
|
|
// CHECKED_GUROBI_CALL(GRBsetdblattr(model_,
|
|
|
|
|
// GRB_DBL_ATTR_OBJCON,
|
|
|
|
|
// solver_->Objective().offset()));
|
|
|
|
|
// CHECKED_GUROBI_CALL(GRBupdatemodel(model_));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2014-01-08 12:01:58 +00:00
|
|
|
void GurobiInterface::ClearObjective() { sync_status_ = MUST_RELOAD; }
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// ------ Query statistics on the solution and the solve ------
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
int64 GurobiInterface::iterations() const {
|
2012-10-28 09:07:15 +00:00
|
|
|
double iter;
|
2013-01-10 17:00:09 +00:00
|
|
|
if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfIterations;
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattr(model_, GRB_DBL_ATTR_ITERCOUNT, &iter));
|
2012-10-28 09:07:15 +00:00
|
|
|
return static_cast<int64>(iter);
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
int64 GurobiInterface::nodes() const {
|
2012-10-28 09:07:15 +00:00
|
|
|
if (mip_) {
|
2013-01-10 17:00:09 +00:00
|
|
|
if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfNodes;
|
2012-10-28 09:07:15 +00:00
|
|
|
double nodes = 0;
|
2013-01-10 17:00:09 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattr(model_, GRB_DBL_ATTR_NODECOUNT, &nodes));
|
2012-10-28 09:07:15 +00:00
|
|
|
return static_cast<int64>(nodes);
|
|
|
|
|
} else {
|
2013-01-10 17:00:09 +00:00
|
|
|
LOG(DFATAL) << "Number of nodes only available for discrete problems.";
|
2012-10-28 09:07:15 +00:00
|
|
|
return kUnknownNumberOfNodes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the best objective bound. Only available for discrete problems.
|
2013-01-10 17:00:09 +00:00
|
|
|
double GurobiInterface::best_objective_bound() const {
|
2012-10-28 09:07:15 +00:00
|
|
|
if (mip_) {
|
2013-01-10 17:00:09 +00:00
|
|
|
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
|
|
|
|
return trivial_worst_objective_bound();
|
|
|
|
|
}
|
2012-10-28 09:07:15 +00:00
|
|
|
if (solver_->variables_.size() == 0 && solver_->constraints_.size() == 0) {
|
|
|
|
|
// Special case for empty model.
|
|
|
|
|
return solver_->Objective().offset();
|
|
|
|
|
} else {
|
|
|
|
|
double value;
|
2013-01-10 17:00:09 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattr(model_, GRB_DBL_ATTR_OBJBOUND, &value));
|
2012-10-28 09:07:15 +00:00
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2013-01-10 17:00:09 +00:00
|
|
|
LOG(DFATAL) << "Best objective bound only available for discrete problems.";
|
|
|
|
|
return trivial_worst_objective_bound();
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-08 12:01:58 +00:00
|
|
|
MPSolver::BasisStatus GurobiInterface::TransformGRBVarBasisStatus(
|
|
|
|
|
int gurobi_basis_status) const {
|
2012-10-28 09:07:15 +00:00
|
|
|
switch (gurobi_basis_status) {
|
|
|
|
|
case GRB_BASIC:
|
|
|
|
|
return MPSolver::BASIC;
|
2013-01-10 17:00:09 +00:00
|
|
|
case GRB_NONBASIC_LOWER:
|
|
|
|
|
return MPSolver::AT_LOWER_BOUND;
|
2012-10-28 09:07:15 +00:00
|
|
|
case GRB_NONBASIC_UPPER:
|
|
|
|
|
return MPSolver::AT_UPPER_BOUND;
|
|
|
|
|
case GRB_SUPERBASIC:
|
|
|
|
|
return MPSolver::FREE;
|
|
|
|
|
default:
|
2013-01-10 17:00:09 +00:00
|
|
|
LOG(DFATAL) << "Unknown GRB basis status.";
|
2012-10-28 09:07:15 +00:00
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
MPSolver::BasisStatus GurobiInterface::TransformGRBConstraintBasisStatus(
|
2014-01-08 12:01:58 +00:00
|
|
|
int gurobi_basis_status, int constraint_index) const {
|
2013-01-10 17:00:09 +00:00
|
|
|
switch (gurobi_basis_status) {
|
|
|
|
|
case GRB_BASIC:
|
|
|
|
|
return MPSolver::BASIC;
|
2014-01-08 12:01:58 +00:00
|
|
|
default: {
|
2013-01-10 17:00:09 +00:00
|
|
|
// Non basic.
|
|
|
|
|
double slack = 0.0;
|
|
|
|
|
double tolerance = 0.0;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblparam(
|
|
|
|
|
GRBgetenv(model_), GRB_DBL_PAR_FEASIBILITYTOL, &tolerance));
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattrelement(model_, GRB_DBL_ATTR_SLACK,
|
|
|
|
|
constraint_index, &slack));
|
2013-01-10 17:00:09 +00:00
|
|
|
char sense;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetcharattrelement(model_, GRB_CHAR_ATTR_SENSE,
|
|
|
|
|
constraint_index, &sense));
|
|
|
|
|
VLOG(4) << "constraint " << constraint_index << " , slack = " << slack
|
2013-01-10 17:00:09 +00:00
|
|
|
<< " , sense = " << sense;
|
|
|
|
|
if (fabs(slack) <= tolerance) {
|
|
|
|
|
switch (sense) {
|
|
|
|
|
case GRB_EQUAL:
|
|
|
|
|
case GRB_LESS_EQUAL:
|
|
|
|
|
return MPSolver::AT_UPPER_BOUND;
|
|
|
|
|
case GRB_GREATER_EQUAL:
|
|
|
|
|
return MPSolver::AT_LOWER_BOUND;
|
|
|
|
|
default:
|
|
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-28 09:07:15 +00:00
|
|
|
// Returns the basis status of a row.
|
2013-01-10 17:00:09 +00:00
|
|
|
MPSolver::BasisStatus GurobiInterface::row_status(int constraint_index) const {
|
2012-10-28 09:07:15 +00:00
|
|
|
int optim_status = 0;
|
2013-01-10 17:00:09 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBgetintattr(model_, GRB_INT_ATTR_STATUS, &optim_status));
|
|
|
|
|
if (optim_status != GRB_OPTIMAL && optim_status != GRB_SUBOPTIMAL) {
|
|
|
|
|
LOG(DFATAL) << "Basis status only available after a solution has "
|
|
|
|
|
<< "been found.";
|
|
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
if (mip_) {
|
|
|
|
|
LOG(DFATAL) << "Basis status only available for continuous problems.";
|
2012-10-28 09:07:15 +00:00
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
int gurobi_basis_status = 0;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetintattrelement(
|
|
|
|
|
model_, GRB_INT_ATTR_CBASIS, constraint_index, &gurobi_basis_status));
|
2013-01-10 17:00:09 +00:00
|
|
|
return TransformGRBConstraintBasisStatus(gurobi_basis_status,
|
|
|
|
|
constraint_index);
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the basis status of a column.
|
2013-01-10 17:00:09 +00:00
|
|
|
MPSolver::BasisStatus GurobiInterface::column_status(int variable_index) const {
|
2012-10-28 09:07:15 +00:00
|
|
|
int optim_status = 0;
|
2013-01-10 17:00:09 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBgetintattr(model_, GRB_INT_ATTR_STATUS, &optim_status));
|
|
|
|
|
if (optim_status != GRB_OPTIMAL && optim_status != GRB_SUBOPTIMAL) {
|
|
|
|
|
LOG(DFATAL) << "Basis status only available after a solution has "
|
|
|
|
|
<< "been found.";
|
|
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
if (mip_) {
|
|
|
|
|
LOG(DFATAL) << "Basis status only available for continuous problems.";
|
2012-10-28 09:07:15 +00:00
|
|
|
return MPSolver::FREE;
|
|
|
|
|
}
|
|
|
|
|
int gurobi_basis_status = 0;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetintattrelement(
|
|
|
|
|
model_, GRB_INT_ATTR_VBASIS, variable_index, &gurobi_basis_status));
|
2013-01-10 17:00:09 +00:00
|
|
|
return TransformGRBVarBasisStatus(gurobi_basis_status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extracts new variables.
|
|
|
|
|
void GurobiInterface::ExtractNewVariables() {
|
|
|
|
|
CHECK(last_variable_index_ == 0 ||
|
|
|
|
|
last_variable_index_ == solver_->variables_.size());
|
|
|
|
|
CHECK(last_constraint_index_ == 0 ||
|
|
|
|
|
last_constraint_index_ == solver_->constraints_.size());
|
2015-05-18 19:41:34 +02:00
|
|
|
const int total_num_vars = solver_->variables_.size();
|
2012-10-28 09:07:15 +00:00
|
|
|
if (total_num_vars > last_variable_index_) {
|
2013-01-10 17:00:09 +00:00
|
|
|
int num_new_variables = total_num_vars - last_variable_index_;
|
2013-12-05 09:24:58 +00:00
|
|
|
std::unique_ptr<double[]> obj_coefs(new double[num_new_variables]);
|
|
|
|
|
std::unique_ptr<double[]> lb(new double[num_new_variables]);
|
|
|
|
|
std::unique_ptr<double[]> ub(new double[num_new_variables]);
|
|
|
|
|
std::unique_ptr<char[]> ctype(new char[num_new_variables]);
|
|
|
|
|
std::unique_ptr<const char * []> colname(
|
|
|
|
|
new const char* [num_new_variables]);
|
2013-01-10 17:00:09 +00:00
|
|
|
|
|
|
|
|
for (int j = 0; j < num_new_variables; ++j) {
|
2014-01-08 12:01:58 +00:00
|
|
|
MPVariable* const var = solver_->variables_[last_variable_index_ + j];
|
2015-06-18 14:08:50 +02:00
|
|
|
set_variable_as_extracted(var->index(), true);
|
2012-10-28 09:07:15 +00:00
|
|
|
lb[j] = var->lb();
|
|
|
|
|
ub[j] = var->ub();
|
2013-01-10 17:00:09 +00:00
|
|
|
ctype.get()[j] = var->integer() && mip_ ? GRB_INTEGER : GRB_CONTINUOUS;
|
2012-10-28 09:07:15 +00:00
|
|
|
if (!var->name().empty()) {
|
2013-01-10 17:00:09 +00:00
|
|
|
colname[j] = var->name().c_str();
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
obj_coefs[j] = solver_->objective_->GetCoefficient(var);
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBaddvars(
|
|
|
|
|
model_, num_new_variables, 0, NULL, NULL, NULL, obj_coefs.get(),
|
|
|
|
|
lb.get(), ub.get(), ctype.get(), const_cast<char**>(colname.get())));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
2013-01-10 17:00:09 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBupdatemodel(model_));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::ExtractNewConstraints() {
|
|
|
|
|
CHECK(last_variable_index_ == 0 ||
|
|
|
|
|
last_variable_index_ == solver_->variables_.size());
|
|
|
|
|
CHECK(last_constraint_index_ == 0 ||
|
|
|
|
|
last_constraint_index_ == solver_->constraints_.size());
|
2012-10-28 09:07:15 +00:00
|
|
|
int total_num_rows = solver_->constraints_.size();
|
|
|
|
|
if (last_constraint_index_ < total_num_rows) {
|
|
|
|
|
// Find the length of the longest row.
|
|
|
|
|
int max_row_length = 0;
|
2013-01-10 17:00:09 +00:00
|
|
|
for (int row = last_constraint_index_; row < total_num_rows; ++row) {
|
|
|
|
|
MPConstraint* const ct = solver_->constraints_[row];
|
2015-06-18 14:08:50 +02:00
|
|
|
CHECK(!constraint_is_extracted(row));
|
|
|
|
|
set_constraint_as_extracted(row, true);
|
2012-10-28 09:07:15 +00:00
|
|
|
if (ct->coefficients_.size() > max_row_length) {
|
|
|
|
|
max_row_length = ct->coefficients_.size();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max_row_length = std::max(1, max_row_length);
|
2013-12-05 09:24:58 +00:00
|
|
|
std::unique_ptr<int[]> col_indices(new int[max_row_length]);
|
|
|
|
|
std::unique_ptr<double[]> coefs(new double[max_row_length]);
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// Add each new constraint.
|
2013-01-10 17:00:09 +00:00
|
|
|
for (int row = last_constraint_index_; row < total_num_rows; ++row) {
|
|
|
|
|
MPConstraint* const ct = solver_->constraints_[row];
|
2015-06-18 14:08:50 +02:00
|
|
|
CHECK(constraint_is_extracted(row));
|
2012-10-28 09:07:15 +00:00
|
|
|
const int size = ct->coefficients_.size();
|
2013-01-10 17:00:09 +00:00
|
|
|
int col = 0;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (CoeffEntry entry : ct->coefficients_) {
|
2015-06-18 14:08:50 +02:00
|
|
|
const int var_index = entry.first->index();
|
|
|
|
|
CHECK(variable_is_extracted(var_index));
|
|
|
|
|
col_indices[col] = var_index;
|
2013-10-10 15:23:20 +00:00
|
|
|
coefs[col] = entry.second;
|
2013-01-10 17:00:09 +00:00
|
|
|
col++;
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
char* const name =
|
|
|
|
|
ct->name().empty() ? NULL : const_cast<char*>(ct->name().c_str());
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBaddrangeconstr(model_, size, col_indices.get(),
|
|
|
|
|
coefs.get(), ct->lb(), ct->ub(),
|
2013-01-10 17:00:09 +00:00
|
|
|
name));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-01-10 17:00:09 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBupdatemodel(model_));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::ExtractObjective() {
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintattr(model_, GRB_INT_ATTR_MODELSENSE, maximize_ ? -1 : 1));
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBsetdblattr(model_, GRB_DBL_ATTR_OBJCON,
|
2013-01-10 17:00:09 +00:00
|
|
|
solver_->Objective().offset()));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------ Parameters -----
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetParameters(const MPSolverParameters& param) {
|
2012-10-28 09:07:15 +00:00
|
|
|
SetCommonParameters(param);
|
|
|
|
|
if (mip_) {
|
|
|
|
|
SetMIPParameters(param);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetRelativeMipGap(double value) {
|
2012-10-28 09:07:15 +00:00
|
|
|
if (mip_) {
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_MIPGAP, value));
|
2012-10-28 09:07:15 +00:00
|
|
|
} else {
|
|
|
|
|
LOG(WARNING) << "The relative MIP gap is only available "
|
|
|
|
|
<< "for discrete problems.";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetPrimalTolerance(double value) {
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_FEASIBILITYTOL, value));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetDualTolerance(double value) {
|
|
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_OPTIMALITYTOL, value));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetPresolveMode(int value) {
|
|
|
|
|
switch (value) {
|
|
|
|
|
case MPSolverParameters::PRESOLVE_OFF: {
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, false));
|
2013-01-10 17:00:09 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case MPSolverParameters::PRESOLVE_ON: {
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, true));
|
2013-01-10 17:00:09 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sets the scaling mode.
|
2013-01-10 17:00:09 +00:00
|
|
|
void GurobiInterface::SetScalingMode(int value) {
|
|
|
|
|
switch (value) {
|
|
|
|
|
case MPSolverParameters::SCALING_OFF:
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_SCALEFLAG, false));
|
2013-01-10 17:00:09 +00:00
|
|
|
break;
|
|
|
|
|
case MPSolverParameters::SCALING_ON:
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_SCALEFLAG, true));
|
|
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_OBJSCALE, 0.0));
|
2013-01-10 17:00:09 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Leave the parameters untouched.
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
// Sets the LP algorithm : primal, dual or barrier. Note that GRB
|
|
|
|
|
// offers automatic selection
|
|
|
|
|
void GurobiInterface::SetLpAlgorithm(int value) {
|
2012-10-28 09:07:15 +00:00
|
|
|
switch (value) {
|
|
|
|
|
case MPSolverParameters::DUAL:
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
|
2013-01-10 17:00:09 +00:00
|
|
|
GRB_METHOD_DUAL));
|
2012-10-28 09:07:15 +00:00
|
|
|
break;
|
|
|
|
|
case MPSolverParameters::PRIMAL:
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
|
2013-01-10 17:00:09 +00:00
|
|
|
GRB_METHOD_PRIMAL));
|
2012-10-28 09:07:15 +00:00
|
|
|
break;
|
|
|
|
|
case MPSolverParameters::BARRIER:
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
|
2013-01-10 17:00:09 +00:00
|
|
|
GRB_METHOD_BARRIER));
|
2012-10-28 09:07:15 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
SetIntegerParamToUnsupportedValue(MPSolverParameters::LP_ALGORITHM,
|
|
|
|
|
value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
2012-10-28 09:07:15 +00:00
|
|
|
WallTimer timer;
|
|
|
|
|
timer.Start();
|
|
|
|
|
|
|
|
|
|
if (param.GetIntegerParam(MPSolverParameters::INCREMENTALITY) ==
|
|
|
|
|
MPSolverParameters::INCREMENTALITY_OFF) {
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
// TODO(user,user): Support incrementality.
|
|
|
|
|
if (sync_status_ == MUST_RELOAD) {
|
|
|
|
|
Reset();
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
// Set log level.
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_OUTPUTFLAG, !quiet_));
|
2013-01-10 17:00:09 +00:00
|
|
|
|
2012-10-28 09:07:15 +00:00
|
|
|
ExtractModel();
|
2013-01-10 17:00:09 +00:00
|
|
|
// Sync solver.
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBupdatemodel(model_));
|
2015-05-18 19:41:34 +02:00
|
|
|
VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get());
|
2013-01-10 17:00:09 +00:00
|
|
|
|
2015-05-18 19:41:34 +02:00
|
|
|
// Set solution hints if any.
|
|
|
|
|
for (const std::pair<MPVariable*, double>& p : solver_->solution_hint_) {
|
|
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBsetdblattrelement(model_, "Start", p.first->index(), p.second));
|
|
|
|
|
}
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
// Time limit.
|
2013-10-10 15:23:20 +00:00
|
|
|
if (solver_->time_limit() != 0) {
|
2012-10-28 09:07:15 +00:00
|
|
|
VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_TIMELIMIT,
|
2013-10-10 15:23:20 +00:00
|
|
|
solver_->time_limit_in_secs()));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-11 14:49:45 +00:00
|
|
|
solver_->SetSolverSpecificParametersAsString(
|
|
|
|
|
solver_->solver_specific_parameter_string_);
|
2013-01-10 17:00:09 +00:00
|
|
|
SetParameters(param);
|
|
|
|
|
|
2012-10-28 09:07:15 +00:00
|
|
|
// Solve
|
|
|
|
|
timer.Restart();
|
2013-01-10 17:00:09 +00:00
|
|
|
const int status = GRBoptimize(model_);
|
2012-10-28 09:07:15 +00:00
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
|
VLOG(1) << "Failed to optimize MIP." << GRBgeterrormsg(env_);
|
|
|
|
|
} else {
|
|
|
|
|
VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get());
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 14:49:45 +00:00
|
|
|
// Get the status.
|
2012-10-28 09:07:15 +00:00
|
|
|
int optimization_status = 0;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBgetintattr(model_, GRB_INT_ATTR_STATUS, &optimization_status));
|
2013-06-11 14:49:45 +00:00
|
|
|
VLOG(1) << StringPrintf("Solution status %d.\n", optimization_status);
|
|
|
|
|
int solution_count = 0;
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBgetintattr(model_, GRB_INT_ATTR_SOLCOUNT, &solution_count));
|
2013-06-11 14:49:45 +00:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-17 19:04:26 +00:00
|
|
|
if (solution_count > 0 && (result_status_ == MPSolver::FEASIBLE ||
|
|
|
|
|
result_status_ == MPSolver::OPTIMAL)) {
|
2013-06-11 14:49:45 +00:00
|
|
|
// Get the results.
|
|
|
|
|
const int total_num_rows = solver_->constraints_.size();
|
|
|
|
|
const int total_num_cols = solver_->variables_.size();
|
|
|
|
|
|
2013-12-05 09:24:58 +00:00
|
|
|
std::unique_ptr<double[]> values(new double[total_num_cols]);
|
|
|
|
|
std::unique_ptr<double[]> dual_values(new double[total_num_rows]);
|
|
|
|
|
std::unique_ptr<double[]> reduced_costs(new double[total_num_cols]);
|
2013-06-11 14:49:45 +00:00
|
|
|
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(
|
|
|
|
|
GRBgetdblattr(model_, GRB_DBL_ATTR_OBJVAL, &objective_value_));
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattrarray(model_, GRB_DBL_ATTR_X, 0,
|
|
|
|
|
total_num_cols, values.get()));
|
2012-10-28 09:07:15 +00:00
|
|
|
if (!mip_) {
|
2014-01-08 12:01:58 +00:00
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattrarray(
|
|
|
|
|
model_, GRB_DBL_ATTR_RC, 0, total_num_cols, reduced_costs.get()));
|
|
|
|
|
CHECKED_GUROBI_CALL(GRBgetdblattrarray(
|
|
|
|
|
model_, GRB_DBL_ATTR_PI, 0, total_num_rows, dual_values.get()));
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VLOG(1) << "objective = " << objective_value_;
|
2012-10-28 14:50:06 +00:00
|
|
|
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
2012-10-28 09:07:15 +00:00
|
|
|
MPVariable* const var = solver_->variables_[i];
|
|
|
|
|
var->set_solution_value(values[i]);
|
2013-01-10 17:00:09 +00:00
|
|
|
VLOG(3) << var->name() << ", value = " << values[i];
|
2012-10-28 09:07:15 +00:00
|
|
|
if (!mip_) {
|
2013-01-10 17:00:09 +00:00
|
|
|
var->set_reduced_cost(reduced_costs[i]);
|
|
|
|
|
VLOG(4) << var->name() << ", reduced cost = " << reduced_costs[i];
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-06-11 14:49:45 +00:00
|
|
|
|
2015-06-18 14:26:12 +02:00
|
|
|
if (!mip_) {
|
|
|
|
|
for (int i = 0; i < solver_->constraints_.size(); ++i) {
|
|
|
|
|
MPConstraint* const ct = solver_->constraints_[i];
|
2013-01-10 17:00:09 +00:00
|
|
|
ct->set_dual_value(dual_values[i]);
|
2015-06-18 14:26:12 +02:00
|
|
|
VLOG(4) << "row " << ct->index() << ", dual value = " << dual_values[i];
|
2012-10-28 09:07:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sync_status_ = SOLUTION_SYNCHRONIZED;
|
2013-01-10 17:00:09 +00:00
|
|
|
GRBresetparams(GRBgetenv(model_));
|
2012-10-28 09:07:15 +00:00
|
|
|
return result_status_;
|
|
|
|
|
}
|
2013-01-10 17:00:09 +00:00
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
bool GurobiInterface::ReadParameterFile(const std::string& filename) {
|
2013-06-11 14:49:45 +00:00
|
|
|
// A non-zero return value indicates that a problem occurred.
|
|
|
|
|
return GRBreadparams(GRBgetenv(model_), filename.c_str()) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string GurobiInterface::ValidFileExtensionForParameterFile() const {
|
2013-06-11 14:49:45 +00:00
|
|
|
return ".prm";
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-04 09:13:47 +00:00
|
|
|
MPSolverInterface* BuildGurobiInterface(bool mip, MPSolver* const solver) {
|
2013-01-10 17:00:09 +00:00
|
|
|
return new GurobiInterface(solver, mip);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 14:49:45 +00:00
|
|
|
#undef CHECKED_GUROBI_CALL
|
|
|
|
|
|
2013-01-10 17:00:09 +00:00
|
|
|
|
2012-10-28 09:07:15 +00:00
|
|
|
} // namespace operations_research
|
2013-01-10 17:00:09 +00:00
|
|
|
#endif // #if defined(USE_GUROBI)
|