31template<
typename ValueType,
bool RawMode>
36 modelContainsIntegerVariables(false),
37 isInfeasibleFlag(false),
38 isUnboundedFlag(false) {
40 lp = glp_create_prob();
43 glp_set_prob_name(lp, name.c_str());
52 glp_iocp* defaultParameters =
new glp_iocp();
53 glp_init_iocp(defaultParameters);
54 this->maxMILPGap = defaultParameters->mip_gap;
55 this->maxMILPGapRelative =
true;
60template<
typename ValueType,
bool RawMode>
66template<
typename ValueType,
bool RawMode>
71template<
typename ValueType,
bool RawMode>
76template<
typename ValueType,
bool RawMode>
81template<
typename ValueType,
bool RawMode>
85 glp_delete_prob(this->lp);
90template<
typename ValueType,
bool RawMode>
104 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
105 "requires this support. Please choose a version with GLPK support.";
109template<
typename ValueType,
bool RawMode>
111 std::optional<ValueType>
const& lowerBound,
112 std::optional<ValueType>
const& upperBound,
113 ValueType objectiveFunctionCoefficient) {
114#ifdef STORM_HAVE_GLPK
116 if constexpr (RawMode) {
117 resultVar = variableToIndexMap.size();
119 resultVar = this->declareOrGetExpressionVariable(name, type);
122 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0,
"Variable " << resultVar.getName() <<
" exists already in the model.");
126 if (lowerBound.has_value()) {
127 boundType = upperBound.has_value() ? GLP_DB : GLP_LO;
129 boundType = upperBound.has_value() ? GLP_UP : GLP_FR;
132 if (type == VariableType::Integer || type == VariableType::Binary) {
133 this->modelContainsIntegerVariables =
true;
137 int variableIndex = glp_add_cols(this->lp, 1);
138 glp_set_col_name(this->lp, variableIndex, name.c_str());
139 glp_set_col_bnds(lp, variableIndex, boundType, lowerBound.has_value() ? storm::utility::convertNumber<double>(*lowerBound) : 0.0,
140 upperBound.has_value() ? storm::utility::convertNumber<double>(*upperBound) : 0.0);
141 glp_set_col_kind(this->lp, variableIndex, getGlpkType<ValueType, RawMode>(type));
142 glp_set_obj_coef(this->lp, variableIndex, storm::utility::convertNumber<double>(objectiveFunctionCoefficient));
144 if constexpr (RawMode) {
145 this->variableToIndexMap.push_back(variableIndex);
147 this->variableToIndexMap.emplace(resultVar, variableIndex);
148 if (!incrementalData.empty()) {
149 incrementalData.back().variables.push_back(resultVar);
155 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
156 "requires this support. Please choose a version with GLPK support.";
160template<
typename ValueType,
bool RawMode>
165template<
typename ValueType,
bool RawMode>
167#ifdef STORM_HAVE_GLPK
169 int constraintIndex = glp_add_rows(this->lp, 1);
170 glp_set_row_name(this->lp, constraintIndex, name.c_str());
176 std::vector<int> variableIndices(1, -1);
177 std::vector<double> coefficients(1, 0.0);
178 if constexpr (RawMode) {
179 rhs = storm::utility::convertNumber<double>(constraint.rhs);
180 relationType = constraint.relationType;
181 variableIndices.reserve(constraint.lhsVariableIndices.size() + 1);
182 for (
auto const& var : constraint.lhsVariableIndices) {
183 variableIndices.push_back(this->variableToIndexMap.at(var));
185 coefficients.reserve(constraint.lhsCoefficients.size() + 1);
186 for (
auto const& coef : constraint.lhsCoefficients) {
187 coefficients.push_back(storm::utility::convertNumber<double>(coef));
190 STORM_LOG_THROW(constraint.getManager() == this->getManager(), storm::exceptions::InvalidArgumentException,
191 "Constraint was not built over the proper variables.");
192 STORM_LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException,
"Illegal constraint is not a relational expression.");
200 relationType = constraint.getBaseExpression().asBinaryRelationExpression().getRelationType();
201 int len = std::distance(leftCoefficients.
begin(), leftCoefficients.
end());
202 variableIndices.reserve(len + 1);
203 coefficients.reserve(len + 1);
204 for (
auto const& variableCoefficientPair : leftCoefficients) {
205 auto variableIndexPair = this->variableToIndexMap.find(variableCoefficientPair.first);
206 variableIndices.push_back(variableIndexPair->second);
207 coefficients.push_back(variableCoefficientPair.second);
212 switch (relationType) {
214 glp_set_row_bnds(this->lp, constraintIndex, GLP_UP, 0,
218 glp_set_row_bnds(this->lp, constraintIndex, GLP_UP, 0, rhs);
221 glp_set_row_bnds(this->lp, constraintIndex, GLP_LO,
225 glp_set_row_bnds(this->lp, constraintIndex, GLP_LO, rhs, 0);
228 glp_set_row_bnds(this->lp, constraintIndex, GLP_FX, rhs, rhs);
235 glp_set_mat_row(this->lp, constraintIndex, variableIndices.size() - 1, variableIndices.data(), coefficients.data());
237 this->currentModelHasBeenOptimized =
false;
239 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
240 "requires this support. Please choose a version with GLPK support.";
244template<
typename ValueType,
bool RawMode>
246 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Indicator constraints are not supported for GLPK.");
249#ifdef STORM_HAVE_GLPK
251void callback(glp_tree* t,
void* info) {
252 auto& mipgap = *
static_cast<std::pair<double, bool>*
>(info);
253 double actualRelativeGap = glp_ios_mip_gap(t);
254 double factor = storm::utility::one<double>();
255 if (!mipgap.second) {
258 assert(factor >= 0.0);
260 if (actualRelativeGap * factor <= mipgap.first) {
262 mipgap.first = actualRelativeGap;
263 mipgap.second =
true;
264 glp_ios_terminate(t);
269template<
typename ValueType,
bool RawMode>
271#ifdef STORM_HAVE_GLPK
273 this->isInfeasibleFlag =
false;
274 this->isUnboundedFlag =
false;
277 glp_set_obj_dir(this->lp, this->getOptimizationDirection() == OptimizationDirection::Minimize ? GLP_MIN : GLP_MAX);
280 if (this->modelContainsIntegerVariables) {
281 glp_iocp* parameters =
new glp_iocp();
282 glp_init_iocp(parameters);
284 this->isInfeasibleFlag =
false;
286 parameters->presolve = GLP_ON;
290 error = glp_simplex(this->lp,
nullptr);
291 STORM_LOG_THROW(error == 0, storm::exceptions::InvalidStateException,
"Unable to optimize relaxed glpk model (" << error <<
").");
293 if (glp_get_status(this->lp) == GLP_INFEAS || glp_get_status(this->lp) == GLP_NOFEAS) {
294 this->isInfeasibleFlag =
true;
298 if (glp_get_status(this->lp) == GLP_UNBND) {
299 parameters->presolve = GLP_ON;
301 parameters->presolve = GLP_OFF;
304 if (!this->isInfeasibleFlag) {
307 std::pair<double, bool> mipgap(this->maxMILPGap, this->maxMILPGapRelative);
309 parameters->cb_func = &callback;
310 parameters->cb_info = &mipgap;
314 error = glp_intopt(this->lp, parameters);
315 int status = glp_mip_status(this->lp);
319 this->actualRelativeMILPGap = mipgap.first;
323 if (error == GLP_ENOPFS || status == GLP_NOFEAS) {
324 this->isInfeasibleFlag =
true;
326 }
else if (error == GLP_ENODFS) {
327 this->isUnboundedFlag =
true;
329 }
else if (error == GLP_ESTOP) {
332 }
else if (error == GLP_EBOUND) {
333 throw storm::exceptions::InvalidStateException()
334 <<
"The bounds of some variables are illegal. Note that glpk only accepts integer bounds for integer variables.";
338 error = glp_simplex(this->lp,
nullptr);
341 STORM_LOG_THROW(error == 0, storm::exceptions::InvalidStateException,
"Unable to optimize glpk model (" << error <<
").");
342 this->currentModelHasBeenOptimized =
true;
344 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
345 "requires this support. Please choose a version with GLPK support.";
349template<
typename ValueType,
bool RawMode>
351#ifdef STORM_HAVE_GLPK
352 if (!this->currentModelHasBeenOptimized) {
353 throw storm::exceptions::InvalidStateException() <<
"Illegal call to GlpkLpSolver::isInfeasible: model has not been optimized.";
356 if (this->modelContainsIntegerVariables) {
357 return isInfeasibleFlag;
359 return glp_get_status(this->lp) == GLP_INFEAS || glp_get_status(this->lp) == GLP_NOFEAS;
362 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
363 "requires this support. Please choose a version with GLPK support.";
367template<
typename ValueType,
bool RawMode>
369#ifdef STORM_HAVE_GLPK
370 if (!this->currentModelHasBeenOptimized) {
371 throw storm::exceptions::InvalidStateException() <<
"Illegal call to GlpkLpSolver::isUnbounded: model has not been optimized.";
374 if (this->modelContainsIntegerVariables) {
375 return isUnboundedFlag;
377 return glp_get_status(this->lp) == GLP_UNBND;
380 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
381 "requires this support. Please choose a version with GLPK support.";
385template<
typename ValueType,
bool RawMode>
387 if (!this->currentModelHasBeenOptimized) {
391 return !isInfeasible() && !isUnbounded();
394template<
typename ValueType,
bool RawMode>
396#ifdef STORM_HAVE_GLPK
397 if (!this->isOptimal()) {
398 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
399 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
400 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
403 int variableIndex = variableToIndexMap.at(variable);
406 if (this->modelContainsIntegerVariables) {
407 value = glp_mip_col_val(this->lp,
static_cast<int>(variableIndex));
409 value = glp_get_col_prim(this->lp,
static_cast<int>(variableIndex));
411 return storm::utility::convertNumber<ValueType>(value);
413 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
414 "requires this support. Please choose a version with GLPK support.";
418template<
typename ValueType,
bool RawMode>
420#ifdef STORM_HAVE_GLPK
421 if (!this->isOptimal()) {
422 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
423 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
424 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
427 int variableIndex = variableToIndexMap.at(variable);
430 if (this->modelContainsIntegerVariables) {
431 value = glp_mip_col_val(this->lp, variableIndex);
433 value = glp_get_col_prim(this->lp, variableIndex);
436 double roundedValue = std::round(value);
437 double diff = std::abs(roundedValue - value);
439 "Illegal value for integer variable in GLPK solution (" << value <<
"). Difference to nearest int is " << diff);
440 return static_cast<int_fast64_t
>(roundedValue);
442 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
443 "requires this support. Please choose a version with GLPK support.";
447template<
typename ValueType,
bool RawMode>
449#ifdef STORM_HAVE_GLPK
450 if (!this->isOptimal()) {
451 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
452 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
453 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
456 int variableIndex = variableToIndexMap.at(variable);
459 if (this->modelContainsIntegerVariables) {
460 value = glp_mip_col_val(this->lp, variableIndex);
462 value = glp_get_col_prim(this->lp, variableIndex);
467 "Illegal value for binary variable in GLPK solution (" << value <<
").");
471 "Illegal value for binary variable in GLPK solution (" << value <<
").");
475 return static_cast<bool>(value);
477 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
478 "requires this support. Please choose a version with GLPK support.";
482template<
typename ValueType,
bool RawMode>
484#ifdef STORM_HAVE_GLPK
485 if (!this->isOptimal()) {
486 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from infeasible model.");
487 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unbounded model.");
488 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get glpk solution from unoptimized model.");
492 if (this->modelContainsIntegerVariables) {
493 value = glp_mip_obj_val(this->lp);
495 value = glp_get_obj_val(this->lp);
498 return storm::utility::convertNumber<ValueType>(value);
500 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
501 "requires this support. Please choose a version with GLPK support.";
505template<
typename ValueType,
bool RawMode>
507#ifdef STORM_HAVE_GLPK
508 glp_write_lp(this->lp, 0, filename.c_str());
510 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
511 "requires this support. Please choose a version with GLPK support.";
515template<
typename ValueType,
bool RawMode>
517#ifdef STORM_HAVE_GLPK
518 if constexpr (RawMode) {
519 STORM_LOG_THROW(
false, storm::exceptions::InvalidOperationException,
"Incremental LP solving not supported in raw mode");
521 IncrementalLevel lvl;
522 lvl.firstConstraintIndex = glp_get_num_rows(this->lp) + 1;
523 incrementalData.push_back(lvl);
526 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
527 "requires this support. Please choose a version with GLPK support.";
531template<
typename ValueType,
bool RawMode>
533#ifdef STORM_HAVE_GLPK
534 if constexpr (RawMode) {
535 STORM_LOG_THROW(
false, storm::exceptions::InvalidOperationException,
"Incremental LP solving not supported in raw mode");
537 if (incrementalData.empty()) {
540 IncrementalLevel
const& lvl = incrementalData.back();
543 if (indicesToBeRemoved.size() > 1) {
544 glp_del_rows(this->lp, indicesToBeRemoved.size() - 1, indicesToBeRemoved.data());
546 indicesToBeRemoved.clear();
548 if (!lvl.variables.empty()) {
551 for (
auto const& var : lvl.variables) {
553 auto it = variableToIndexMap.find(var);
554 firstIndex = it->second;
555 variableToIndexMap.erase(it);
558 variableToIndexMap.erase(var);
563 glp_del_cols(this->lp, indicesToBeRemoved.size() - 1, indicesToBeRemoved.data());
565 incrementalData.pop_back();
568 int n = glp_get_num_rows(lp);
569 int m = glp_get_num_cols(lp);
571 for (
int i = 1; i <= n; ++i) {
572 if (glp_get_row_stat(lp, i) == GLP_BS) {
576 for (
int j = 1; j <= m; ++j) {
577 if (glp_get_col_stat(lp, j) == GLP_BS) {
581 if (n != (nb + mb)) {
582 glp_std_basis(this->lp);
587 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for GLPK. Yet, a method was called that "
588 "requires this support. Please choose a version with GLPK support.";
592template<
typename ValueType,
bool RawMode>
594 this->maxMILPGap = storm::utility::convertNumber<double>(gap);
595 this->maxMILPGapRelative = relative;
598template<
typename ValueType,
bool RawMode>
600 STORM_LOG_ASSERT(this->isOptimal(),
"Asked for the MILP gap although there is no (bounded) solution.");
602 return storm::utility::convertNumber<ValueType>(this->actualRelativeMILPGap);
604 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.
SettingsType const & getModule()
Get module.
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