25template<
typename ValueType,
bool RawMode>
30 modelContainsIntegerVariables(false),
31 isInfeasibleFlag(false),
32 isUnboundedFlag(false) {
34 lp = glp_create_prob();
37 glp_set_prob_name(lp, name.c_str());
40 glp_term_out(storm::settings::getModule<storm::settings::modules::DebugSettings>().isDebugSet() ||
41 storm::settings::getModule<storm::settings::modules::GlpkSettings>().isOutputSet()
46 glp_iocp* defaultParameters =
new glp_iocp();
47 glp_init_iocp(defaultParameters);
48 this->maxMILPGap = defaultParameters->mip_gap;
49 this->maxMILPGapRelative =
true;
54template<
typename ValueType,
bool RawMode>
60template<
typename ValueType,
bool RawMode>
65template<
typename ValueType,
bool RawMode>
70template<
typename ValueType,
bool RawMode>
75template<
typename ValueType,
bool RawMode>
79 glp_delete_prob(this->lp);
84template<
typename ValueType,
bool RawMode>
98 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
99 "requires this support. Please choose a version with GLPK support.";
103template<
typename ValueType,
bool RawMode>
105 std::optional<ValueType>
const& lowerBound,
106 std::optional<ValueType>
const& upperBound,
107 ValueType objectiveFunctionCoefficient) {
108#ifdef STORM_HAVE_GLPK
110 if constexpr (RawMode) {
111 resultVar = variableToIndexMap.size();
113 resultVar = this->declareOrGetExpressionVariable(name, type);
116 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0,
"Variable " << resultVar.getName() <<
" exists already in the model.");
120 if (lowerBound.has_value()) {
121 boundType = upperBound.has_value() ? GLP_DB : GLP_LO;
123 boundType = upperBound.has_value() ? GLP_UP : GLP_FR;
126 if (type == VariableType::Integer || type == VariableType::Binary) {
127 this->modelContainsIntegerVariables =
true;
131 int variableIndex = glp_add_cols(this->lp, 1);
132 glp_set_col_name(this->lp, variableIndex, name.c_str());
133 glp_set_col_bnds(lp, variableIndex, boundType, lowerBound.has_value() ? storm::utility::convertNumber<double>(*lowerBound) : 0.0,
134 upperBound.has_value() ? storm::utility::convertNumber<double>(*upperBound) : 0.0);
135 glp_set_col_kind(this->lp, variableIndex, getGlpkType<ValueType, RawMode>(type));
136 glp_set_obj_coef(this->lp, variableIndex, storm::utility::convertNumber<double>(objectiveFunctionCoefficient));
138 if constexpr (RawMode) {
139 this->variableToIndexMap.push_back(variableIndex);
141 this->variableToIndexMap.emplace(resultVar, variableIndex);
142 if (!incrementalData.empty()) {
143 incrementalData.back().variables.push_back(resultVar);
149 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
150 "requires this support. Please choose a version with GLPK support.";
154template<
typename ValueType,
bool RawMode>
159template<
typename ValueType,
bool RawMode>
161#ifdef STORM_HAVE_GLPK
163 int constraintIndex = glp_add_rows(this->lp, 1);
164 glp_set_row_name(this->lp, constraintIndex, name.c_str());
170 std::vector<int> variableIndices(1, -1);
171 std::vector<double> coefficients(1, 0.0);
172 if constexpr (RawMode) {
173 rhs = storm::utility::convertNumber<double>(constraint.rhs);
174 relationType = constraint.relationType;
175 variableIndices.reserve(constraint.lhsVariableIndices.size() + 1);
176 for (
auto const& var : constraint.lhsVariableIndices) {
177 variableIndices.push_back(this->variableToIndexMap.at(var));
179 coefficients.reserve(constraint.lhsCoefficients.size() + 1);
180 for (
auto const& coef : constraint.lhsCoefficients) {
181 coefficients.push_back(storm::utility::convertNumber<double>(coef));
184 STORM_LOG_THROW(constraint.getManager() == this->getManager(), storm::exceptions::InvalidArgumentException,
185 "Constraint was not built over the proper variables.");
186 STORM_LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException,
"Illegal constraint is not a relational expression.");
194 relationType = constraint.getBaseExpression().asBinaryRelationExpression().getRelationType();
195 int len = std::distance(leftCoefficients.
begin(), leftCoefficients.
end());
196 variableIndices.reserve(len + 1);
197 coefficients.reserve(len + 1);
198 for (
auto const& variableCoefficientPair : leftCoefficients) {
199 auto variableIndexPair = this->variableToIndexMap.find(variableCoefficientPair.first);
200 variableIndices.push_back(variableIndexPair->second);
201 coefficients.push_back(variableCoefficientPair.second);
206 switch (relationType) {
208 glp_set_row_bnds(this->lp, constraintIndex, GLP_UP, 0,
209 rhs - storm::settings::getModule<storm::settings::modules::GlpkSettings>().getIntegerTolerance());
212 glp_set_row_bnds(this->lp, constraintIndex, GLP_UP, 0, rhs);
215 glp_set_row_bnds(this->lp, constraintIndex, GLP_LO,
216 rhs + storm::settings::getModule<storm::settings::modules::GlpkSettings>().getIntegerTolerance(), 0);
219 glp_set_row_bnds(this->lp, constraintIndex, GLP_LO, rhs, 0);
222 glp_set_row_bnds(this->lp, constraintIndex, GLP_FX, rhs, rhs);
229 glp_set_mat_row(this->lp, constraintIndex, variableIndices.size() - 1, variableIndices.data(), coefficients.data());
231 this->currentModelHasBeenOptimized =
false;
233 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
234 "requires this support. Please choose a version with GLPK support.";
238template<
typename ValueType,
bool RawMode>
240 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Indicator constraints are not supported for GLPK.");
243#ifdef STORM_HAVE_GLPK
245void callback(glp_tree* t,
void* info) {
246 auto& mipgap = *
static_cast<std::pair<double, bool>*
>(info);
247 double actualRelativeGap = glp_ios_mip_gap(t);
248 double factor = storm::utility::one<double>();
249 if (!mipgap.second) {
252 assert(factor >= 0.0);
254 if (actualRelativeGap * factor <= mipgap.first) {
256 mipgap.first = actualRelativeGap;
257 mipgap.second =
true;
258 glp_ios_terminate(t);
263template<
typename ValueType,
bool RawMode>
265#ifdef STORM_HAVE_GLPK
267 this->isInfeasibleFlag =
false;
268 this->isUnboundedFlag =
false;
271 glp_set_obj_dir(this->lp, this->getOptimizationDirection() == OptimizationDirection::Minimize ? GLP_MIN : GLP_MAX);
274 if (this->modelContainsIntegerVariables) {
275 glp_iocp* parameters =
new glp_iocp();
276 glp_init_iocp(parameters);
277 parameters->tol_int = storm::settings::getModule<storm::settings::modules::GlpkSettings>().getIntegerTolerance();
278 this->isInfeasibleFlag =
false;
279 if (storm::settings::getModule<storm::settings::modules::GlpkSettings>().isMILPPresolverEnabled()) {
280 parameters->presolve = GLP_ON;
284 error = glp_simplex(this->lp,
nullptr);
285 STORM_LOG_THROW(error == 0, storm::exceptions::InvalidStateException,
"Unable to optimize relaxed glpk model (" << error <<
").");
287 if (glp_get_status(this->lp) == GLP_INFEAS || glp_get_status(this->lp) == GLP_NOFEAS) {
288 this->isInfeasibleFlag =
true;
292 if (glp_get_status(this->lp) == GLP_UNBND) {
293 parameters->presolve = GLP_ON;
295 parameters->presolve = GLP_OFF;
298 if (!this->isInfeasibleFlag) {
301 std::pair<double, bool> mipgap(this->maxMILPGap, this->maxMILPGapRelative);
303 parameters->cb_func = &callback;
304 parameters->cb_info = &mipgap;
308 error = glp_intopt(this->lp, parameters);
309 int status = glp_mip_status(this->lp);
313 this->actualRelativeMILPGap = mipgap.first;
317 if (error == GLP_ENOPFS || status == GLP_NOFEAS) {
318 this->isInfeasibleFlag =
true;
320 }
else if (error == GLP_ENODFS) {
321 this->isUnboundedFlag =
true;
323 }
else if (error == GLP_ESTOP) {
326 }
else if (error == GLP_EBOUND) {
327 throw storm::exceptions::InvalidStateException()
328 <<
"The bounds of some variables are illegal. Note that glpk only accepts integer bounds for integer variables.";
332 error = glp_simplex(this->lp,
nullptr);
335 STORM_LOG_THROW(error == 0, storm::exceptions::InvalidStateException,
"Unable to optimize glpk model (" << error <<
").");
336 this->currentModelHasBeenOptimized =
true;
338 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
339 "requires this support. Please choose a version with GLPK support.";
343template<
typename ValueType,
bool RawMode>
345#ifdef STORM_HAVE_GLPK
346 if (!this->currentModelHasBeenOptimized) {
347 throw storm::exceptions::InvalidStateException() <<
"Illegal call to GlpkLpSolver::isInfeasible: model has not been optimized.";
350 if (this->modelContainsIntegerVariables) {
351 return isInfeasibleFlag;
353 return glp_get_status(this->lp) == GLP_INFEAS || glp_get_status(this->lp) == GLP_NOFEAS;
356 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
357 "requires this support. Please choose a version with GLPK support.";
361template<
typename ValueType,
bool RawMode>
363#ifdef STORM_HAVE_GLPK
364 if (!this->currentModelHasBeenOptimized) {
365 throw storm::exceptions::InvalidStateException() <<
"Illegal call to GlpkLpSolver::isUnbounded: model has not been optimized.";
368 if (this->modelContainsIntegerVariables) {
369 return isUnboundedFlag;
371 return glp_get_status(this->lp) == GLP_UNBND;
374 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
375 "requires this support. Please choose a version with GLPK support.";
379template<
typename ValueType,
bool RawMode>
381 if (!this->currentModelHasBeenOptimized) {
385 return !isInfeasible() && !isUnbounded();
388template<
typename ValueType,
bool RawMode>
390#ifdef STORM_HAVE_GLPK
391 if (!this->isOptimal()) {
392 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
393 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
394 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
397 int variableIndex = variableToIndexMap.at(variable);
400 if (this->modelContainsIntegerVariables) {
401 value = glp_mip_col_val(this->lp,
static_cast<int>(variableIndex));
403 value = glp_get_col_prim(this->lp,
static_cast<int>(variableIndex));
405 return storm::utility::convertNumber<ValueType>(value);
407 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
408 "requires this support. Please choose a version with GLPK support.";
412template<
typename ValueType,
bool RawMode>
414#ifdef STORM_HAVE_GLPK
415 if (!this->isOptimal()) {
416 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
417 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
418 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
421 int variableIndex = variableToIndexMap.at(variable);
424 if (this->modelContainsIntegerVariables) {
425 value = glp_mip_col_val(this->lp, variableIndex);
427 value = glp_get_col_prim(this->lp, variableIndex);
430 double roundedValue = std::round(value);
431 double diff = std::abs(roundedValue - value);
432 STORM_LOG_ERROR_COND(diff <= storm::settings::getModule<storm::settings::modules::GlpkSettings>().getIntegerTolerance(),
433 "Illegal value for integer variable in GLPK solution (" << value <<
"). Difference to nearest int is " << diff);
434 return static_cast<int_fast64_t
>(roundedValue);
436 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
437 "requires this support. Please choose a version with GLPK support.";
441template<
typename ValueType,
bool RawMode>
443#ifdef STORM_HAVE_GLPK
444 if (!this->isOptimal()) {
445 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
446 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
447 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
450 int variableIndex = variableToIndexMap.at(variable);
453 if (this->modelContainsIntegerVariables) {
454 value = glp_mip_col_val(this->lp, variableIndex);
456 value = glp_get_col_prim(this->lp, variableIndex);
460 STORM_LOG_ERROR_COND(std::abs(value - 1.0) <= storm::settings::getModule<storm::settings::modules::GlpkSettings>().getIntegerTolerance(),
461 "Illegal value for binary variable in GLPK solution (" << value <<
").");
464 STORM_LOG_ERROR_COND(std::abs(value) <= storm::settings::getModule<storm::settings::modules::GlpkSettings>().getIntegerTolerance(),
465 "Illegal value for binary variable in GLPK solution (" << value <<
").");
469 return static_cast<bool>(value);
471 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
472 "requires this support. Please choose a version with GLPK support.";
476template<
typename ValueType,
bool RawMode>
478#ifdef STORM_HAVE_GLPK
479 if (!this->isOptimal()) {
480 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
481 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
482 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
486 if (this->modelContainsIntegerVariables) {
487 value = glp_mip_obj_val(this->lp);
489 value = glp_get_obj_val(this->lp);
492 return storm::utility::convertNumber<ValueType>(value);
494 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
495 "requires this support. Please choose a version with GLPK support.";
499template<
typename ValueType,
bool RawMode>
501#ifdef STORM_HAVE_GLPK
502 glp_write_lp(this->lp, 0, filename.c_str());
504 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
505 "requires this support. Please choose a version with GLPK support.";
509template<
typename ValueType,
bool RawMode>
511#ifdef STORM_HAVE_GLPK
512 if constexpr (RawMode) {
513 STORM_LOG_THROW(
false, storm::exceptions::InvalidOperationException,
"Incremental LP solving not supported in raw mode");
515 IncrementalLevel lvl;
516 lvl.firstConstraintIndex = glp_get_num_rows(this->lp) + 1;
517 incrementalData.push_back(lvl);
520 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
521 "requires this support. Please choose a version with GLPK support.";
525template<
typename ValueType,
bool RawMode>
527#ifdef STORM_HAVE_GLPK
528 if constexpr (RawMode) {
529 STORM_LOG_THROW(
false, storm::exceptions::InvalidOperationException,
"Incremental LP solving not supported in raw mode");
531 if (incrementalData.empty()) {
534 IncrementalLevel
const& lvl = incrementalData.back();
537 if (indicesToBeRemoved.size() > 1) {
538 glp_del_rows(this->lp, indicesToBeRemoved.size() - 1, indicesToBeRemoved.data());
540 indicesToBeRemoved.clear();
542 if (!lvl.variables.empty()) {
545 for (
auto const& var : lvl.variables) {
547 auto it = variableToIndexMap.find(var);
548 firstIndex = it->second;
549 variableToIndexMap.erase(it);
552 variableToIndexMap.erase(var);
557 glp_del_cols(this->lp, indicesToBeRemoved.size() - 1, indicesToBeRemoved.data());
559 incrementalData.pop_back();
562 int n = glp_get_num_rows(lp);
563 int m = glp_get_num_cols(lp);
565 for (
int i = 1; i <= n; ++i) {
566 if (glp_get_row_stat(lp, i) == GLP_BS) {
570 for (
int j = 1; j <= m; ++j) {
571 if (glp_get_col_stat(lp, j) == GLP_BS) {
575 if (n != (nb + mb)) {
576 glp_std_basis(this->lp);
581 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
582 "requires this support. Please choose a version with GLPK support.";
586template<
typename ValueType,
bool RawMode>
588 this->maxMILPGap = storm::utility::convertNumber<double>(gap);
589 this->maxMILPGapRelative = relative;
592template<
typename ValueType,
bool RawMode>
594 STORM_LOG_ASSERT(this->isOptimal(),
"Asked for the MILP gap although there is no (bounded) solution.");
596 return storm::utility::convertNumber<ValueType>(this->actualRelativeMILPGap);
598 return storm::utility::abs<ValueType>(storm::utility::convertNumber<ValueType>(this->actualRelativeMILPGap) * getObjectiveValue());
VariableCoefficients getLinearCoefficients(Expression const &expression)
Computes the (double) coefficients of all identifiers appearing in the expression if the expression w...
A class that implements the LpSolver interface using glpk as the background solver.
virtual bool getBinaryValue(Variable const &name) const override
Retrieves the value of the binary variable with the given name.
virtual void setMaximalMILPGap(ValueType const &gap, bool relative) override
Specifies the maximum difference between lower- and upper objective bounds that triggers termination.
virtual void push() override
Pushes a backtracking point on the solver's stack.
virtual ValueType getContinuousValue(Variable const &name) const override
Retrieves the value of the continuous variable with the given name.
virtual int_fast64_t getIntegerValue(Variable const &name) const override
Retrieves the value of the integer variable with the given name.
GlpkLpSolver()
Constructs a solver without a name.
virtual Variable addVariable(std::string const &name, VariableType const &type, std::optional< ValueType > const &lowerBound=std::nullopt, std::optional< ValueType > const &upperBound=std::nullopt, ValueType objectiveFunctionCoefficient=0) override
Registers a variable of the given type.
virtual bool isOptimal() const override
Retrieves whether the model was found to be optimal, i.e.
virtual void writeModelToFile(std::string const &filename) const override
Writes the current LP problem to the given file.
virtual bool isInfeasible() const override
Retrieves whether the model was found to be infeasible.
virtual void pop() override
Pops a backtracking point from the solver's stack.
typename LpSolver< ValueType, RawMode >::Constraint Constraint
virtual ValueType getObjectiveValue() const override
Retrieves the value of the objective function.
virtual void addIndicatorConstraint(std::string const &name, Variable indicatorVariable, bool indicatorValue, Constraint const &constraint) override
Adds the given indicator constraint to the LP problem: "If indicatorVariable == indicatorValue,...
virtual void addConstraint(std::string const &name, Constraint const &constraint) override
Adds a the given constraint to the LP problem.
virtual void update() const override
Updates the model to make the variables that have been declared since the last call to update usable.
virtual ValueType getMILPGap(bool relative) const override
Returns the obtained gap after a call to optimize()
virtual ~GlpkLpSolver()
Destructs a solver by freeing the pointers to glpk's structures.
typename LpSolver< ValueType, RawMode >::VariableType VariableType
virtual bool isUnbounded() const override
Retrieves whether the model was found to be infeasible.
virtual void optimize() const override
Optimizes the LP problem previously constructed.
typename LpSolver< ValueType, RawMode >::Variable Variable
#define STORM_LOG_ERROR(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_ERROR_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
RelationType
An enum type specifying the different relations applicable.
int getGlpkType(typename GlpkLpSolver< ValueType, RawMode >::VariableType const &type)
std::vector< T > buildVectorForRange(T min, T max)
Constructs a vector [min, min+1, ...., max-1].
bool isZero(ValueType const &a)
ValueType abs(ValueType const &number)
std::map< storm::expressions::Variable, double >::const_iterator end() const
void separateVariablesFromConstantPart(VariableCoefficients &rhs)
Brings all variables of the right-hand side coefficients to the left-hand side by negating them and m...
std::map< storm::expressions::Variable, double >::const_iterator begin() const
double getConstantPart() const