18template<
typename ValueType>
20 : lpSolverFactory(
std::move(lpSolverFactory)) {
24template<
typename ValueType>
31template<
typename ValueType>
38template<
typename ValueType>
40 std::vector<ValueType>
const& b)
const {
42 return solveEquationsLp(env, dir, x, b);
45 "This min max solver does not support the selected technique.");
46 return solveEquationsViToLp(env, dir, x, b);
50template<
typename ValueType>
52 std::vector<ValueType>
const& b)
const {
54 STORM_LOG_THROW(!this->choiceFixedForRowGroup, storm::exceptions::NotImplementedException,
"Fixed choices not implemented for this solution method.");
56 auto viOperator = std::make_shared<helper::ValueIterationOperator<double, false>>();
57 if constexpr (std::is_same_v<ValueType, double>) {
58 viOperator->setMatrixBackwards(*this->A);
60 viOperator->setMatrixBackwards(this->A->template toValueType<double>(), &this->A->getRowGroupIndices());
63 uint64_t numIterations{0};
65 this->showProgressIterative(numIterations);
69 this->createUpperBoundsVector(x);
71 this->createLowerBoundsVector(x);
73 this->startMeasureProgress();
74 if constexpr (std::is_same_v<ValueType, double>) {
79 auto xVi = storm::utility::vector::convertNumericVector<double>(x);
80 auto bVi = storm::utility::vector::convertNumericVector<double>(b);
83 viHelper.VI(xVi, bVi, numIterations, relative, precision, dir, viCallback);
84 auto xIt = xVi.cbegin();
86 xi = storm::utility::convertNumber<ValueType>(*xIt);
91 STORM_LOG_DEBUG(
"Found initial values using Value Iteration. Starting LP solving now.");
94 res = solveEquationsLp(env, dir, x, b,
nullptr, &x);
96 res = solveEquationsLp(env, dir, x, b, &x,
nullptr);
105 for (uint64_t rowGroup = 0; rowGroup < this->A->getRowGroupCount(); ++rowGroup) {
106 uint64_t row = this->A->getRowGroupIndices()[rowGroup];
107 ValueType optimalGroupValue = this->A->multiplyRowWithVector(row, x) + b[row];
108 for (++row; row < this->A->getRowGroupIndices()[rowGroup + 1]; ++row) {
109 ValueType rowValue = this->A->multiplyRowWithVector(row, x) + b[row];
110 if ((
minimize(dir) && rowValue < optimalGroupValue) || (
maximize(dir) && rowValue > optimalGroupValue)) {
111 optimalGroupValue = rowValue;
114 if (x[rowGroup] != optimalGroupValue) {
115 STORM_LOG_WARN(
"LP with provided bounds is incorrect. Restarting without bounds.");
116 return solveEquationsLp(env, dir, x, b);
124template<
typename ValueType>
125bool LpMinMaxLinearEquationSolver<ValueType>::solveEquationsLp(Environment
const& env,
OptimizationDirection dir, std::vector<ValueType>& x,
126 std::vector<ValueType>
const& b, std::vector<ValueType>
const* lowerBounds,
127 std::vector<ValueType>
const* upperBounds)
const {
131 bool const optimizeOnlyRelevant = this->hasRelevantValues() && env.solver().minMax().lp().getOptimizeOnlyForInitialState();
132 STORM_LOG_DEBUG(
"Optimize only for relevant state requested:" << env.solver().minMax().lp().getOptimizeOnlyForInitialState());
133 if (optimizeOnlyRelevant) {
135 }
else if (!this->hasRelevantValues()) {
136 STORM_LOG_DEBUG(
"No relevant values set! Optimizing over all states.");
140 std::function<
ValueType(uint64_t
const&)> lower, upper;
141 if (this->hasLowerBound() && lowerBounds ==
nullptr) {
142 lower = [
this](uint64_t
const&
i) {
return this->getLowerBound(i); };
143 }
else if (!this->hasLowerBound() && lowerBounds !=
nullptr) {
144 STORM_LOG_ASSERT(lowerBounds->size() == x.size(),
"lower bounds vector has invalid size.");
145 lower = [&lowerBounds](uint64_t
const&
i) {
return (*lowerBounds)[
i]; };
146 }
else if (this->hasLowerBound() && lowerBounds !=
nullptr) {
147 STORM_LOG_ASSERT(lowerBounds->size() == x.size(),
"lower bounds vector has invalid size.");
148 lower = [&lowerBounds,
this](uint64_t
const&
i) {
return std::max(this->getLowerBound(i), (*lowerBounds)[i]); };
150 if (this->hasUpperBound() && upperBounds ==
nullptr) {
151 upper = [
this](uint64_t
const&
i) {
return this->getUpperBound(i); };
152 }
else if (!this->hasUpperBound() && upperBounds !=
nullptr) {
153 STORM_LOG_ASSERT(upperBounds->size() == x.size(),
"upper bounds vector has invalid size.");
154 upper = [&upperBounds](uint64_t
const&
i) {
return (*upperBounds)[
i]; };
155 }
else if (this->hasUpperBound() && upperBounds !=
nullptr) {
156 STORM_LOG_ASSERT(upperBounds->size() == x.size(),
"upper bounds vector has invalid size.");
157 upper = [&upperBounds,
this](uint64_t
const&
i) {
return std::min(this->getUpperBound(i), (*upperBounds)[i]); };
159 bool const useBounds = lower || upper;
162 auto solver = lpSolverFactory->createRaw(
"");
163 solver->setOptimizationDirection(
invert(dir));
165 std::map<VariableIndex, ValueType> constantRowGroups;
168 for (uint64_t rowGroup = 0; rowGroup < this->A->getRowGroupCount(); ++rowGroup) {
170 (optimizeOnlyRelevant && !this->getRelevantValues().get(rowGroup)) ? storm::utility::zero<ValueType>() : storm::utility::one<ValueType>();
171 std::optional<ValueType> lowerBound, upperBound;
174 lowerBound = lower(rowGroup);
177 upperBound = upper(rowGroup);
179 STORM_LOG_ASSERT(*lowerBound <= *upperBound,
"Lower Bound at row group " << rowGroup <<
" is " << *lowerBound
180 <<
" which exceeds the upper bound " << *upperBound <<
".");
181 if (*lowerBound == *upperBound) {
184 constantRowGroups.emplace(rowGroup, *lowerBound);
186 solver->addContinuousVariable(
"dummy" + std::to_string(rowGroup));
192 solver->addContinuousVariable(
"x" + std::to_string(rowGroup), lowerBound, upperBound, objValue);
195 STORM_LOG_DEBUG(
"Use eq if there is a single action: " << env.solver().minMax().lp().getUseEqualityForSingleActions());
196 bool const useEqualityForSingleAction = env.solver().minMax().lp().getUseEqualityForSingleActions();
200 for (uint64_t rowGroup = 0; rowGroup < this->A->getRowGroupCount(); ++rowGroup) {
202 uint64_t rowIndex, rowGroupEnd;
203 if (this->choiceFixedForRowGroup && this->choiceFixedForRowGroup.get()[rowGroup]) {
204 rowIndex = this->A->getRowGroupIndices()[rowGroup] + this->getInitialScheduler()[rowGroup];
205 rowGroupEnd = rowIndex + 1;
207 rowIndex = this->A->getRowGroupIndices()[rowGroup];
208 rowGroupEnd = this->A->getRowGroupIndices()[rowGroup + 1];
210 bool const singleAction = (rowIndex + 1 == rowGroupEnd);
213 for (; rowIndex < rowGroupEnd; ++rowIndex) {
214 auto row = this->A->getRow(rowIndex);
215 RawLpConstraint<ValueType> constraint(relationType, -b[rowIndex], row.getNumberOfEntries());
216 auto addToConstraint = [&constraint, &constantRowGroups](VariableIndex
const& var,
ValueType const& val) {
217 if (
auto findRes = constantRowGroups.find(var); findRes != constantRowGroups.end()) {
218 constraint.rhs -= findRes->second * val;
220 constraint.addToLhs(var, val);
223 auto entryIt = row.begin();
224 auto const entryItEnd = row.end();
225 for (; entryIt != entryItEnd && entryIt->getColumn() < rowGroup; ++entryIt) {
226 addToConstraint(entryIt->getColumn(), entryIt->getValue());
228 ValueType diagVal = -storm::utility::one<ValueType>();
229 if (entryIt != entryItEnd && entryIt->getColumn() == rowGroup) {
230 diagVal += entryIt->getValue();
233 addToConstraint(rowGroup, diagVal);
234 for (; entryIt != entryItEnd; ++entryIt) {
235 addToConstraint(entryIt->getColumn(), entryIt->getValue());
237 solver->addConstraint(
"", constraint);
246 bool const infeasible = solver->isInfeasible();
247 if (infeasible && (lowerBounds || upperBounds)) {
249 STORM_LOG_WARN(
"LP with provided bounds is infeasible. Restarting without bounds.");
250 return solveEquationsLp(env, dir, x, b);
252 STORM_LOG_THROW(!infeasible, storm::exceptions::UnexpectedException,
"The MinMax equation system is infeasible.");
253 STORM_LOG_THROW(!solver->isUnbounded(), storm::exceptions::UnexpectedException,
"The MinMax equation system is unbounded.");
254 STORM_LOG_THROW(solver->isOptimal(), storm::exceptions::UnexpectedException,
"Unable to find optimal solution for MinMax equation system.");
257 auto xIt = x.begin();
259 for (; xIt != x.end(); ++xIt, ++
i) {
260 if (
auto findRes = constantRowGroups.find(i); findRes != constantRowGroups.end()) {
261 *xIt = findRes->second;
263 *xIt = solver->getContinuousValue(i);
268 if (this->isTrackSchedulerSet()) {
269 this->schedulerChoices = std::vector<uint_fast64_t>(this->A->getRowGroupCount());
270 for (uint64_t rowGroup = 0; rowGroup < this->A->getRowGroupCount(); ++rowGroup) {
271 if (!this->choiceFixedForRowGroup || !this->choiceFixedForRowGroup.get()[rowGroup]) {
273 uint64_t row = this->A->getRowGroupIndices()[rowGroup];
274 uint64_t optimalChoiceIndex = 0;
275 uint64_t currChoice = 0;
276 ValueType optimalGroupValue = this->A->multiplyRowWithVector(row, x) + b[row];
277 for (++row, ++currChoice; row < this->A->getRowGroupIndices()[rowGroup + 1]; ++row, ++currChoice) {
278 ValueType rowValue = this->A->multiplyRowWithVector(row, x) + b[row];
279 if ((
minimize(dir) && rowValue < optimalGroupValue) || (
maximize(dir) && rowValue > optimalGroupValue)) {
280 optimalGroupValue = rowValue;
281 optimalChoiceIndex = currChoice;
284 this->schedulerChoices.get()[rowGroup] = optimalChoiceIndex;
292template<
typename ValueType>
297template<
typename ValueType>
299 Environment const& env, boost::optional<storm::solver::OptimizationDirection>
const& direction,
bool const& hasInitialScheduler)
const {
327#ifdef STORM_HAVE_CARL
SolverEnvironment & solver()
bool getUseNonTrivialBounds() const
uint64_t const & getMaximalNumberOfIterations() const
MinMaxLpSolverEnvironment const & lp() const
storm::RationalNumber const & getPrecision() const
storm::solver::MinMaxMethod const & getMethod() const
bool isForceRequireUnique() const
bool const & getRelativeTerminationCriterion() const
MinMaxSolverEnvironment & minMax()
Solves a MinMaxLinearEquationSystem using a linear programming solver.
LpMinMaxLinearEquationSolver(std::unique_ptr< storm::utility::solver::LpSolverFactory< ValueType > > &&lpSolverFactory)
virtual bool internalSolveEquations(Environment const &env, OptimizationDirection dir, std::vector< ValueType > &x, std::vector< ValueType > const &b) const override
virtual MinMaxLinearEquationSolverRequirements getRequirements(Environment const &env, boost::optional< storm::solver::OptimizationDirection > const &direction=boost::none, bool const &hasInitialScheduler=false) const override
Retrieves the requirements of this solver for solving equations with the current settings.
virtual void clearCache() const override
Clears the currently cached data that has been stored during previous calls of the solver.
std::conditional_t< RawMode, typename RawLpConstraint< ValueType >::VariableIndexType, storm::expressions::Variable > Variable
virtual void clearCache() const
Clears the currently cached data that has been stored during previous calls of the solver.
MinMaxLinearEquationSolverRequirements & requireBounds(bool critical=true)
MinMaxLinearEquationSolverRequirements & requireUniqueSolution(bool critical=true)
MinMaxLinearEquationSolverRequirements & requireLowerBounds(bool critical=true)
MinMaxLinearEquationSolverRequirements & requireUpperBounds(bool critical=true)
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_WARN(message)
#define STORM_LOG_DEBUG(message)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
bool constexpr maximize(OptimizationDirection d)
OptimizationDirection constexpr invert(OptimizationDirection d)
bool constexpr minimize(OptimizationDirection d)