21#ifdef STORM_HAVE_SOPLEX
23using namespace soplex;
25soplex::Rational to_soplex_rational(storm::RationalNumber
const& in) {
26 return soplex::Rational(storm::utility::convertNumber<GmpRationalNumber>(in).get_mpq_t());
29storm::RationalNumber from_soplex_rational(soplex::Rational
const& r) {
30 return storm::utility::convertNumber<storm::RationalNumber>(GmpRationalNumber(r.backend().data()));
33template<
typename ValueType,
bool RawMode>
35 if (std::is_same_v<ValueType, storm::RationalNumber>) {
36 solver.setIntParam(SoPlex::READMODE, SoPlex::READMODE_RATIONAL);
37 solver.setIntParam(SoPlex::SOLVEMODE, SoPlex::SOLVEMODE_RATIONAL);
38 solver.setIntParam(SoPlex::CHECKMODE, SoPlex::CHECKMODE_RATIONAL);
39 solver.setIntParam(SoPlex::SYNCMODE, SoPlex::SYNCMODE_AUTO);
40 solver.setRealParam(SoPlex::FEASTOL, 0.0);
41 solver.setRealParam(SoPlex::OPTTOL, 0.0);
43 solver.setIntParam(SoPlex::VERBOSITY, 0);
46template<
typename ValueType,
bool RawMode>
51template<
typename ValueType,
bool RawMode>
52SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(OptimizationDirection
const& optDir) : SoplexLpSolver(
"", optDir) {
56template<
typename ValueType,
bool RawMode>
57SoplexLpSolver<ValueType, RawMode>::~SoplexLpSolver() {}
59template<
typename ValueType,
bool RawMode>
60void SoplexLpSolver<ValueType, RawMode>::update()
const {
64template<
typename ValueType,
bool RawMode>
65typename SoplexLpSolver<ValueType, RawMode>::Variable SoplexLpSolver<ValueType, RawMode>::addVariable(std::string
const& name, VariableType
const& type,
66 std::optional<ValueType>
const& lowerBound,
67 std::optional<ValueType>
const& upperBound,
68 ValueType objectiveFunctionCoefficient) {
69 STORM_LOG_THROW(type == VariableType::Continuous, storm::exceptions::NotSupportedException,
"Soplex LP Solver only supports variables of continuous type.");
71 if constexpr (RawMode) {
72 resultVar = nextVariableIndex;
74 resultVar = this->declareOrGetExpressionVariable(name, type);
77 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0,
"Variable " << resultVar.getName() <<
" exists already in the model.");
78 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
82 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
83 soplex::Rational
const rat_inf(soplex::infinity);
84 solver.addColRational(soplex::LPColRational(to_soplex_rational(objectiveFunctionCoefficient), variables,
85 upperBound.has_value() ? to_soplex_rational(*upperBound) : rat_inf,
86 lowerBound.has_value() ? to_soplex_rational(*lowerBound) : -rat_inf));
88 solver.addColReal(soplex::LPColReal(objectiveFunctionCoefficient, variables, upperBound.has_value() ? *upperBound : soplex::
infinity,
89 lowerBound.has_value() ? *lowerBound : -soplex::
infinity));
94template<
typename ValueType,
bool RawMode>
95void SoplexLpSolver<ValueType, RawMode>::addConstraint(std::string
const& name, Constraint
const& constraint) {
96 if constexpr (!RawMode) {
97 STORM_LOG_TRACE(
"Adding constraint " << (name ==
"" ? std::to_string(nextConstraintIndex) : name) <<
" to SoplexLpSolver:\n"
98 <<
"\t" << constraint);
100 using SoplexValueType = std::conditional_t<std::is_same_v<ValueType, storm::RationalNumber>, soplex::Rational, soplex::Real>;
104 TypedDSVector row(0);
105 if constexpr (RawMode) {
106 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
107 rhs = to_soplex_rational(constraint.rhs);
109 rhs = constraint.rhs;
111 relationType = constraint.relationType;
112 row = TypedDSVector(constraint.lhsVariableIndices.size());
113 auto varIt = constraint.lhsVariableIndices.cbegin();
114 auto varItEnd = constraint.lhsVariableIndices.cend();
115 auto coefIt = constraint.lhsCoefficients.cbegin();
116 for (; varIt != varItEnd; ++varIt, ++coefIt) {
117 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
118 row.add(*varIt, to_soplex_rational(*coefIt));
120 row.add(*varIt, *coefIt);
124 STORM_LOG_THROW(constraint.getManager() == this->getManager(), storm::exceptions::InvalidArgumentException,
125 "Constraint was not built over the proper variables.");
126 STORM_LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException,
"Illegal constraint is not a relational expression.");
135 relationType = constraint.getBaseExpression().asBinaryRelationExpression().getRelationType();
136 row = TypedDSVector(std::distance(leftCoefficients.
begin(), leftCoefficients.
end()));
137 for (
auto const& variableCoefficientPair : leftCoefficients) {
138 auto variableIndexPair = this->variableToIndexMap.find(variableCoefficientPair.first);
139 row.add(variableIndexPair->second, leftCoefficients.
getCoefficient(variableIndexPair->first));
144 SoplexValueType l, r;
145 switch (relationType) {
147 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex only supports nonstrict inequalities");
150 l = -soplex::infinity;
154 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex only supports nonstrict inequalities");
158 r = soplex::infinity;
167 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
168 solver.addRowRational(LPRowRational(l, row, r));
170 solver.addRowReal(LPRow(l, row, r));
174template<
typename ValueType,
bool RawMode>
175void SoplexLpSolver<ValueType, RawMode>::addIndicatorConstraint(std::string
const&, Variable,
bool, Constraint
const&) {
176 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Indicator Constraints not supported for SoPlex.");
179template<
typename ValueType,
bool RawMode>
180void SoplexLpSolver<ValueType, RawMode>::optimize()
const {
184 if (this->getOptimizationDirection() == storm::solver::OptimizationDirection::Minimize) {
185 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE);
187 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MAXIMIZE);
190 status = solver.optimize();
192 if (status == soplex::SPxSolver::ERROR) {
193 STORM_LOG_THROW(
false, storm::exceptions::InternalException,
"Soplex failed");
194 }
else if (status == soplex::SPxSolver::UNKNOWN) {
195 STORM_LOG_THROW(
false, storm::exceptions::InternalException,
"Soplex gives up on this problem");
197 this->currentModelHasBeenOptimized =
true;
200template<
typename ValueType,
bool RawMode>
201bool SoplexLpSolver<ValueType, RawMode>::isInfeasible()
const {
202 if (!this->currentModelHasBeenOptimized) {
203 throw storm::exceptions::InvalidStateException() <<
"Illegal call to SoplexLpSolver<ValueType, RawMode>::isInfeasible: model has not been optimized.";
206 return (status == soplex::SPxSolver::INFEASIBLE);
209template<
typename ValueType,
bool RawMode>
210bool SoplexLpSolver<ValueType, RawMode>::isUnbounded()
const {
211 if (!this->currentModelHasBeenOptimized) {
212 throw storm::exceptions::InvalidStateException() <<
"Illegal call to SoplexLpSolver<ValueType, RawMode>::isUnbounded: model has not been optimized.";
215 return (status == soplex::SPxSolver::UNBOUNDED);
218template<
typename ValueType,
bool RawMode>
219bool SoplexLpSolver<ValueType, RawMode>::isOptimal()
const {
220 if (!this->currentModelHasBeenOptimized) {
223 return (status == soplex::SPxSolver::OPTIMAL);
226template<
typename ValueType,
bool RawMode>
227ValueType SoplexLpSolver<ValueType, RawMode>::getContinuousValue(Variable
const& variable)
const {
231 if constexpr (RawMode) {
234 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
235 "Accessing value of unknown variable '" << variable.getName() <<
"'.");
236 varIndex = variableToIndexMap.at(variable);
238 STORM_LOG_ASSERT(varIndex < nextVariableIndex,
"Variable Index exceeds highest value.");
240 if (primalSolution.dim() == 0) {
241 primalSolution = TypedDVector(nextVariableIndex);
242 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
243 solver.getPrimalRational(primalSolution);
245 solver.getPrimal(primalSolution);
248 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
249 return storm::utility::convertNumber<ValueType>(from_soplex_rational(primalSolution[varIndex]));
251 return primalSolution[varIndex];
256double SoplexLpSolver<double>::getObjectiveValue()
const {
258 return solver.objValueReal();
261template<
typename ValueType,
bool RawMode>
262ValueType SoplexLpSolver<ValueType, RawMode>::getObjectiveValue()
const {
264 return storm::utility::convertNumber<ValueType>(from_soplex_rational(solver.objValueRational()));
267template<
typename ValueType,
bool RawMode>
268void SoplexLpSolver<ValueType, RawMode>::ensureSolved()
const {
269 if (!this->isOptimal()) {
270 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get Soplex solution from infeasible model.");
271 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get Soplex solution from unbounded model.");
272 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get Soplex solution from unoptimized model.");
276template<
typename ValueType,
bool RawMode>
277void SoplexLpSolver<ValueType, RawMode>::writeModelToFile(std::string
const& filename)
const {
278 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
279 solver.writeFileRational(filename.c_str(), NULL, NULL, NULL);
281 solver.writeFileReal(filename.c_str(), NULL, NULL, NULL);
285template<
typename ValueType,
bool RawMode>
286void SoplexLpSolver<ValueType, RawMode>::push() {
287 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported on SoPlex");
290template<
typename ValueType,
bool RawMode>
291void SoplexLpSolver<ValueType, RawMode>::pop() {
292 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported on SoPlex");
296template<
typename ValueType,
bool RawMode>
298 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
299 "requires this support. Please choose a version of support with Soplex support.";
302template<
typename ValueType,
bool RawMode>
304 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
305 "requires this support. Please choose a version of support with Soplex support.";
308template<
typename ValueType,
bool RawMode>
310 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
311 "requires this support. Please choose a version of support with Soplex support.";
314template<
typename ValueType,
bool RawMode>
317template<
typename ValueType,
bool RawMode>
319 std::optional<ValueType>
const&,
320 std::optional<ValueType>
const&, ValueType) {
321 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
322 "requires this support. Please choose a version of support with Soplex support.";
325template<
typename ValueType,
bool RawMode>
327 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
328 "requires this support. Please choose a version of support with Soplex support.";
331template<
typename ValueType,
bool RawMode>
333 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
334 "requires this support. Please choose a version of support with Soplex support.";
337template<
typename ValueType,
bool RawMode>
339 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
340 "requires this support. Please choose a version of support with Soplex support.";
343template<
typename ValueType,
bool RawMode>
345 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
346 "requires this support. Please choose a version of support with Soplex support.";
349template<
typename ValueType,
bool RawMode>
351 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
352 "requires this support. Please choose a version of support with Soplex support.";
355template<
typename ValueType,
bool RawMode>
357 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
358 "requires this support. Please choose a version of support with Soplex support.";
361template<
typename ValueType,
bool RawMode>
363 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
364 "requires this support. Please choose a version of support with Soplex support.";
367template<
typename ValueType,
bool RawMode>
369 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
370 "requires this support. Please choose a version of support with Soplex support.";
373template<
typename ValueType,
bool RawMode>
375 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
376 "requires this support. Please choose a version of support with Soplex support.";
379template<
typename ValueType,
bool RawMode>
381 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
382 "requires this support. Please choose a version of support with Soplex support.";
385template<
typename ValueType,
bool RawMode>
387 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
388 "requires this support. Please choose a version of support with Soplex support.";
391template<
typename ValueType,
bool RawMode>
393 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
394 "requires this support. Please choose a version of support with Soplex support.";
398template<
typename ValueType,
bool RawMode>
400 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables");
403template<
typename ValueType,
bool RawMode>
405 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support binary variables");
408template<
typename ValueType,
bool RawMode>
410 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables.");
413template<
typename ValueType,
bool RawMode>
415 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables.");
VariableCoefficients getLinearCoefficients(Expression const &expression)
Computes the (double) coefficients of all identifiers appearing in the expression if the expression w...
typename LpSolver< ValueType, RawMode >::Variable Variable
SoplexLpSolver(std::string const &name, OptimizationDirection const &optDir)
Constructs a solver with the given name and model sense.
typename LpSolver< ValueType, RawMode >::VariableType VariableType
typename LpSolver< ValueType, RawMode >::Constraint Constraint
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
RelationType
An enum type specifying the different relations applicable.
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...
double getCoefficient(storm::expressions::Variable const &variable)
std::map< storm::expressions::Variable, double >::const_iterator begin() const
double getConstantPart() const