24#ifdef STORM_HAVE_SOPLEX
26using namespace soplex;
28soplex::Rational to_soplex_rational(storm::RationalNumber
const& in) {
29 return soplex::Rational(storm::utility::convertNumber<GmpRationalNumber>(in).get_mpq_t());
32storm::RationalNumber from_soplex_rational(soplex::Rational
const& r) {
33 return storm::utility::convertNumber<storm::RationalNumber>(GmpRationalNumber(r.backend().data()));
36template<
typename ValueType,
bool RawMode>
38 if (std::is_same_v<ValueType, storm::RationalNumber>) {
39 solver.setIntParam(SoPlex::READMODE, SoPlex::READMODE_RATIONAL);
40 solver.setIntParam(SoPlex::SOLVEMODE, SoPlex::SOLVEMODE_RATIONAL);
41 solver.setIntParam(SoPlex::CHECKMODE, SoPlex::CHECKMODE_RATIONAL);
42 solver.setIntParam(SoPlex::SYNCMODE, SoPlex::SYNCMODE_AUTO);
43 solver.setRealParam(SoPlex::FEASTOL, 0.0);
44 solver.setRealParam(SoPlex::OPTTOL, 0.0);
46 solver.setIntParam(SoPlex::VERBOSITY, 0);
49template<
typename ValueType,
bool RawMode>
54template<
typename ValueType,
bool RawMode>
55SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(OptimizationDirection
const& optDir) : SoplexLpSolver(
"", optDir) {
59template<
typename ValueType,
bool RawMode>
60SoplexLpSolver<ValueType, RawMode>::~SoplexLpSolver() {}
62template<
typename ValueType,
bool RawMode>
63void SoplexLpSolver<ValueType, RawMode>::update()
const {
67template<
typename ValueType,
bool RawMode>
68typename SoplexLpSolver<ValueType, RawMode>::Variable SoplexLpSolver<ValueType, RawMode>::addVariable(std::string
const& name, VariableType
const& type,
69 std::optional<ValueType>
const& lowerBound,
70 std::optional<ValueType>
const& upperBound,
71 ValueType objectiveFunctionCoefficient) {
72 STORM_LOG_THROW(type == VariableType::Continuous, storm::exceptions::NotSupportedException,
"Soplex LP Solver only supports variables of continuous type.");
74 if constexpr (RawMode) {
75 resultVar = nextVariableIndex;
77 resultVar = this->declareOrGetExpressionVariable(name, type);
80 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0,
"Variable " << resultVar.getName() <<
" exists already in the model.");
81 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
85 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
86 soplex::Rational
const rat_inf(soplex::infinity);
87 solver.addColRational(soplex::LPColRational(to_soplex_rational(objectiveFunctionCoefficient), variables,
88 upperBound.has_value() ? to_soplex_rational(*upperBound) : rat_inf,
89 lowerBound.has_value() ? to_soplex_rational(*lowerBound) : -rat_inf));
91 solver.addColReal(soplex::LPColReal(objectiveFunctionCoefficient, variables, upperBound.has_value() ? *upperBound : soplex::
infinity,
92 lowerBound.has_value() ? *lowerBound : -soplex::
infinity));
97template<
typename ValueType,
bool RawMode>
98void SoplexLpSolver<ValueType, RawMode>::addConstraint(std::string
const& name, Constraint
const& constraint) {
99 if constexpr (!RawMode) {
100 STORM_LOG_TRACE(
"Adding constraint " << (name ==
"" ? std::to_string(nextConstraintIndex) : name) <<
" to SoplexLpSolver:\n"
101 <<
"\t" << constraint);
103 using SoplexValueType = std::conditional_t<std::is_same_v<ValueType, storm::RationalNumber>, soplex::Rational, soplex::Real>;
107 TypedDSVector row(0);
108 if constexpr (RawMode) {
109 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
110 rhs = to_soplex_rational(constraint.rhs);
112 rhs = constraint.rhs;
114 relationType = constraint.relationType;
115 row = TypedDSVector(constraint.lhsVariableIndices.size());
116 auto varIt = constraint.lhsVariableIndices.cbegin();
117 auto varItEnd = constraint.lhsVariableIndices.cend();
118 auto coefIt = constraint.lhsCoefficients.cbegin();
119 for (; varIt != varItEnd; ++varIt, ++coefIt) {
120 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
121 row.add(*varIt, to_soplex_rational(*coefIt));
123 row.add(*varIt, *coefIt);
127 STORM_LOG_THROW(constraint.getManager() == this->getManager(), storm::exceptions::InvalidArgumentException,
128 "Constraint was not built over the proper variables.");
129 STORM_LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException,
"Illegal constraint is not a relational expression.");
138 relationType = constraint.getBaseExpression().asBinaryRelationExpression().getRelationType();
139 row = TypedDSVector(std::distance(leftCoefficients.
begin(), leftCoefficients.
end()));
140 for (
auto const& variableCoefficientPair : leftCoefficients) {
141 auto variableIndexPair = this->variableToIndexMap.find(variableCoefficientPair.first);
142 row.add(variableIndexPair->second, leftCoefficients.
getCoefficient(variableIndexPair->first));
147 SoplexValueType l, r;
148 switch (relationType) {
150 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex only supports nonstrict inequalities");
153 l = -soplex::infinity;
157 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex only supports nonstrict inequalities");
161 r = soplex::infinity;
170 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
171 solver.addRowRational(LPRowRational(l, row, r));
173 solver.addRowReal(LPRow(l, row, r));
177template<
typename ValueType,
bool RawMode>
178void SoplexLpSolver<ValueType, RawMode>::addIndicatorConstraint(std::string
const&, Variable,
bool, Constraint
const&) {
179 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"Indicator Constraints not supported for SoPlex.");
182template<
typename ValueType,
bool RawMode>
183void SoplexLpSolver<ValueType, RawMode>::optimize()
const {
187 if (this->getOptimizationDirection() == storm::solver::OptimizationDirection::Minimize) {
188 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE);
190 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MAXIMIZE);
193 status = solver.optimize();
195 if (status == soplex::SPxSolver::ERROR) {
196 STORM_LOG_THROW(
false, storm::exceptions::InternalException,
"Soplex failed");
197 }
else if (status == soplex::SPxSolver::UNKNOWN) {
198 STORM_LOG_THROW(
false, storm::exceptions::InternalException,
"Soplex gives up on this problem");
200 this->currentModelHasBeenOptimized =
true;
203template<
typename ValueType,
bool RawMode>
204bool SoplexLpSolver<ValueType, RawMode>::isInfeasible()
const {
205 if (!this->currentModelHasBeenOptimized) {
206 throw storm::exceptions::InvalidStateException() <<
"Illegal call to SoplexLpSolver<ValueType, RawMode>::isInfeasible: model has not been optimized.";
209 return (status == soplex::SPxSolver::INFEASIBLE);
212template<
typename ValueType,
bool RawMode>
213bool SoplexLpSolver<ValueType, RawMode>::isUnbounded()
const {
214 if (!this->currentModelHasBeenOptimized) {
215 throw storm::exceptions::InvalidStateException() <<
"Illegal call to SoplexLpSolver<ValueType, RawMode>::isUnbounded: model has not been optimized.";
218 return (status == soplex::SPxSolver::UNBOUNDED);
221template<
typename ValueType,
bool RawMode>
222bool SoplexLpSolver<ValueType, RawMode>::isOptimal()
const {
223 if (!this->currentModelHasBeenOptimized) {
226 return (status == soplex::SPxSolver::OPTIMAL);
229template<
typename ValueType,
bool RawMode>
230ValueType SoplexLpSolver<ValueType, RawMode>::getContinuousValue(Variable
const& variable)
const {
234 if constexpr (RawMode) {
237 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
238 "Accessing value of unknown variable '" << variable.getName() <<
"'.");
239 varIndex = variableToIndexMap.at(variable);
241 STORM_LOG_ASSERT(varIndex < nextVariableIndex,
"Variable Index exceeds highest value.");
243 if (primalSolution.dim() == 0) {
244 primalSolution = TypedDVector(nextVariableIndex);
245 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
246 solver.getPrimalRational(primalSolution);
248 solver.getPrimal(primalSolution);
251 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
252 return storm::utility::convertNumber<ValueType>(from_soplex_rational(primalSolution[varIndex]));
254 return primalSolution[varIndex];
259double SoplexLpSolver<double>::getObjectiveValue()
const {
261 return solver.objValueReal();
264template<
typename ValueType,
bool RawMode>
265ValueType SoplexLpSolver<ValueType, RawMode>::getObjectiveValue()
const {
267 return storm::utility::convertNumber<ValueType>(from_soplex_rational(solver.objValueRational()));
270template<
typename ValueType,
bool RawMode>
271void SoplexLpSolver<ValueType, RawMode>::ensureSolved()
const {
272 if (!this->isOptimal()) {
273 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException,
"Unable to get Soplex solution from infeasible model.");
274 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException,
"Unable to get Soplex solution from unbounded model.");
275 STORM_LOG_THROW(
false, storm::exceptions::InvalidAccessException,
"Unable to get Soplex solution from unoptimized model.");
279template<
typename ValueType,
bool RawMode>
280void SoplexLpSolver<ValueType, RawMode>::writeModelToFile(std::string
const& filename)
const {
281 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
282 solver.writeFileRational(filename.c_str(), NULL, NULL, NULL);
284 solver.writeFileReal(filename.c_str(), NULL, NULL, NULL);
288template<
typename ValueType,
bool RawMode>
289void SoplexLpSolver<ValueType, RawMode>::push() {
290 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported on SoPlex");
293template<
typename ValueType,
bool RawMode>
294void SoplexLpSolver<ValueType, RawMode>::pop() {
295 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported on SoPlex");
299template<
typename ValueType,
bool RawMode>
301 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
302 "requires this support. Please choose a version of support with Soplex support.";
305template<
typename ValueType,
bool RawMode>
307 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
308 "requires this support. Please choose a version of support with Soplex support.";
311template<
typename ValueType,
bool RawMode>
313 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
314 "requires this support. Please choose a version of support with Soplex support.";
317template<
typename ValueType,
bool RawMode>
320template<
typename ValueType,
bool RawMode>
322 std::optional<ValueType>
const&,
323 std::optional<ValueType>
const&, ValueType) {
324 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
325 "requires this support. Please choose a version of support with Soplex support.";
328template<
typename ValueType,
bool RawMode>
330 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
331 "requires this support. Please choose a version of support with Soplex support.";
334template<
typename ValueType,
bool RawMode>
336 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
337 "requires this support. Please choose a version of support with Soplex support.";
340template<
typename ValueType,
bool RawMode>
342 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
343 "requires this support. Please choose a version of support with Soplex support.";
346template<
typename ValueType,
bool RawMode>
348 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
349 "requires this support. Please choose a version of support with Soplex support.";
352template<
typename ValueType,
bool RawMode>
354 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
355 "requires this support. Please choose a version of support with Soplex support.";
358template<
typename ValueType,
bool RawMode>
360 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
361 "requires this support. Please choose a version of support with Soplex support.";
364template<
typename ValueType,
bool RawMode>
366 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
367 "requires this support. Please choose a version of support with Soplex support.";
370template<
typename ValueType,
bool RawMode>
372 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
373 "requires this support. Please choose a version of support with Soplex support.";
376template<
typename ValueType,
bool RawMode>
378 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
379 "requires this support. Please choose a version of support with Soplex support.";
382template<
typename ValueType,
bool RawMode>
384 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
385 "requires this support. Please choose a version of support with Soplex support.";
388template<
typename ValueType,
bool RawMode>
390 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
391 "requires this support. Please choose a version of support with Soplex support.";
394template<
typename ValueType,
bool RawMode>
396 throw storm::exceptions::NotImplementedException() <<
"This version of storm was compiled without support for Soplex. Yet, a method was called that "
397 "requires this support. Please choose a version of support with Soplex support.";
401template<
typename ValueType,
bool RawMode>
403 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables");
406template<
typename ValueType,
bool RawMode>
408 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support binary variables");
411template<
typename ValueType,
bool RawMode>
413 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables.");
416template<
typename ValueType,
bool RawMode>
418 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