21#ifdef STORM_HAVE_SOPLEX
23using namespace soplex;
25soplex::Rational to_soplex_rational(storm::RationalNumber
const& in) {
29storm::RationalNumber from_soplex_rational(soplex::Rational
const& r) {
33template<
typename ValueType,
bool RawMode>
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);
43 solver.setIntParam(SoPlex::VERBOSITY, 0);
46template<
typename ValueType,
bool RawMode>
51template<
typename ValueType,
bool RawMode>
52SoplexLpSolver<ValueType, RawMode>::SoplexLpSolver(OptimizationDirection
const& optDir) : SoplexLpSolver(
"", optDir) {
56template<
typename ValueType,
bool RawMode>
57SoplexLpSolver<ValueType, RawMode>::~SoplexLpSolver() {}
59template<
typename ValueType,
bool RawMode>
60void SoplexLpSolver<ValueType, RawMode>::update()
const {
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.");
71 if constexpr (RawMode) {
72 resultVar = nextVariableIndex;
74 resultVar = this->declareOrGetExpressionVariable(name, type);
77 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0,
"Variable " << resultVar.getName() <<
" exists already in the model.");
78 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
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));
88 solver.addColReal(soplex::LPColReal(objectiveFunctionCoefficient, variables, upperBound.has_value() ? *upperBound : soplex::infinity,
89 lowerBound.has_value() ? *lowerBound : -soplex::infinity));
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);
100 using SoplexValueType = std::conditional_t<std::is_same_v<ValueType, storm::RationalNumber>, soplex::Rational, soplex::Real>;
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);
109 rhs = constraint.rhs;
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));
120 row.add(*varIt, *coefIt);
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.");
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));
144 SoplexValueType l, r;
145 switch (relationType) {
147 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex only supports nonstrict inequalities");
150 l = -soplex::infinity;
154 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex only supports nonstrict inequalities");
158 r = soplex::infinity;
167 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
168 solver.addRowRational(LPRowRational(l, row, r));
170 solver.addRowReal(LPRow(l, row, r));
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.");
179template<
typename ValueType,
bool RawMode>
180void SoplexLpSolver<ValueType, RawMode>::optimize()
const {
184 if (this->getOptimizationDirection() == storm::solver::OptimizationDirection::Minimize) {
185 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE);
187 solver.setIntParam(SoPlex::OBJSENSE, SoPlex::OBJSENSE_MAXIMIZE);
190 status = solver.optimize();
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");
197 this->currentModelHasBeenOptimized =
true;
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.";
206 return (status == soplex::SPxSolver::INFEASIBLE);
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.";
215 return (status == soplex::SPxSolver::UNBOUNDED);
218template<
typename ValueType,
bool RawMode>
219bool SoplexLpSolver<ValueType, RawMode>::isOptimal()
const {
220 if (!this->currentModelHasBeenOptimized) {
223 return (status == soplex::SPxSolver::OPTIMAL);
226template<
typename ValueType,
bool RawMode>
227ValueType SoplexLpSolver<ValueType, RawMode>::getContinuousValue(Variable
const& variable)
const {
231 if constexpr (RawMode) {
234 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
235 "Accessing value of unknown variable '" << variable.getName() <<
"'.");
236 varIndex = variableToIndexMap.at(variable);
238 STORM_LOG_ASSERT(varIndex < nextVariableIndex,
"Variable Index exceeds highest value.");
240 if (primalSolution.dim() == 0) {
241 primalSolution = TypedDVector(nextVariableIndex);
242 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
243 solver.getPrimalRational(primalSolution);
245 solver.getPrimal(primalSolution);
248 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
251 return primalSolution[varIndex];
256double SoplexLpSolver<double>::getObjectiveValue()
const {
258 return solver.objValueReal();
261template<
typename ValueType,
bool RawMode>
262ValueType SoplexLpSolver<ValueType, RawMode>::getObjectiveValue()
const {
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.");
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);
281 solver.writeFileReal(filename.c_str(), NULL, NULL, NULL);
285template<
typename ValueType,
bool RawMode>
286void SoplexLpSolver<ValueType, RawMode>::push() {
287 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported on SoPlex");
290template<
typename ValueType,
bool RawMode>
291void SoplexLpSolver<ValueType, RawMode>::pop() {
292 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported on SoPlex");
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.";
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.";
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.";
314template<
typename ValueType,
bool RawMode>
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.";
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.";
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.";
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.";
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.";
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.";
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.";
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.";
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.";
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.";
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.";
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.";
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.";
398template<
typename ValueType,
bool RawMode>
400 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables");
403template<
typename ValueType,
bool RawMode>
405 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support binary variables");
408template<
typename ValueType,
bool RawMode>
410 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"SoPlex does not support integer variables.");
413template<
typename ValueType,
bool RawMode>
415 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...
An interface that captures the functionality of an LP solver.
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)
#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.
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
double getConstantPart() const