Browse Source

Added function tests for both glpk- and Gurobi-based LP solver implementations. Found and fixed some bugs while doing this.

Former-commit-id: 99e58097f7
tempestpy_adaptions
dehnert 11 years ago
parent
commit
514aace4fd
  1. 154
      src/solver/GlpkLpSolver.cpp
  2. 26
      src/solver/GlpkLpSolver.h
  3. 87
      src/solver/GurobiLpSolver.cpp
  4. 19
      src/solver/GurobiLpSolver.h
  5. 11
      src/solver/LpSolver.h
  6. 103
      test/functional/solver/GlpkLpSolverTest.cpp
  7. 103
      test/functional/solver/GurobiLpSolverTest.cpp

154
src/solver/GlpkLpSolver.cpp

@ -15,12 +15,14 @@ extern log4cplus::Logger logger;
bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool {
instance->addOption(storm::settings::OptionBuilder("GlpkLpSolver", "glpkoutput", "", "If set, the glpk output will be printed to the command line.").build());
instance->addOption(storm::settings::OptionBuilder("GurobiLpSolver", "glpkinttol", "", "Sets glpk's precision for integer variables.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build());
return true;
});
namespace storm {
namespace solver {
GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), rowIndices(), columnIndices(), coefficientValues() {
GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() {
// Create the LP problem for glpk.
lp = glp_create_prob();
@ -44,6 +46,10 @@ namespace storm {
// Intentionally left empty.
}
GlpkLpSolver::GlpkLpSolver(ModelSense const& modelSense) : GlpkLpSolver("", modelSense) {
// Intentionally left empty.
}
GlpkLpSolver::~GlpkLpSolver() {
// Dispose of all objects allocated dynamically by glpk.
glp_delete_prob(this->lp);
@ -78,12 +84,14 @@ namespace storm {
uint_fast64_t GlpkLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
uint_fast64_t index = this->createContinuousVariable(name, variableType, lowerBound, upperBound, objectiveFunctionCoefficient);
glp_set_col_kind(this->lp, index, GLP_IV);
this->modelContainsIntegerVariables = true;
return index;
}
uint_fast64_t GlpkLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) {
uint_fast64_t index = this->createContinuousVariable(name, UNBOUNDED, 0, 1, objectiveFunctionCoefficient);
glp_set_col_kind(this->lp, index, GLP_BV);
this->modelContainsIntegerVariables = true;
return index;
}
@ -128,11 +136,36 @@ namespace storm {
}
void GlpkLpSolver::optimize() const {
// First, reset the flags.
this->isInfeasibleFlag = false;
this->isUnboundedFlag = false;
// Start by setting the model sense.
glp_set_obj_dir(this->lp, this->getModelSense() == MINIMIZE ? GLP_MIN : GLP_MAX);
glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data());
int error = glp_simplex(this->lp, nullptr);
int error = 0;
if (this->modelContainsIntegerVariables) {
glp_iocp* parameters = new glp_iocp;
glp_init_iocp(parameters);
parameters->presolve = GLP_ON;
parameters->tol_int = storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble();
error = glp_intopt(this->lp, parameters);
delete parameters;
// In case the error is caused by an infeasible problem, we do not want to view this as an error and
// reset the error code.
if (error == GLP_ENOPFS) {
this->isInfeasibleFlag = true;
error = 0;
} else if (error == GLP_ENODFS) {
this->isUnboundedFlag = true;
error = 0;
}
} else {
error = glp_simplex(this->lp, nullptr);
}
if (error != 0) {
LOG4CPLUS_ERROR(logger, "Unable to optimize glpk model (" << error << ").");
@ -143,26 +176,64 @@ namespace storm {
}
bool GlpkLpSolver::isInfeasible() const {
int status = glp_get_status(this->lp);
return status == GLP_INFEAS;
if (!this->currentModelHasBeenOptimized) {
throw storm::exceptions::InvalidStateException() << "Illegal call to GlpkLpSolver::isInfeasible: model has not been optimized.";
}
if (this->modelContainsIntegerVariables) {
return isInfeasibleFlag;
} else {
return glp_get_status(this->lp) == GLP_INFEAS;
}
}
bool GlpkLpSolver::isUnbounded() const {
int status = glp_get_status(this->lp);
return status == GLP_UNBND;
if (!this->currentModelHasBeenOptimized) {
throw storm::exceptions::InvalidStateException() << "Illegal call to GlpkLpSolver::isUnbounded: model has not been optimized.";
}
if (this->modelContainsIntegerVariables) {
return isUnboundedFlag;
} else {
return glp_get_status(this->lp) == GLP_UNBND;
}
}
bool GlpkLpSolver::isOptimal() const {
if (!this->currentModelHasBeenOptimized) {
return false;
}
int status = glp_get_status(this->lp);
int status = 0;
if (this->modelContainsIntegerVariables) {
status = glp_mip_status(this->lp);
} else {
status = glp_get_status(this->lp);
}
return status == GLP_OPT;
}
int_fast64_t GlpkLpSolver::getIntegerValue(uint_fast64_t variableIndex) const {
double value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
if (!this->isOptimal()) {
if (this->isInfeasible()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
} else if (this->isUnbounded()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
} else {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
}
}
double value = 0;
if (this->modelContainsIntegerVariables) {
value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex));
} else {
value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
}
if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
// Nothing to do in this case.
} else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
@ -174,7 +245,25 @@ namespace storm {
}
bool GlpkLpSolver::getBinaryValue(uint_fast64_t variableIndex) const {
double value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
if (!this->isOptimal()) {
if (this->isInfeasible()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
} else if (this->isUnbounded()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
} else {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
}
}
double value = 0;
if (this->modelContainsIntegerVariables) {
value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex));
} else {
value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
}
if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
// Nothing to do in this case.
@ -187,7 +276,50 @@ namespace storm {
}
double GlpkLpSolver::getContinuousValue(uint_fast64_t variableIndex) const {
return glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
if (!this->isOptimal()) {
if (this->isInfeasible()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
} else if (this->isUnbounded()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
} else {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
}
}
double value = 0;
if (this->modelContainsIntegerVariables) {
value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex));
} else {
value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
}
return value;
}
double GlpkLpSolver::getObjectiveValue() const {
if (!this->isOptimal()) {
if (this->isInfeasible()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
} else if (this->isUnbounded()) {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
} else {
LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
}
}
double value = 0;
if (this->modelContainsIntegerVariables) {
value = glp_mip_obj_val(this->lp);
} else {
value = glp_get_obj_val(this->lp);
}
return value;
}
void GlpkLpSolver::writeModelToFile(std::string const& filename) const {

26
src/solver/GlpkLpSolver.h

@ -37,6 +37,14 @@ namespace storm {
*/
GlpkLpSolver(std::string const& name);
/*!
* Constructs a solver without a name and the given model sense.
*
* @param modelSense A value indicating whether the value of the objective function is to be minimized or
* maximized.
*/
GlpkLpSolver(ModelSense const& modelSense);
/*!
* Constructs a solver without a name. By default the objective function is assumed to be minimized,
* but this may be altered later using a call to setModelSense.
@ -62,7 +70,8 @@ namespace storm {
virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override;
virtual bool getBinaryValue(uint_fast64_t variableIndex) const override;
virtual double getContinuousValue(uint_fast64_t variableIndex) const override;
virtual double getObjectiveValue() const override;
virtual void writeModelToFile(std::string const& filename) const override;
private:
@ -75,6 +84,13 @@ namespace storm {
// A counter that keeps track of the next free constraint index.
uint_fast64_t nextConstraintIndex;
// A flag storing whether the model is an LP or an MILP.
bool modelContainsIntegerVariables;
// Flags that store whether the MILP was found to be infeasible or unbounded.
mutable bool isInfeasibleFlag;
mutable bool isUnboundedFlag;
// The arrays that store the coefficient matrix of the problem.
std::vector<int> rowIndices;
std::vector<int> columnIndices;
@ -92,6 +108,10 @@ namespace storm {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
GlpkLpSolver(ModelSense const& modelSense) : LpSolver(modelSense) {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
GlpkLpSolver() : LpSolver(MINIMIZE) {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
@ -144,6 +164,10 @@ namespace storm {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual double getObjectiveValue() const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual void writeModelToFile(std::string const& filename) const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}

87
src/solver/GurobiLpSolver.cpp

@ -46,6 +46,10 @@ namespace storm {
// Intentionally left empty.
}
GurobiLpSolver::GurobiLpSolver(ModelSense const& modelSense) : GurobiLpSolver("", modelSense) {
// Intentionally left empty.
}
GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", MINIMIZE) {
// Intentionally left empty.
}
@ -64,7 +68,7 @@ namespace storm {
error = GRBsetintparam(env, "Threads", storm::settings::Settings::getInstance()->getOptionByLongName("gurobithreads").getArgument(0).getValueAsUnsignedInteger());
// Enable the following line to force Gurobi to be as precise about the binary variables as required by the given precision option.
error = GRBsetdblparam(env, "IntFeasTol", storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
error = GRBsetdblparam(env, "IntFeasTol", storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble());
}
void GurobiLpSolver::updateModel() const {
@ -198,6 +202,10 @@ namespace storm {
}
bool GurobiLpSolver::isInfeasible() const {
if (!this->currentModelHasBeenOptimized) {
throw storm::exceptions::InvalidStateException() << "Illegal call to GurobiLpSolver::isInfeasible: model has not been optimized.";
}
int optimalityStatus = 0;
int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
@ -206,10 +214,39 @@ namespace storm {
throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ").";
}
// By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one
// it is, we need to perform an extra step.
if (optimalityStatus == GRB_INF_OR_UNBD) {
std::cout << "here" << std::endl;
error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").";
}
this->optimize();
error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ").";
}
error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").";
}
}
return optimalityStatus == GRB_INFEASIBLE;
}
bool GurobiLpSolver::isUnbounded() const {
if (!this->currentModelHasBeenOptimized) {
throw storm::exceptions::InvalidStateException() << "Illegal call to GurobiLpSolver::isUnbounded: model has not been optimized.";
}
int optimalityStatus = 0;
int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
@ -218,6 +255,30 @@ namespace storm {
throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ").";
}
// By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one
// it is, we need to perform an extra step.
if (optimalityStatus == GRB_INF_OR_UNBD) {
error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").";
}
this->optimize();
error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ").";
}
error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ").";
}
}
return optimalityStatus == GRB_UNBOUNDED;
}
@ -322,6 +383,30 @@ namespace storm {
return value;
}
double GurobiLpSolver::getObjectiveValue() const {
if (!this->isOptimal()) {
if (this->isInfeasible()) {
LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").";
} else if (this->isUnbounded()) {
LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").";
} else {
LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").";
}
}
double value = 0;
int error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &value);
if (error) {
LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ").");
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ").";
}
return value;
}
void GurobiLpSolver::writeModelToFile(std::string const& filename) const {
int error = GRBwrite(model, filename.c_str());
if (error) {

19
src/solver/GurobiLpSolver.h

@ -41,6 +41,14 @@ namespace storm {
*/
GurobiLpSolver(std::string const& name);
/*!
* Constructs a solver without a name and the given model sense.
*
* @param modelSense A value indicating whether the value of the objective function is to be minimized or
* maximized.
*/
GurobiLpSolver(ModelSense const& modelSense);
/*!
* Constructs a solver without a name. By default the objective function is assumed to be minimized,
* but this may be altered later using a call to setModelSense.
@ -66,7 +74,8 @@ namespace storm {
virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override;
virtual bool getBinaryValue(uint_fast64_t variableIndex) const override;
virtual double getContinuousValue(uint_fast64_t variableIndex) const override;
virtual double getObjectiveValue() const override;
virtual void writeModelToFile(std::string const& filename) const override;
private:
@ -101,6 +110,10 @@ namespace storm {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
}
GurobiLpSolver(ModelSense const& modelSense) {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
}
GurobiLpSolver() {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
}
@ -153,6 +166,10 @@ namespace storm {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
}
virtual double getObjectiveValue() const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
}
virtual void writeModelToFile(std::string const& filename) const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
}

11
src/solver/LpSolver.h

@ -152,6 +152,7 @@ namespace storm {
*
* @param variableIndex The index of the integer variable whose value to query. If this index does not
* belong to a previously declared integer variable, the behaviour is undefined.
* @return The value of the integer variable in the optimal solution.
*/
virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const = 0;
@ -161,6 +162,7 @@ namespace storm {
*
* @param variableIndex The index of the binary variable whose value to query. If this index does not
* belong to a previously declared binary variable, the behaviour is undefined.
* @return The value of the binary variable in the optimal solution.
*/
virtual bool getBinaryValue(uint_fast64_t variableIndex) const = 0;
@ -170,9 +172,18 @@ namespace storm {
*
* @param variableIndex The index of the continuous variable whose value to query. If this index does not
* belong to a previously declared continuous variable, the behaviour is undefined.
* @return The value of the continuous variable in the optimal solution.
*/
virtual double getContinuousValue(uint_fast64_t variableIndex) const = 0;
/*!
* Retrieves the value of the objective function. Note that this may only be called, if the model was found
* to be optimal, i.e. iff isOptimal() returns true.
*
* @return The value of the objective function in the optimal solution.
*/
virtual double getObjectiveValue() const = 0;
/*!
* Writes the current LP problem to the given file.
*

103
test/functional/solver/GlpkLpSolverTest.cpp

@ -0,0 +1,103 @@
#include "gtest/gtest.h"
#include "storm-config.h"
#include "src/solver/GlpkLpSolver.h"
#include "src/exceptions/InvalidStateException.h"
#include "src/settings/Settings.h"
TEST(GlpkLpSolver, Optimize) {
#ifdef STORM_HAVE_GLPK
storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
uint_fast64_t xIndex;
ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
uint_fast64_t yIndex;
ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
uint_fast64_t zIndex;
ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
ASSERT_NO_THROW(solver.optimize());
ASSERT_TRUE(solver.isOptimal());
ASSERT_FALSE(solver.isUnbounded());
ASSERT_FALSE(solver.isInfeasible());
bool xValue = false;
ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
ASSERT_EQ(true, xValue);
int_fast64_t yValue = 0;
ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
ASSERT_EQ(6, yValue);
double zValue = 0;
ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
double objectiveValue = 0;
ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
ASSERT_LT(std::abs(objectiveValue - 14), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
#else
ASSERT_TRUE(false, "StoRM built without Gurobi support.");
#endif
}
TEST(GlpkLpSolver, Infeasible) {
#ifdef STORM_HAVE_GLPK
storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
uint_fast64_t xIndex;
ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
uint_fast64_t yIndex;
ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
uint_fast64_t zIndex;
ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
ASSERT_NO_THROW(solver.optimize());
ASSERT_FALSE(solver.isOptimal());
ASSERT_FALSE(solver.isUnbounded());
ASSERT_TRUE(solver.isInfeasible());
bool xValue = false;
ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
int_fast64_t yValue = 0;
ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
double zValue = 0;
ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
double objectiveValue = 0;
ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
#else
ASSERT_TRUE(false, "StoRM built without Gurobi support.");
#endif
}
TEST(GlpkLpSolver, Unbounded) {
#ifdef STORM_HAVE_GLPK
storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
uint_fast64_t xIndex;
ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
uint_fast64_t yIndex;
ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
uint_fast64_t zIndex;
ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
ASSERT_NO_THROW(solver.optimize());
ASSERT_FALSE(solver.isOptimal());
ASSERT_TRUE(solver.isUnbounded());
ASSERT_FALSE(solver.isInfeasible());
bool xValue = false;
ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
int_fast64_t yValue = 0;
ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
double zValue = 0;
ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
double objectiveValue = 0;
ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
#else
ASSERT_TRUE(false, "StoRM built without Gurobi support.");
#endif
}

103
test/functional/solver/GurobiLpSolverTest.cpp

@ -0,0 +1,103 @@
#include "gtest/gtest.h"
#include "storm-config.h"
#include "src/solver/GurobiLpSolver.h"
#include "src/exceptions/InvalidStateException.h"
#include "src/settings/Settings.h"
TEST(GurobiLpSolver, Optimize) {
#ifdef STORM_HAVE_GUROBI
storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
uint_fast64_t xIndex;
ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
uint_fast64_t yIndex;
ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
uint_fast64_t zIndex;
ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
ASSERT_NO_THROW(solver.optimize());
ASSERT_TRUE(solver.isOptimal());
ASSERT_FALSE(solver.isUnbounded());
ASSERT_FALSE(solver.isInfeasible());
bool xValue = false;
ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
ASSERT_EQ(true, xValue);
int_fast64_t yValue = 0;
ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
ASSERT_EQ(6, yValue);
double zValue = 0;
ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
double objectiveValue = 0;
ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
ASSERT_LT(std::abs(objectiveValue - 14), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
#else
ASSERT_TRUE(false, "StoRM built without Gurobi support.");
#endif
}
TEST(GurobiLpSolver, Infeasible) {
#ifdef STORM_HAVE_GUROBI
storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
uint_fast64_t xIndex;
ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
uint_fast64_t yIndex;
ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
uint_fast64_t zIndex;
ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
ASSERT_NO_THROW(solver.optimize());
ASSERT_FALSE(solver.isOptimal());
ASSERT_FALSE(solver.isUnbounded());
ASSERT_TRUE(solver.isInfeasible());
bool xValue = false;
ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
int_fast64_t yValue = 0;
ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
double zValue = 0;
ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
double objectiveValue = 0;
ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
#else
ASSERT_TRUE(false, "StoRM built without Gurobi support.");
#endif
}
TEST(GurobiLpSolver, Unbounded) {
#ifdef STORM_HAVE_GUROBI
storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
uint_fast64_t xIndex;
ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
uint_fast64_t yIndex;
ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
uint_fast64_t zIndex;
ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
ASSERT_NO_THROW(solver.optimize());
ASSERT_FALSE(solver.isOptimal());
ASSERT_TRUE(solver.isUnbounded());
ASSERT_FALSE(solver.isInfeasible());
bool xValue = false;
ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
int_fast64_t yValue = 0;
ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
double zValue = 0;
ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
double objectiveValue = 0;
ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
#else
ASSERT_TRUE(false, "StoRM built without Gurobi support.");
#endif
}
Loading…
Cancel
Save