Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SoplexLpSolver.cpp
Go to the documentation of this file.
2
3#include <numeric>
4
18
19namespace storm {
20namespace solver {
21
22#ifdef STORM_HAVE_SOPLEX
23
24using namespace soplex;
25
26soplex::Rational to_soplex_rational(storm::RationalNumber const& in) {
27 return soplex::Rational(storm::utility::convertNumber<GmpRationalNumber>(in).get_mpq_t());
28}
29
30storm::RationalNumber from_soplex_rational(soplex::Rational const& r) {
31 return storm::utility::convertNumber<storm::RationalNumber>(GmpRationalNumber(r.backend().data()));
32}
33
34template<typename ValueType, bool RawMode>
35SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(std::string const& name, OptimizationDirection const& optDir) : LpSolver<ValueType, RawMode>(optDir) {
36 if (std::is_same_v<ValueType, storm::RationalNumber>) {
37 solver.setIntParam(SoPlex::READMODE, SoPlex::READMODE_RATIONAL);
38 solver.setIntParam(SoPlex::SOLVEMODE, SoPlex::SOLVEMODE_RATIONAL);
39 solver.setIntParam(SoPlex::CHECKMODE, SoPlex::CHECKMODE_RATIONAL);
40 solver.setIntParam(SoPlex::SYNCMODE, SoPlex::SYNCMODE_AUTO);
41 solver.setRealParam(SoPlex::FEASTOL, 0.0);
42 solver.setRealParam(SoPlex::OPTTOL, 0.0);
43 }
44 solver.setIntParam(SoPlex::VERBOSITY, 0);
45}
46
47template<typename ValueType, bool RawMode>
48SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(std::string const& name) : SoplexLpSolver(name, OptimizationDirection::Minimize) {
49 // Intentionally left empty.
50}
51
52template<typename ValueType, bool RawMode>
53SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(OptimizationDirection const& optDir) : SoplexLpSolver("", optDir) {
54 // Intentionally left empty.
55}
56
57template<typename ValueType, bool RawMode>
58SoplexLpSolver<ValueType, RawMode>::~SoplexLpSolver() {}
59
60template<typename ValueType, bool RawMode>
61void SoplexLpSolver<ValueType, RawMode>::update() const {
62 // Nothing to be done.
63}
64
65template<typename ValueType, bool RawMode>
66typename SoplexLpSolver<ValueType, RawMode>::Variable SoplexLpSolver<ValueType, RawMode>::addVariable(std::string const& name, VariableType const& type,
67 std::optional<ValueType> const& lowerBound,
68 std::optional<ValueType> const& upperBound,
69 ValueType objectiveFunctionCoefficient) {
70 STORM_LOG_THROW(type == VariableType::Continuous, storm::exceptions::NotSupportedException, "Soplex LP Solver only supports variables of continuous type.");
71 Variable resultVar;
72 if constexpr (RawMode) {
73 resultVar = nextVariableIndex;
74 } else {
75 resultVar = this->declareOrGetExpressionVariable(name, type);
76 // Assert whether the variable does not exist yet.
77 // Due to incremental usage (push(), pop()), a variable might be declared in the manager but not in the lp model.
78 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0, "Variable " << resultVar.getName() << " exists already in the model.");
79 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
80 }
81 ++nextVariableIndex;
82
83 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
84 soplex::Rational const rat_inf(soplex::infinity);
85 solver.addColRational(soplex::LPColRational(to_soplex_rational(objectiveFunctionCoefficient), variables,
86 upperBound.has_value() ? to_soplex_rational(*upperBound) : rat_inf,
87 lowerBound.has_value() ? to_soplex_rational(*lowerBound) : -rat_inf));
88 } else {
89 solver.addColReal(soplex::LPColReal(objectiveFunctionCoefficient, variables, upperBound.has_value() ? *upperBound : soplex::infinity,
90 lowerBound.has_value() ? *lowerBound : -soplex::infinity));
91 }
92 return resultVar;
93}
94
95template<typename ValueType, bool RawMode>
96void SoplexLpSolver<ValueType, RawMode>::addConstraint(std::string const& name, Constraint const& constraint) {
97 if constexpr (!RawMode) {
98 STORM_LOG_TRACE("Adding constraint " << (name == "" ? std::to_string(nextConstraintIndex) : name) << " to SoplexLpSolver:\n"
99 << "\t" << constraint);
100 }
101 using SoplexValueType = std::conditional_t<std::is_same_v<ValueType, storm::RationalNumber>, soplex::Rational, soplex::Real>;
102 // Extract constraint data
103 SoplexValueType rhs;
105 TypedDSVector row(0);
106 if constexpr (RawMode) {
107 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
108 rhs = to_soplex_rational(constraint.rhs);
109 } else {
110 rhs = constraint.rhs;
111 }
112 relationType = constraint.relationType;
113 row = TypedDSVector(constraint.lhsVariableIndices.size());
114 auto varIt = constraint.lhsVariableIndices.cbegin();
115 auto varItEnd = constraint.lhsVariableIndices.cend();
116 auto coefIt = constraint.lhsCoefficients.cbegin();
117 for (; varIt != varItEnd; ++varIt, ++coefIt) {
118 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
119 row.add(*varIt, to_soplex_rational(*coefIt));
120 } else {
121 row.add(*varIt, *coefIt);
122 }
123 }
124 } else {
125 STORM_LOG_THROW(constraint.getManager() == this->getManager(), storm::exceptions::InvalidArgumentException,
126 "Constraint was not built over the proper variables.");
127 STORM_LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression.");
128
129 // FIXME: We're potentially losing precision as the coefficients and the rhs are converted to double. The LinearCoefficientVisitor needs a ValueType!
134 leftCoefficients.separateVariablesFromConstantPart(rightCoefficients);
135 rhs = rightCoefficients.getConstantPart();
136 relationType = constraint.getBaseExpression().asBinaryRelationExpression().getRelationType();
137 row = TypedDSVector(std::distance(leftCoefficients.begin(), leftCoefficients.end()));
138 for (auto const& variableCoefficientPair : leftCoefficients) {
139 auto variableIndexPair = this->variableToIndexMap.find(variableCoefficientPair.first);
140 row.add(variableIndexPair->second, leftCoefficients.getCoefficient(variableIndexPair->first));
141 }
142 }
143
144 // Determine the type of the constraint and add it properly.
145 SoplexValueType l, r;
146 switch (relationType) {
148 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex only supports nonstrict inequalities.");
149 break;
151 l = -soplex::infinity;
152 r = rhs;
153 break;
155 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex only supports nonstrict inequalities.");
156 break;
158 l = rhs;
159 r = soplex::infinity;
160 break;
162 l = rhs;
163 r = rhs;
164 break;
165 default:
166 STORM_LOG_ASSERT(false, "Illegal operator in LP solver constraint.");
167 }
168 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
169 solver.addRowRational(LPRowRational(l, row, r));
170 } else {
171 solver.addRowReal(LPRow(l, row, r));
172 }
173}
174
175template<typename ValueType, bool RawMode>
176void SoplexLpSolver<ValueType, RawMode>::addIndicatorConstraint(std::string const&, Variable, bool, Constraint const&) {
177 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Indicator Constraints not supported for SoPlex.");
178}
179
180template<typename ValueType, bool RawMode>
181void SoplexLpSolver<ValueType, RawMode>::optimize() const {
182 // First incorporate all recent changes.
183 this->update();
184 //
185 if (this->getOptimizationDirection() == storm::solver::OptimizationDirection::Minimize) {
186 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE);
187 } else {
188 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MAXIMIZE);
189 }
190
191 status = solver.optimize();
192 STORM_LOG_TRACE("soplex status " << status);
193 if (status == soplex::SPxSolver::ERROR) {
194 STORM_LOG_THROW(false, storm::exceptions::InternalException, "Soplex failed.");
195 } else if (status == soplex::SPxSolver::UNKNOWN) {
196 STORM_LOG_THROW(false, storm::exceptions::InternalException, "Soplex gives up on this problem.");
197 }
198 this->currentModelHasBeenOptimized = true;
199}
200
201template<typename ValueType, bool RawMode>
202bool SoplexLpSolver<ValueType, RawMode>::isInfeasible() const {
203 STORM_LOG_THROW(this->currentModelHasBeenOptimized, storm::exceptions::InvalidStateException,
204 "Illegal call to SoplexLpSolver<ValueType, RawMode>::isInfeasible: model has not been optimized.");
205
206 return (status == soplex::SPxSolver::INFEASIBLE);
207}
208
209template<typename ValueType, bool RawMode>
210bool SoplexLpSolver<ValueType, RawMode>::isUnbounded() const {
211 STORM_LOG_THROW(this->currentModelHasBeenOptimized, storm::exceptions::InvalidStateException,
212 "Illegal call to SoplexLpSolver<ValueType, RawMode>::isUnbounded: model has not been optimized.");
213
214 return (status == soplex::SPxSolver::UNBOUNDED);
215}
216
217template<typename ValueType, bool RawMode>
218bool SoplexLpSolver<ValueType, RawMode>::isOptimal() const {
219 if (!this->currentModelHasBeenOptimized) {
220 return false;
221 }
222 return (status == soplex::SPxSolver::OPTIMAL);
223}
224
225template<typename ValueType, bool RawMode>
226ValueType SoplexLpSolver<ValueType, RawMode>::getContinuousValue(Variable const& variable) const {
227 ensureSolved();
228
229 uint64_t varIndex;
230 if constexpr (RawMode) {
231 varIndex = variable;
232 } else {
233 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
234 "Accessing value of unknown variable '" << variable.getName() << "'.");
235 varIndex = variableToIndexMap.at(variable);
236 }
237 STORM_LOG_ASSERT(varIndex < nextVariableIndex, "Variable Index exceeds highest value.");
238
239 if (primalSolution.dim() == 0) {
240 primalSolution = TypedDVector(nextVariableIndex);
241 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
242 solver.getPrimalRational(primalSolution);
243 } else {
244 solver.getPrimal(primalSolution);
245 }
246 }
247 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
248 return storm::utility::convertNumber<ValueType>(from_soplex_rational(primalSolution[varIndex]));
249 } else {
250 return primalSolution[varIndex];
251 }
252}
253
254template<>
255double SoplexLpSolver<double>::getObjectiveValue() const {
256 ensureSolved();
257 return solver.objValueReal();
258}
259
260template<typename ValueType, bool RawMode>
261ValueType SoplexLpSolver<ValueType, RawMode>::getObjectiveValue() const {
262 ensureSolved();
263 return storm::utility::convertNumber<ValueType>(from_soplex_rational(solver.objValueRational()));
264}
265
266template<typename ValueType, bool RawMode>
267void SoplexLpSolver<ValueType, RawMode>::ensureSolved() const {
268 if (!this->isOptimal()) {
269 STORM_LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Soplex solution from infeasible model.");
270 STORM_LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Soplex solution from unbounded model.");
271 STORM_LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Soplex solution from unoptimized model.");
272 }
273}
274
275template<typename ValueType, bool RawMode>
276void SoplexLpSolver<ValueType, RawMode>::writeModelToFile(std::string const& filename) const {
277 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
278 solver.writeFileRational(filename.c_str(), nullptr, nullptr, nullptr);
279 } else {
280 solver.writeFileReal(filename.c_str(), nullptr, nullptr, nullptr);
281 }
282}
283
284template<typename ValueType, bool RawMode>
285void SoplexLpSolver<ValueType, RawMode>::push() {
286 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Push/Pop not supported on SoPlex.");
287}
288
289template<typename ValueType, bool RawMode>
290void SoplexLpSolver<ValueType, RawMode>::pop() {
291 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Push/Pop not supported on SoPlex.");
292}
293
294#else
295template<typename ValueType, bool RawMode>
297 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
298 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
299 "version of support with Soplex support.");
300}
301
302template<typename ValueType, bool RawMode>
304 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
305 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
306 "version of support with Soplex support.");
307}
308
309template<typename ValueType, bool RawMode>
311 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
312 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
313 "version of support with Soplex support.");
314}
315
316template<typename ValueType, bool RawMode>
318
319template<typename ValueType, bool RawMode>
321 std::optional<ValueType> const&,
322 std::optional<ValueType> const&, ValueType) {
323 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
324 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
325 "version of support with Soplex support.");
326}
327
328template<typename ValueType, bool RawMode>
330 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
331 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
332 "version of support with Soplex support.");
333}
334
335template<typename ValueType, bool RawMode>
337 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
338 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
339 "version of support with Soplex support.");
340}
341
342template<typename ValueType, bool RawMode>
344 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
345 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
346 "version of support with Soplex support.");
347}
348
349template<typename ValueType, bool RawMode>
351 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
352 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
353 "version of support with Soplex support.");
354}
355
356template<typename ValueType, bool RawMode>
358 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
359 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
360 "version of support with Soplex support.");
361}
362
363template<typename ValueType, bool RawMode>
365 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
366 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
367 "version of support with Soplex support.");
368}
369
370template<typename ValueType, bool RawMode>
372 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
373 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
374 "version of support with Soplex support.");
375}
376
377template<typename ValueType, bool RawMode>
379 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
380 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
381 "version of support with Soplex support.");
382}
383
384template<typename ValueType, bool RawMode>
386 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
387 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
388 "version of support with Soplex support.");
389}
390
391template<typename ValueType, bool RawMode>
393 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
394 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
395 "version of support with Soplex support.");
396}
397
398template<typename ValueType, bool RawMode>
400 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
401 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
402 "version of support with Soplex support.");
403}
404
405template<typename ValueType, bool RawMode>
407 STORM_LOG_THROW(false, storm::exceptions::MissingLibraryException,
408 "This version of storm was compiled without support for Soplex. Yet, a method was called that requires this support. Please choose a "
409 "version of support with Soplex support.");
410}
411#endif
412
413template<typename ValueType, bool RawMode>
415 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
416}
417
418template<typename ValueType, bool RawMode>
420 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support binary variables.");
421}
422
423template<typename ValueType, bool RawMode>
425 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
426}
427
428template<typename ValueType, bool RawMode>
430 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "SoPlex does not support integer variables.");
431}
432
433template class SoplexLpSolver<double, false>;
435template class SoplexLpSolver<double, true>;
437} // namespace solver
438} // namespace storm
VariableCoefficients getLinearCoefficients(Expression const &expression)
Computes the (double) coefficients of all identifiers appearing in the expression if the expression w...
An interface that captures the functionality of an LP solver.
Definition LpSolver.h:50
typename LpSolver< ValueType, RawMode >::Variable Variable
virtual void pop() override
Pops a backtracking point from the solver's stack.
virtual ValueType getMILPGap(bool relative) const override
Returns the obtained gap after a call to optimize().
virtual ValueType getObjectiveValue() const override
Retrieves the value of the objective function.
virtual int_fast64_t getIntegerValue(Variable const &name) const override
Retrieves the value of the integer variable with the given name.
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 bool isInfeasible() const override
Retrieves whether the model was found to be infeasible.
SoplexLpSolver(std::string const &name, OptimizationDirection const &optDir)
Constructs a solver with the given name and model sense.
typename LpSolver< ValueType, RawMode >::VariableType VariableType
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 isUnbounded() const override
Retrieves whether the model was found to be infeasible.
virtual void push() override
Pushes a backtracking point on the solver's stack.
virtual ~SoplexLpSolver()
Destructs a solver by freeing the pointers to Gurobi's structures.
virtual ValueType getContinuousValue(Variable const &name) const override
Retrieves the value of the continuous variable with the given 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
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 void optimize() const override
Optimizes the LP problem previously constructed.
virtual void setMaximalMILPGap(ValueType const &gap, bool relative) override
Specifies the maximum difference between lower- and upper objective bounds that triggers termination.
typename LpSolver< ValueType, RawMode >::Constraint Constraint
virtual bool getBinaryValue(Variable const &name) const override
Retrieves the value of the binary variable with the given name.
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
RelationType
An enum type specifying the different relations applicable.
TargetType convertNumber(SourceType 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