Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SoplexLpSolver.cpp
Go to the documentation of this file.
2
3#include <numeric>
4
6
10
13
20
21namespace storm {
22namespace solver {
23
24#ifdef STORM_HAVE_SOPLEX
25
26using namespace soplex;
27
28soplex::Rational to_soplex_rational(storm::RationalNumber const& in) {
29 return soplex::Rational(storm::utility::convertNumber<GmpRationalNumber>(in).get_mpq_t());
30}
31
32storm::RationalNumber from_soplex_rational(soplex::Rational const& r) {
33 return storm::utility::convertNumber<storm::RationalNumber>(GmpRationalNumber(r.backend().data()));
34}
35
36template<typename ValueType, bool RawMode>
37SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(std::string const& name, OptimizationDirection const& optDir) : LpSolver<ValueType, RawMode>(optDir) {
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);
45 }
46 solver.setIntParam(SoPlex::VERBOSITY, 0);
47}
48
49template<typename ValueType, bool RawMode>
50SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(std::string const& name) : SoplexLpSolver(name, OptimizationDirection::Minimize) {
51 // Intentionally left empty.
52}
53
54template<typename ValueType, bool RawMode>
55SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(OptimizationDirection const& optDir) : SoplexLpSolver("", optDir) {
56 // Intentionally left empty.
57}
58
59template<typename ValueType, bool RawMode>
60SoplexLpSolver<ValueType, RawMode>::~SoplexLpSolver() {}
61
62template<typename ValueType, bool RawMode>
63void SoplexLpSolver<ValueType, RawMode>::update() const {
64 // Nothing to be done.
65}
66
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.");
73 Variable resultVar;
74 if constexpr (RawMode) {
75 resultVar = nextVariableIndex;
76 } else {
77 resultVar = this->declareOrGetExpressionVariable(name, type);
78 // Assert whether the variable does not exist yet.
79 // Due to incremental usage (push(), pop()), a variable might be declared in the manager but not in the lp model.
80 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0, "Variable " << resultVar.getName() << " exists already in the model.");
81 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
82 }
83 ++nextVariableIndex;
84
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));
90 } else {
91 solver.addColReal(soplex::LPColReal(objectiveFunctionCoefficient, variables, upperBound.has_value() ? *upperBound : soplex::infinity,
92 lowerBound.has_value() ? *lowerBound : -soplex::infinity));
93 }
94 return resultVar;
95}
96
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);
102 }
103 using SoplexValueType = std::conditional_t<std::is_same_v<ValueType, storm::RationalNumber>, soplex::Rational, soplex::Real>;
104 // Extract constraint data
105 SoplexValueType rhs;
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);
111 } else {
112 rhs = constraint.rhs;
113 }
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));
122 } else {
123 row.add(*varIt, *coefIt);
124 }
125 }
126 } else {
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.");
130
131 // FIXME: We're potentially losing precision as the coefficients and the rhs are converted to double. The LinearCoefficientVisitor needs a ValueType!
136 leftCoefficients.separateVariablesFromConstantPart(rightCoefficients);
137 rhs = rightCoefficients.getConstantPart();
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));
143 }
144 }
145
146 // Determine the type of the constraint and add it properly.
147 SoplexValueType l, r;
148 switch (relationType) {
150 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex only supports nonstrict inequalities");
151 break;
153 l = -soplex::infinity;
154 r = rhs;
155 break;
157 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex only supports nonstrict inequalities");
158 break;
160 l = rhs;
161 r = soplex::infinity;
162 break;
164 l = rhs;
165 r = rhs;
166 break;
167 default:
168 STORM_LOG_ASSERT(false, "Illegal operator in LP solver constraint.");
169 }
170 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
171 solver.addRowRational(LPRowRational(l, row, r));
172 } else {
173 solver.addRowReal(LPRow(l, row, r));
174 }
175}
176
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.");
180}
181
182template<typename ValueType, bool RawMode>
183void SoplexLpSolver<ValueType, RawMode>::optimize() const {
184 // First incorporate all recent changes.
185 this->update();
186 //
187 if (this->getOptimizationDirection() == storm::solver::OptimizationDirection::Minimize) {
188 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE);
189 } else {
190 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MAXIMIZE);
191 }
192
193 status = solver.optimize();
194 STORM_LOG_TRACE("soplex status " << status);
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");
199 }
200 this->currentModelHasBeenOptimized = true;
201}
202
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.";
207 }
208
209 return (status == soplex::SPxSolver::INFEASIBLE);
210}
211
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.";
216 }
217
218 return (status == soplex::SPxSolver::UNBOUNDED);
219}
220
221template<typename ValueType, bool RawMode>
222bool SoplexLpSolver<ValueType, RawMode>::isOptimal() const {
223 if (!this->currentModelHasBeenOptimized) {
224 return false;
225 }
226 return (status == soplex::SPxSolver::OPTIMAL);
227}
228
229template<typename ValueType, bool RawMode>
230ValueType SoplexLpSolver<ValueType, RawMode>::getContinuousValue(Variable const& variable) const {
231 ensureSolved();
232
233 uint64_t varIndex;
234 if constexpr (RawMode) {
235 varIndex = variable;
236 } else {
237 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
238 "Accessing value of unknown variable '" << variable.getName() << "'.");
239 varIndex = variableToIndexMap.at(variable);
240 }
241 STORM_LOG_ASSERT(varIndex < nextVariableIndex, "Variable Index exceeds highest value.");
242
243 if (primalSolution.dim() == 0) {
244 primalSolution = TypedDVector(nextVariableIndex);
245 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
246 solver.getPrimalRational(primalSolution);
247 } else {
248 solver.getPrimal(primalSolution);
249 }
250 }
251 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
252 return storm::utility::convertNumber<ValueType>(from_soplex_rational(primalSolution[varIndex]));
253 } else {
254 return primalSolution[varIndex];
255 }
256}
257
258template<>
259double SoplexLpSolver<double>::getObjectiveValue() const {
260 ensureSolved();
261 return solver.objValueReal();
262}
263
264template<typename ValueType, bool RawMode>
265ValueType SoplexLpSolver<ValueType, RawMode>::getObjectiveValue() const {
266 ensureSolved();
267 return storm::utility::convertNumber<ValueType>(from_soplex_rational(solver.objValueRational()));
268}
269
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.");
276 }
277}
278
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);
283 } else {
284 solver.writeFileReal(filename.c_str(), NULL, NULL, NULL);
285 }
286}
287
288template<typename ValueType, bool RawMode>
289void SoplexLpSolver<ValueType, RawMode>::push() {
290 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Push/Pop not supported on SoPlex");
291}
292
293template<typename ValueType, bool RawMode>
294void SoplexLpSolver<ValueType, RawMode>::pop() {
295 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Push/Pop not supported on SoPlex");
296}
297
298#else
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.";
303}
304
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.";
309}
310
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.";
315}
316
317template<typename ValueType, bool RawMode>
319
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.";
326}
327
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.";
332}
333
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.";
338}
339
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.";
344}
345
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.";
350}
351
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.";
356}
357
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.";
362}
363
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.";
368}
369
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.";
374}
375
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.";
380}
381
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.";
386}
387
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.";
392}
393
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.";
398}
399#endif
400
401template<typename ValueType, bool RawMode>
403 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables");
404}
405
406template<typename ValueType, bool RawMode>
408 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support binary variables");
409}
410
411template<typename ValueType, bool RawMode>
413 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
414}
415
416template<typename ValueType, bool RawMode>
418 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
419}
420
421template class SoplexLpSolver<double, false>;
423template class SoplexLpSolver<double, true>;
425} // namespace solver
426} // namespace storm
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)
Definition logging.h:17
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
SFTBDDChecker::ValueType ValueType
RelationType
An enum type specifying the different relations applicable.
ValueType infinity()
Definition constants.cpp:31
LabParser.cpp.
Definition cli.cpp:18
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