Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SoplexLpSolver.cpp
Go to the documentation of this file.
2
3#include <numeric>
4
17
18namespace storm {
19namespace solver {
20
21#ifdef STORM_HAVE_SOPLEX
22
23using namespace soplex;
24
25soplex::Rational to_soplex_rational(storm::RationalNumber const& in) {
26 return soplex::Rational(storm::utility::convertNumber<GmpRationalNumber>(in).get_mpq_t());
27}
28
29storm::RationalNumber from_soplex_rational(soplex::Rational const& r) {
30 return storm::utility::convertNumber<storm::RationalNumber>(GmpRationalNumber(r.backend().data()));
31}
32
33template<typename ValueType, bool RawMode>
34SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(std::string const& name, OptimizationDirection const& optDir) : LpSolver<ValueType, RawMode>(optDir) {
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);
42 }
43 solver.setIntParam(SoPlex::VERBOSITY, 0);
44}
45
46template<typename ValueType, bool RawMode>
47SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(std::string const& name) : SoplexLpSolver(name, OptimizationDirection::Minimize) {
48 // Intentionally left empty.
49}
50
51template<typename ValueType, bool RawMode>
52SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(OptimizationDirection const& optDir) : SoplexLpSolver("", optDir) {
53 // Intentionally left empty.
54}
55
56template<typename ValueType, bool RawMode>
57SoplexLpSolver<ValueType, RawMode>::~SoplexLpSolver() {}
58
59template<typename ValueType, bool RawMode>
60void SoplexLpSolver<ValueType, RawMode>::update() const {
61 // Nothing to be done.
62}
63
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.");
70 Variable resultVar;
71 if constexpr (RawMode) {
72 resultVar = nextVariableIndex;
73 } else {
74 resultVar = this->declareOrGetExpressionVariable(name, type);
75 // Assert whether the variable does not exist yet.
76 // Due to incremental usage (push(), pop()), a variable might be declared in the manager but not in the lp model.
77 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0, "Variable " << resultVar.getName() << " exists already in the model.");
78 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
79 }
80 ++nextVariableIndex;
81
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));
87 } else {
88 solver.addColReal(soplex::LPColReal(objectiveFunctionCoefficient, variables, upperBound.has_value() ? *upperBound : soplex::infinity,
89 lowerBound.has_value() ? *lowerBound : -soplex::infinity));
90 }
91 return resultVar;
92}
93
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);
99 }
100 using SoplexValueType = std::conditional_t<std::is_same_v<ValueType, storm::RationalNumber>, soplex::Rational, soplex::Real>;
101 // Extract constraint data
102 SoplexValueType rhs;
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);
108 } else {
109 rhs = constraint.rhs;
110 }
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));
119 } else {
120 row.add(*varIt, *coefIt);
121 }
122 }
123 } else {
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.");
127
128 // FIXME: We're potentially losing precision as the coefficients and the rhs are converted to double. The LinearCoefficientVisitor needs a ValueType!
133 leftCoefficients.separateVariablesFromConstantPart(rightCoefficients);
134 rhs = rightCoefficients.getConstantPart();
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));
140 }
141 }
142
143 // Determine the type of the constraint and add it properly.
144 SoplexValueType l, r;
145 switch (relationType) {
147 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex only supports nonstrict inequalities");
148 break;
150 l = -soplex::infinity;
151 r = rhs;
152 break;
154 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex only supports nonstrict inequalities");
155 break;
157 l = rhs;
158 r = soplex::infinity;
159 break;
161 l = rhs;
162 r = rhs;
163 break;
164 default:
165 STORM_LOG_ASSERT(false, "Illegal operator in LP solver constraint.");
166 }
167 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
168 solver.addRowRational(LPRowRational(l, row, r));
169 } else {
170 solver.addRowReal(LPRow(l, row, r));
171 }
172}
173
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.");
177}
178
179template<typename ValueType, bool RawMode>
180void SoplexLpSolver<ValueType, RawMode>::optimize() const {
181 // First incorporate all recent changes.
182 this->update();
183 //
184 if (this->getOptimizationDirection() == storm::solver::OptimizationDirection::Minimize) {
185 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE);
186 } else {
187 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MAXIMIZE);
188 }
189
190 status = solver.optimize();
191 STORM_LOG_TRACE("soplex status " << status);
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");
196 }
197 this->currentModelHasBeenOptimized = true;
198}
199
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.";
204 }
205
206 return (status == soplex::SPxSolver::INFEASIBLE);
207}
208
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.";
213 }
214
215 return (status == soplex::SPxSolver::UNBOUNDED);
216}
217
218template<typename ValueType, bool RawMode>
219bool SoplexLpSolver<ValueType, RawMode>::isOptimal() const {
220 if (!this->currentModelHasBeenOptimized) {
221 return false;
222 }
223 return (status == soplex::SPxSolver::OPTIMAL);
224}
225
226template<typename ValueType, bool RawMode>
227ValueType SoplexLpSolver<ValueType, RawMode>::getContinuousValue(Variable const& variable) const {
228 ensureSolved();
229
230 uint64_t varIndex;
231 if constexpr (RawMode) {
232 varIndex = variable;
233 } else {
234 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
235 "Accessing value of unknown variable '" << variable.getName() << "'.");
236 varIndex = variableToIndexMap.at(variable);
237 }
238 STORM_LOG_ASSERT(varIndex < nextVariableIndex, "Variable Index exceeds highest value.");
239
240 if (primalSolution.dim() == 0) {
241 primalSolution = TypedDVector(nextVariableIndex);
242 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
243 solver.getPrimalRational(primalSolution);
244 } else {
245 solver.getPrimal(primalSolution);
246 }
247 }
248 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
249 return storm::utility::convertNumber<ValueType>(from_soplex_rational(primalSolution[varIndex]));
250 } else {
251 return primalSolution[varIndex];
252 }
253}
254
255template<>
256double SoplexLpSolver<double>::getObjectiveValue() const {
257 ensureSolved();
258 return solver.objValueReal();
259}
260
261template<typename ValueType, bool RawMode>
262ValueType SoplexLpSolver<ValueType, RawMode>::getObjectiveValue() const {
263 ensureSolved();
264 return storm::utility::convertNumber<ValueType>(from_soplex_rational(solver.objValueRational()));
265}
266
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.");
273 }
274}
275
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);
280 } else {
281 solver.writeFileReal(filename.c_str(), NULL, NULL, NULL);
282 }
283}
284
285template<typename ValueType, bool RawMode>
286void SoplexLpSolver<ValueType, RawMode>::push() {
287 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Push/Pop not supported on SoPlex");
288}
289
290template<typename ValueType, bool RawMode>
291void SoplexLpSolver<ValueType, RawMode>::pop() {
292 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Push/Pop not supported on SoPlex");
293}
294
295#else
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.";
300}
301
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.";
306}
307
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.";
312}
313
314template<typename ValueType, bool RawMode>
316
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.";
323}
324
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.";
329}
330
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.";
335}
336
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.";
341}
342
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.";
347}
348
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.";
353}
354
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.";
359}
360
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.";
365}
366
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.";
371}
372
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.";
377}
378
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.";
383}
384
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.";
389}
390
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.";
395}
396#endif
397
398template<typename ValueType, bool RawMode>
400 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables");
401}
402
403template<typename ValueType, bool RawMode>
405 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support binary variables");
406}
407
408template<typename ValueType, bool RawMode>
410 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
411}
412
413template<typename ValueType, bool RawMode>
415 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
416}
417
418template class SoplexLpSolver<double, false>;
420template class SoplexLpSolver<double, true>;
422} // namespace solver
423} // 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:12
#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:29
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