Storm 1.13.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
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...
An interface that captures the functionality of an LP solver.
Definition LpSolver.h:51
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: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.
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