Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
IterativeMinMaxLinearEquationSolver.cpp
Go to the documentation of this file.
1#include <functional>
2#include <limits>
3#include <type_traits>
4
9
12
30
31namespace storm {
32namespace solver {
33
34template<typename ValueType, typename SolutionType>
36 std::unique_ptr<LinearEquationSolverFactory<SolutionType>>&& linearEquationSolverFactory)
37 : linearEquationSolverFactory(std::move(linearEquationSolverFactory)) {
38 // Intentionally left empty
39}
40
41template<typename ValueType, typename SolutionType>
43 storm::storage::SparseMatrix<ValueType> const& A, std::unique_ptr<LinearEquationSolverFactory<SolutionType>>&& linearEquationSolverFactory)
44 : StandardMinMaxLinearEquationSolver<ValueType, SolutionType>(A), linearEquationSolverFactory(std::move(linearEquationSolverFactory)) {
45 // Intentionally left empty.
46}
47
48template<typename ValueType, typename SolutionType>
50 storm::storage::SparseMatrix<ValueType>&& A, std::unique_ptr<LinearEquationSolverFactory<SolutionType>>&& linearEquationSolverFactory)
51 : StandardMinMaxLinearEquationSolver<ValueType, SolutionType>(std::move(A)), linearEquationSolverFactory(std::move(linearEquationSolverFactory)) {
52 // Intentionally left empty.
53}
54
55template<typename ValueType, typename SolutionType>
56MinMaxMethod IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::getMethod(Environment const& env, bool isExactMode) const {
57 // Adjust the method if none was specified and we want exact or sound computations.
58 auto method = env.solver().minMax().getMethod();
59
60 if (isExactMode && method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch && method != MinMaxMethod::ViToPi) {
61 if (env.solver().minMax().isMethodSetFromDefault()) {
63 "Selecting 'Policy iteration' as the solution technique to guarantee exact results. If you want to override this, please explicitly specify a "
64 "different method.");
65 method = MinMaxMethod::PolicyIteration;
66 } else {
67 STORM_LOG_WARN("The selected solution method " << toString(method) << " does not guarantee exact results.");
68 }
69 } else if (env.solver().isForceSoundness() && method != MinMaxMethod::SoundValueIteration && method != MinMaxMethod::IntervalIteration &&
70 method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch && method != MinMaxMethod::OptimisticValueIteration &&
71 method != MinMaxMethod::GuessingValueIteration) {
72 if (env.solver().minMax().isMethodSetFromDefault()) {
73 method = MinMaxMethod::OptimisticValueIteration;
75 "Selecting '"
76 << toString(method)
77 << "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
78 } else {
79 STORM_LOG_WARN("The selected solution method does not guarantee sound results.");
80 }
81 }
82 STORM_LOG_THROW(method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch ||
83 method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::IntervalIteration ||
84 method == MinMaxMethod::OptimisticValueIteration || method == MinMaxMethod::GuessingValueIteration || method == MinMaxMethod::ViToPi,
85 storm::exceptions::InvalidEnvironmentException, "This solver does not support the selected method '" << toString(method) << "'.");
86 return method;
87}
88
89template<typename ValueType, typename SolutionType>
91 std::vector<SolutionType>& x, std::vector<ValueType> const& b) const {
92 bool result = false;
93 switch (getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact())) {
94 case MinMaxMethod::ValueIteration:
95 result = solveEquationsValueIteration(env, dir, x, b);
96 break;
97 case MinMaxMethod::OptimisticValueIteration:
98 result = solveEquationsOptimisticValueIteration(env, dir, x, b);
99 break;
100 case MinMaxMethod::GuessingValueIteration:
101 result = solveEquationsGuessingValueIteration(env, dir, x, b);
102 break;
103 case MinMaxMethod::PolicyIteration:
104 result = solveEquationsPolicyIteration(env, dir, x, b);
105 break;
106 case MinMaxMethod::RationalSearch:
107 result = solveEquationsRationalSearch(env, dir, x, b);
108 break;
109 case MinMaxMethod::IntervalIteration:
110 result = solveEquationsIntervalIteration(env, dir, x, b);
111 break;
112 case MinMaxMethod::SoundValueIteration:
113 result = solveEquationsSoundValueIteration(env, dir, x, b);
114 break;
115 case MinMaxMethod::ViToPi:
116 result = solveEquationsViToPi(env, dir, x, b);
117 break;
118 default:
119 STORM_LOG_THROW(false, storm::exceptions::InvalidEnvironmentException, "This solver does not implement the selected solution method");
120 }
121
122 return result;
123}
124
125template<typename ValueType, typename SolutionType>
127 if (!viOperatorTriv && !viOperatorNontriv) {
128 if (this->A->hasTrivialRowGrouping()) {
129 // The trivial row grouping minmax operator makes sense over intervals.
130 viOperatorTriv = std::make_shared<helper::ValueIterationOperator<ValueType, true, SolutionType>>();
131 viOperatorTriv->setMatrixBackwards(*this->A);
132 if constexpr (!std::is_same_v<ValueType, storm::Interval>) {
133 // It might be that someone is using a minmaxlinearequationsolver with an advanced VI algorithm
134 // but is just passing a DTMC over doubles. In this case we need to populate this VI operator.
135 // It behaves exactly the same as the trivial row grouping operator, but it is currently hardcoded
136 // to be used by, e.g., optimistic value iteration.
137 viOperatorNontriv = std::make_shared<helper::ValueIterationOperator<ValueType, false, SolutionType>>();
138 viOperatorNontriv->setMatrixBackwards(*this->A);
139 }
140 } else {
141 // The nontrivial row grouping minmax operator makes sense for MDPs.
142 viOperatorNontriv = std::make_shared<helper::ValueIterationOperator<ValueType, false, SolutionType>>();
143 viOperatorNontriv->setMatrixBackwards(*this->A);
144 }
145 }
146 if (this->choiceFixedForRowGroup) {
147 // Ignore those rows that are not selected
148 assert(this->initialScheduler);
149 auto callback = [&](uint64_t groupIndex, uint64_t localRowIndex) {
150 return this->choiceFixedForRowGroup->get(groupIndex) && this->initialScheduler->at(groupIndex) != localRowIndex;
151 };
152 if (viOperatorTriv) {
153 viOperatorTriv->setIgnoredRows(true, callback);
154 }
155 if (viOperatorNontriv) {
156 viOperatorNontriv->setIgnoredRows(true, callback);
157 }
158 }
159}
160
161template<typename ValueType, typename SolutionType>
162void IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::extractScheduler(std::vector<SolutionType>& x, std::vector<ValueType> const& b,
163 OptimizationDirection const& dir, bool updateX, bool robust) const {
164 if (std::is_same_v<ValueType, storm::Interval> && this->A->hasTrivialRowGrouping()) {
165 // Create robust scheduler index if it doesn't exist yet
166 if (!this->robustSchedulerIndex) {
167 this->robustSchedulerIndex = std::vector<uint64_t>(x.size(), 0);
168 } else {
169 this->robustSchedulerIndex->resize(x.size(), 0);
170 }
171 uint64_t numSchedulerChoices = 0;
172 for (uint64_t row = 0; row < this->A->getRowCount(); ++row) {
173 this->robustSchedulerIndex->at(row) = numSchedulerChoices;
174 numSchedulerChoices += this->A->getRow(row).getNumberOfEntries();
175 }
176 // Make sure that storage for scheduler choices is available
177 if (!this->schedulerChoices) {
178 this->schedulerChoices = std::vector<uint64_t>(numSchedulerChoices, 0);
179 } else {
180 this->schedulerChoices->resize(numSchedulerChoices, 0);
181 }
182 } else {
183 // Make sure that storage for scheduler choices is available
184 if (!this->schedulerChoices) {
185 this->schedulerChoices = std::vector<uint64_t>(x.size(), 0);
186 } else {
187 this->schedulerChoices->resize(x.size(), 0);
188 }
189 }
190
191 // Set the correct choices.
192 if (!viOperatorTriv && !viOperatorNontriv) {
193 STORM_LOG_WARN("Expected VI operator to be initialized for scheduler extraction. Initializing now, but this is inefficient.");
194 setUpViOperator();
195 }
196 if (viOperatorTriv) {
197 if constexpr (std::is_same<ValueType, storm::Interval>() && std::is_same<SolutionType, double>()) {
199 schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, robust, updateX ? &x : nullptr, this->robustSchedulerIndex);
200 } else {
201 STORM_LOG_ERROR("SchedulerTrackingHelper not implemented for this setting (trivial row grouping but not Interval->double).");
202 }
203 }
204 if (viOperatorNontriv) {
206 schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, robust, updateX ? &x : nullptr, this->robustSchedulerIndex);
207 }
208}
209
210template<typename ValueType, typename SolutionType>
211bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveInducedEquationSystem(
212 Environment const& env, std::unique_ptr<LinearEquationSolver<SolutionType>>& linearEquationSolver, std::vector<uint64_t> const& scheduler,
213 std::vector<SolutionType>& x, std::vector<ValueType>& subB, std::vector<ValueType> const& originalB, OptimizationDirection dir) const {
214 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
215 if constexpr (std::is_same_v<SolutionType, storm::Interval>) {
216 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException,
217 "We did not implement solving induced equation systems for interval-based models outside of robust VI.");
218 // Implementing this requires linear equation systems with different value types and solution types (or some appropriate casting)
219 return false;
220 }
221 STORM_LOG_ASSERT(subB.size() == x.size(), "Sizes of subB and x do not coincide.");
222 STORM_LOG_ASSERT(this->linearEquationSolverFactory != nullptr, "Wrong constructor was called.");
223 STORM_LOG_THROW(this->linearEquationSolverFactory->getEquationProblemFormat(env) == LinearEquationSolverProblemFormat::FixedPointSystem,
224 storm::exceptions::NotImplementedException, "Solving induced system of Interval Model not supported for the selected equation solver");
225
226 storm::storage::SparseMatrixBuilder<SolutionType> newMatrixBuilder(this->A->getRowCount(), this->A->getColumnCount(), this->A->getEntryCount());
227
228 // Robust VI scheduler is an order, compute the correct values for this order
229 auto schedulerIterator = scheduler.begin();
230 for (uint64_t rowIndex = 0; rowIndex < this->A->getRowCount(); rowIndex++) {
231 auto const& row = this->A->getRow(rowIndex);
232
233 static std::vector<SolutionType> tmp;
234 tmp.clear();
235
236 SolutionType probLeft = storm::utility::one<SolutionType>();
237
238 for (auto const& entry : row) {
239 tmp.push_back(entry.getValue().lower());
240 probLeft -= entry.getValue().lower();
241 }
242
243 auto const& rowIter = row.begin();
244 for (uint64_t i = 0; i < row.getNumberOfEntries(); i++, schedulerIterator++) {
245 if (!utility::isZero(probLeft)) {
246 auto const& entry = rowIter[*schedulerIterator];
247 auto const diameter = entry.getValue().upper() - entry.getValue().lower();
248 auto const value = utility::min(probLeft, diameter);
249 tmp[*schedulerIterator] += value;
250 probLeft -= value;
251 } else {
252 // Intentionally left empty: advance schedulerIterator to end of row
253 }
254 }
255
256 for (uint64_t i = 0; i < row.getNumberOfEntries(); i++) {
257 auto const& entry = rowIter[i];
258 newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), tmp[i]);
259 }
260 }
261 STORM_LOG_ASSERT(schedulerIterator == scheduler.end(), "Offset issue in scheduler?");
262
263 subB = originalB;
264
265 std::vector<SolutionType> b;
266 for (auto const& entry : subB) {
267 if (dir == OptimizationDirection::Maximize) {
268 b.push_back(entry.upper());
269 } else {
270 b.push_back(entry.lower());
271 }
272 }
273
274 auto const submatrix = newMatrixBuilder.build();
275
276 // Check whether the linear equation solver is already initialized
277 if (!linearEquationSolver) {
278 // Initialize the equation solver
279 linearEquationSolver = this->linearEquationSolverFactory->create(env, std::move(submatrix));
280 linearEquationSolver->setBoundsFromOtherSolver(*this);
281 linearEquationSolver->setCachingEnabled(true);
282 } else {
283 // If the equation solver is already initialized, it suffices to update the matrix
284 linearEquationSolver->setMatrix(std::move(submatrix));
285 }
286 // Solve the equation system for the 'DTMC' and return true upon success
287 return linearEquationSolver->solveEquations(env, x, b);
288 } else {
289 STORM_LOG_ASSERT(subB.size() == x.size(), "Sizes of subB and x do not coincide.");
290 STORM_LOG_ASSERT(this->linearEquationSolverFactory != nullptr, "Wrong constructor was called.");
291
292 // Resolve the nondeterminism according to the given scheduler.
293 bool convertToEquationSystem = this->linearEquationSolverFactory->getEquationProblemFormat(env) == LinearEquationSolverProblemFormat::EquationSystem;
295
296 submatrix = this->A->selectRowsFromRowGroups(scheduler, convertToEquationSystem);
297 if (convertToEquationSystem) {
298 submatrix.convertToEquationSystem();
299 }
300 storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, this->A->getRowGroupIndices(), originalB);
301
302 // Check whether the linear equation solver is already initialized
303 if (!linearEquationSolver) {
304 // Initialize the equation solver
305 linearEquationSolver = this->linearEquationSolverFactory->create(env, std::move(submatrix));
306 linearEquationSolver->setBoundsFromOtherSolver(*this);
307 linearEquationSolver->setCachingEnabled(true);
308 } else {
309 // If the equation solver is already initialized, it suffices to update the matrix
310 linearEquationSolver->setMatrix(std::move(submatrix));
311 }
312 // Solve the equation system for the 'DTMC' and return true upon success
313 return linearEquationSolver->solveEquations(env, x, subB);
314 }
315}
316
317template<typename ValueType, typename SolutionType>
318bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsPolicyIteration(Environment const& env, OptimizationDirection dir,
319 std::vector<SolutionType>& x,
320 std::vector<ValueType> const& b) const {
321 std::vector<storm::storage::sparse::state_type> scheduler =
322 this->hasInitialScheduler() ? this->getInitialScheduler() : std::vector<storm::storage::sparse::state_type>(this->A->getRowGroupCount());
323 return performPolicyIteration(env, dir, x, b, std::move(scheduler));
324}
325
326template<typename ValueType, typename SolutionType>
327bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::performPolicyIteration(
328 Environment const& env, OptimizationDirection dir, std::vector<SolutionType>& x, std::vector<ValueType> const& b,
329 std::vector<storm::storage::sparse::state_type>&& initialPolicy) const {
330 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
331 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement policy iteration for interval-based models.");
332 return false;
333 } else {
334 std::vector<storm::storage::sparse::state_type> scheduler = std::move(initialPolicy);
335 // Get a vector for storing the right-hand side of the inner equation system.
336 if (!auxiliaryRowGroupVector) {
337 auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
338 }
339 std::vector<ValueType>& subB = *auxiliaryRowGroupVector;
340
341 // The solver that we will use throughout the procedure.
342 std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver;
343 // The linear equation solver should be at least as precise as this solver
344 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
345 auto precOfSolver = env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType());
347 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().minMax().getPrecision();
348 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().minMax().getRelativeTerminationCriterion();
349 if (changePrecision || changeRelative) {
350 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
351 boost::optional<storm::RationalNumber> newPrecision;
352 boost::optional<bool> newRelative;
353 if (changePrecision) {
354 newPrecision = env.solver().minMax().getPrecision();
355 }
356 if (changeRelative) {
357 newRelative = true;
358 }
359 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
360 }
361 }
362 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
363
365 uint64_t iterations = 0;
366 this->startMeasureProgress();
367 do {
368 // Solve the equation system for the 'DTMC'.
369 solveInducedEquationSystem(environmentOfSolver, solver, scheduler, x, subB, b, dir);
370
371 // Go through the multiplication result and see whether we can improve any of the choices.
372 bool schedulerImproved = false;
373 // Group refers to the state number
374 for (uint_fast64_t group = 0; group < this->A->getRowGroupCount(); ++group) {
375 if (!this->choiceFixedForRowGroup || !this->choiceFixedForRowGroup.get()[group]) {
376 // Only update when the choice is not fixed
377 uint_fast64_t currentChoice = scheduler[group];
378 for (uint_fast64_t choice = this->A->getRowGroupIndices()[group]; choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
379 // If the choice is the currently selected one, we can skip it.
380 if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
381 continue;
382 }
383
384 // Create the value of the choice.
385 ValueType choiceValue = storm::utility::zero<ValueType>();
386 for (auto const& entry : this->A->getRow(choice)) {
387 choiceValue += entry.getValue() * x[entry.getColumn()];
388 }
389 choiceValue += b[choice];
390
391 // If the value is strictly better than the solution of the inner system, we need to improve the scheduler.
392 // TODO: If the underlying solver is not precise, this might run forever (i.e. when a state has two choices where the (exact) values are
393 // equal). only changing the scheduler if the values are not equal (modulo precision) would make this unsound.
394 if (valueImproved(dir, x[group], choiceValue)) {
395 schedulerImproved = true;
396 scheduler[group] = choice - this->A->getRowGroupIndices()[group];
397 x[group] = std::move(choiceValue);
398 }
399 }
400 }
401 }
402
403 // If the scheduler did not improve, we are done.
404 if (!schedulerImproved) {
406 }
407
408 // Update environment variables.
409 ++iterations;
410 status =
411 this->updateStatus(status, x, dir == storm::OptimizationDirection::Minimize ? SolverGuarantee::GreaterOrEqual : SolverGuarantee::LessOrEqual,
412 iterations, env.solver().minMax().getMaximalNumberOfIterations());
413
414 // Potentially show progress.
415 this->showProgressIterative(iterations);
416 } while (status == SolverStatus::InProgress);
417
418 STORM_LOG_INFO("Number of iterations: " << iterations);
419 this->reportStatus(status, iterations);
420
421 // If requested, we store the scheduler for retrieval.
422 if (this->isTrackSchedulerSet()) {
423 this->schedulerChoices = std::move(scheduler);
424 }
425
426 if (!this->isCachingEnabled()) {
427 clearCache();
428 }
429
430 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
431 }
432}
433
434template<typename ValueType, typename SolutionType>
435bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::valueImproved(OptimizationDirection dir, ValueType const& value1,
436 ValueType const& value2) const {
437 if (dir == OptimizationDirection::Minimize) {
438 return value2 < value1;
439 } else {
440 return value2 > value1;
441 }
442}
443
444template<typename ValueType, typename SolutionType>
446 Environment const& env, boost::optional<storm::solver::OptimizationDirection> const& direction, bool const& hasInitialScheduler) const {
447 auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact());
448
449 // Check whether a linear equation solver is needed and potentially start with its requirements
450 bool needsLinEqSolver = false;
451 needsLinEqSolver |= method == MinMaxMethod::PolicyIteration;
452 needsLinEqSolver |= method == MinMaxMethod::ValueIteration && (this->hasInitialScheduler() || hasInitialScheduler);
453 needsLinEqSolver |= method == MinMaxMethod::ViToPi;
454
456 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
457 STORM_LOG_ASSERT(!needsLinEqSolver, "Intervals should not require a linear equation solver.");
458 // nothing to be done;
459 } else if (needsLinEqSolver) {
460 requirements = MinMaxLinearEquationSolverRequirements(this->linearEquationSolverFactory->getRequirements(env));
461 } else {
462 // nothing to be done.
463 }
464
465 if (method == MinMaxMethod::ValueIteration) {
466 if (!this->hasUniqueSolution()) { // Traditional value iteration has no requirements if the solution is unique.
467 // Computing a scheduler is only possible if the solution is unique
468 if (env.solver().minMax().isForceRequireUnique() || this->isTrackSchedulerSet()) {
469 requirements.requireUniqueSolution();
470 } else {
471 // As we want the smallest (largest) solution for maximizing (minimizing) equation systems, we have to approach the solution from below (above).
472 if (!direction || direction.get() == OptimizationDirection::Maximize) {
473 requirements.requireLowerBounds();
474 }
475 if (!direction || direction.get() == OptimizationDirection::Minimize) {
476 requirements.requireUpperBounds();
477 }
478 }
479 }
480 } else if (method == MinMaxMethod::OptimisticValueIteration) {
481 // OptimisticValueIteration always requires lower bounds and a unique solution.
482 if (!this->hasUniqueSolution()) {
483 requirements.requireUniqueSolution();
484 }
485 requirements.requireLowerBounds();
486
487 } else if (method == MinMaxMethod::GuessingValueIteration) {
488 // Guessing value iteration requires a unique solution and lower+upper bounds
489 if (!this->hasUniqueSolution()) {
490 requirements.requireUniqueSolution();
491 }
492 requirements.requireBounds();
493 } else if (method == MinMaxMethod::IntervalIteration) {
494 // Interval iteration requires a unique solution and lower+upper bounds
495 if (!this->hasUniqueSolution()) {
496 requirements.requireUniqueSolution();
497 }
498 requirements.requireBounds();
499 } else if (method == MinMaxMethod::RationalSearch) {
500 // Rational search needs to approach the solution from below.
501 requirements.requireLowerBounds();
502 // The solution needs to be unique in case of minimizing or in cases where we want a scheduler.
503 if (!this->hasUniqueSolution()) {
504 // RationalSearch guesses and verifies a fixpoint and terminates once a fixpoint is found. To ensure that the guessed fixpoint is the
505 // correct one, we enforce uniqueness.
506 requirements.requireUniqueSolution();
507 }
508 } else if (method == MinMaxMethod::PolicyIteration) {
509 // The initial scheduler shall not select an end component
510 if (!this->hasUniqueSolution() && env.solver().minMax().isForceRequireUnique()) {
511 requirements.requireUniqueSolution();
512 }
513 if (!this->hasNoEndComponents() && !this->hasInitialScheduler()) {
514 requirements.requireValidInitialScheduler();
515 }
516 } else if (method == MinMaxMethod::SoundValueIteration) {
517 if (!this->hasUniqueSolution()) {
518 requirements.requireUniqueSolution();
519 }
520 requirements.requireBounds(false);
521 } else if (method == MinMaxMethod::ViToPi) {
522 // Since we want to use value iteration to extract an initial scheduler, the solution has to be unique.
523 if (!this->hasUniqueSolution()) {
524 requirements.requireUniqueSolution();
525 }
526 } else {
527 STORM_LOG_THROW(false, storm::exceptions::InvalidEnvironmentException, "Unsupported technique for iterative MinMax linear equation solver.");
528 }
529 return requirements;
530}
531
532template<typename ValueType, typename SolutionType>
533ValueType computeMaxAbsDiff(std::vector<ValueType> const& allValues, storm::storage::BitVector const& relevantValues, std::vector<ValueType> const& oldValues) {
534 ValueType result = storm::utility::zero<ValueType>();
535 auto oldValueIt = oldValues.begin();
536 for (auto value : relevantValues) {
537 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
538 ++oldValueIt;
539 }
540 return result;
541}
542
543template<typename ValueType, typename SolutionType>
544ValueType computeMaxAbsDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues,
545 storm::storage::BitVector const& relevantValues) {
546 ValueType result = storm::utility::zero<ValueType>();
547 for (auto value : relevantValues) {
548 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
549 }
550 return result;
551}
552
553template<typename ValueType, typename SolutionType>
554bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsOptimisticValueIteration(Environment const& env, OptimizationDirection dir,
555 std::vector<SolutionType>& x,
556 std::vector<ValueType> const& b) const {
557 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
558 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement optimistic value iteration for interval-based models.");
559 return false;
560 } else {
562 // If all entries are zero, OVI might run in an endless loop. However, the result is easy in this case.
563 x.assign(x.size(), storm::utility::zero<SolutionType>());
564 if (this->isTrackSchedulerSet()) {
565 this->schedulerChoices = std::vector<uint_fast64_t>(x.size(), 0);
566 }
567 return true;
568 }
569
570 setUpViOperator();
571
572 helper::OptimisticValueIterationHelper<ValueType, false> oviHelper(viOperatorNontriv);
573 auto prec = storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision());
574 std::optional<ValueType> lowerBound, upperBound;
575 if (this->hasLowerBound()) {
576 lowerBound = this->getLowerBound(true);
577 }
578 if (this->hasUpperBound()) {
579 upperBound = this->getUpperBound(true);
580 }
581 uint64_t numIterations{0};
582 auto oviCallback = [&](SolverStatus const& current, std::vector<ValueType> const& v) {
583 this->showProgressIterative(numIterations);
584 return this->updateStatus(current, v, SolverGuarantee::LessOrEqual, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
585 };
586 this->createLowerBoundsVector(x);
587 std::optional<ValueType> guessingFactor;
588 if (env.solver().ovi().getUpperBoundGuessingFactor()) {
589 guessingFactor = storm::utility::convertNumber<ValueType>(*env.solver().ovi().getUpperBoundGuessingFactor());
590 }
591 this->startMeasureProgress();
592 auto status = oviHelper.OVI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(), prec, dir, guessingFactor, lowerBound,
593 upperBound, oviCallback);
594 this->reportStatus(status, numIterations);
595
596 // If requested, we store the scheduler for retrieval.
597 if (this->isTrackSchedulerSet()) {
598 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
599 }
600
601 if (!this->isCachingEnabled()) {
602 clearCache();
603 }
604
605 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
606 }
607}
608
609template<typename ValueType, typename SolutionType>
610bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsGuessingValueIteration(Environment const& env, OptimizationDirection dir,
611 std::vector<SolutionType>& x,
612 std::vector<ValueType> const& b) const {
613 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
614 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement guessing value iteration for interval-based models.");
615 return false;
616 } else {
617 setUpViOperator();
618
619 auto& lowerX = x;
620 auto upperX = std::make_unique<std::vector<SolutionType>>(x.size());
621
623
624 uint64_t numIterations{0};
625
626 auto gviCallback = [&](helper::GVIData<ValueType> const& data) {
627 this->showProgressIterative(numIterations);
628 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x, SolverGuarantee::LessOrEqual) &&
629 this->getTerminationCondition().terminateNow(data.y, SolverGuarantee::GreaterOrEqual);
630 return this->updateStatus(data.status, terminateEarly, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
631 };
632
633 this->createLowerBoundsVector(lowerX);
634 this->createUpperBoundsVector(*upperX);
635
636 this->startMeasureProgress();
637 auto statusIters = helper.solveEquations(lowerX, *upperX, b, numIterations,
638 storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()), dir, gviCallback);
639 auto two = storm::utility::convertNumber<ValueType>(2.0);
640 storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(
641 lowerX, *upperX, x, [&two](ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });
642
643 this->reportStatus(statusIters, numIterations);
644
645 // If requested, we store the scheduler for retrieval.
646 if (this->isTrackSchedulerSet()) {
647 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
648 }
649
650 if (!this->isCachingEnabled()) {
651 clearCache();
652 }
653
654 return statusIters == SolverStatus::Converged || statusIters == SolverStatus::TerminatedEarly;
655 }
656}
657
658template<typename ValueType, typename SolutionType>
659bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsValueIteration(Environment const& env, OptimizationDirection dir,
660 std::vector<SolutionType>& x,
661 std::vector<ValueType> const& b) const {
662 setUpViOperator();
663 // By default, we can not provide any guarantee
665
666 if (this->hasInitialScheduler()) {
667 if (!auxiliaryRowGroupVector) {
668 auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
669 }
670 // Solve the equation system induced by the initial scheduler.
671 std::unique_ptr<storm::solver::LinearEquationSolver<SolutionType>> linEqSolver;
672 // The linear equation solver should be at least as precise as this solver
673 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
674 auto precOfSolver = env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType());
676 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().minMax().getPrecision();
677 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().minMax().getRelativeTerminationCriterion();
678 if (changePrecision || changeRelative) {
679 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
680 boost::optional<storm::RationalNumber> newPrecision;
681 boost::optional<bool> newRelative;
682 if (changePrecision) {
683 newPrecision = env.solver().minMax().getPrecision();
684 }
685 if (changeRelative) {
686 newRelative = true;
687 }
688 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
689 }
690 }
691 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
692
693 bool success = solveInducedEquationSystem(environmentOfSolver, linEqSolver, this->getInitialScheduler(), x, *auxiliaryRowGroupVector, b, dir);
694 if (success) {
695 // If we were given an initial scheduler and are maximizing (minimizing), our current solution becomes
696 // always less-or-equal (greater-or-equal) than the actual solution.
698 } else {
699 guarantee = SolverGuarantee::None;
700 }
701 } else if (!this->hasUniqueSolution()) {
702 if (maximize(dir)) {
703 this->createLowerBoundsVector(x);
705 } else {
706 this->createUpperBoundsVector(x);
708 }
709 } else if (this->hasCustomTerminationCondition()) {
710 if (this->getTerminationCondition().requiresGuarantee(SolverGuarantee::LessOrEqual) && this->hasLowerBound()) {
711 this->createLowerBoundsVector(x);
713 } else if (this->getTerminationCondition().requiresGuarantee(SolverGuarantee::GreaterOrEqual) && this->hasUpperBound()) {
714 this->createUpperBoundsVector(x);
716 }
717 }
718
719 uint64_t numIterations{0};
720 auto viCallback = [&](SolverStatus const& current) {
721 this->showProgressIterative(numIterations);
722 return this->updateStatus(current, x, guarantee, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
723 };
724 this->startMeasureProgress();
725 // This code duplication is necessary because the helper class is different for the two cases.
726 if (this->A->hasTrivialRowGrouping()) {
728
729 auto status = viHelper.VI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(),
730 storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision()), dir, viCallback,
731 env.solver().minMax().getMultiplicationStyle(), this->isUncertaintyRobust());
732 this->reportStatus(status, numIterations);
733
734 // If requested, we store the scheduler for retrieval.
735 if (this->isTrackSchedulerSet()) {
736 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
737 }
738
739 if (!this->isCachingEnabled()) {
740 clearCache();
741 }
742
743 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
744 } else {
746
747 auto status = viHelper.VI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(),
748 storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision()), dir, viCallback,
749 env.solver().minMax().getMultiplicationStyle(), this->isUncertaintyRobust());
750 this->reportStatus(status, numIterations);
751
752 // If requested, we store the scheduler for retrieval.
753 if (this->isTrackSchedulerSet()) {
754 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
755 }
756
757 if (!this->isCachingEnabled()) {
758 clearCache();
759 }
760
761 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
762 }
763}
764
765template<typename ValueType, typename SolutionType>
766void preserveOldRelevantValues(std::vector<ValueType> const& allValues, storm::storage::BitVector const& relevantValues, std::vector<ValueType>& oldValues) {
767 storm::utility::vector::selectVectorValues(oldValues, relevantValues, allValues);
768}
769
776template<typename ValueType, typename SolutionType>
777bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsIntervalIteration(Environment const& env, OptimizationDirection dir,
778 std::vector<SolutionType>& x,
779 std::vector<ValueType> const& b) const {
780 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
781 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement intervaliteration for interval-based models");
782 return false;
783 } else {
784 setUpViOperator();
785 helper::IntervalIterationHelper<ValueType, false> iiHelper(viOperatorNontriv);
786 auto prec = storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision());
787 auto lowerBoundsCallback = [&](std::vector<SolutionType>& vector) { this->createLowerBoundsVector(vector); };
788 auto upperBoundsCallback = [&](std::vector<SolutionType>& vector) { this->createUpperBoundsVector(vector); };
789
790 uint64_t numIterations{0};
791 auto iiCallback = [&](helper::IIData<ValueType> const& data) {
792 this->showProgressIterative(numIterations);
793 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x, SolverGuarantee::LessOrEqual) &&
794 this->getTerminationCondition().terminateNow(data.y, SolverGuarantee::GreaterOrEqual);
795 return this->updateStatus(data.status, terminateEarly, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
796 };
797 std::optional<storm::storage::BitVector> optionalRelevantValues;
798 if (this->hasRelevantValues()) {
799 optionalRelevantValues = this->getRelevantValues();
800 }
801 this->startMeasureProgress();
802 auto status = iiHelper.II(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(), prec, lowerBoundsCallback, upperBoundsCallback,
803 dir, iiCallback, optionalRelevantValues);
804 this->reportStatus(status, numIterations);
805
806 // If requested, we store the scheduler for retrieval.
807 if (this->isTrackSchedulerSet()) {
808 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
809 }
810
811 if (!this->isCachingEnabled()) {
812 clearCache();
813 }
814
815 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
816 }
817}
818
819template<typename ValueType, typename SolutionType>
820bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsSoundValueIteration(Environment const& env, OptimizationDirection dir,
821 std::vector<SolutionType>& x,
822 std::vector<ValueType> const& b) const {
823 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
824 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "SoundVI does not handle interval-based models");
825 return false;
826 } else {
827 // Prepare the solution vectors and the helper.
828 assert(x.size() == this->A->getRowGroupCount());
829
830 std::optional<ValueType> lowerBound, upperBound;
831 if (this->hasLowerBound()) {
832 lowerBound = this->getLowerBound(true);
833 }
834 if (this->hasUpperBound()) {
835 upperBound = this->getUpperBound(true);
836 }
837
838 setUpViOperator();
839
840 auto precision = storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision());
841 uint64_t numIterations{0};
842 auto sviCallback = [&](typename helper::SoundValueIterationHelper<ValueType, false>::SVIData const& current) {
843 this->showProgressIterative(numIterations);
844 return this->updateStatus(current.status,
845 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
846 numIterations, env.solver().minMax().getMaximalNumberOfIterations());
847 };
848 this->startMeasureProgress();
849 helper::SoundValueIterationHelper<ValueType, false> sviHelper(viOperatorNontriv);
850 std::optional<storm::storage::BitVector> optionalRelevantValues;
851 if (this->hasRelevantValues()) {
852 optionalRelevantValues = this->getRelevantValues();
853 }
854 auto status = sviHelper.SVI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(), precision, dir, lowerBound, upperBound,
855 sviCallback, optionalRelevantValues);
856
857 // If requested, we store the scheduler for retrieval.
858 if (this->isTrackSchedulerSet()) {
859 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
860 }
861
862 this->reportStatus(status, numIterations);
863
864 if (!this->isCachingEnabled()) {
865 clearCache();
866 }
867
868 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
869 }
870}
871
872template<typename ValueType, typename SolutionType>
873bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsViToPi(Environment const& env, OptimizationDirection dir,
874 std::vector<SolutionType>& x, std::vector<ValueType> const& b) const {
875 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
876 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "ViToPi does not handle interval-based models");
877 return false;
878 }
879 // First create an (inprecise) vi solver to get a good initial strategy for the (potentially precise) policy iteration solver.
880 std::vector<storm::storage::sparse::state_type> initialSched;
881 {
882 Environment viEnv = env;
883 viEnv.solver().minMax().setMethod(MinMaxMethod::ValueIteration);
884 viEnv.solver().setForceExact(false);
885 viEnv.solver().setForceSoundness(false);
886 auto impreciseSolver = GeneralMinMaxLinearEquationSolverFactory<double>().create(viEnv, this->A->template toValueType<double>());
887 impreciseSolver->setHasUniqueSolution(this->hasUniqueSolution());
888 impreciseSolver->setTrackScheduler(true);
889 if (this->hasInitialScheduler()) {
890 auto initSched = this->getInitialScheduler();
891 impreciseSolver->setInitialScheduler(std::move(initSched));
892 }
893 auto impreciseSolverReq = impreciseSolver->getRequirements(viEnv, dir);
894 STORM_LOG_THROW(!impreciseSolverReq.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
895 "The value-iteration based solver has an unmet requirement: " << impreciseSolverReq.getEnabledRequirementsAsString());
896 impreciseSolver->setRequirementsChecked(true);
897 auto xVi = storm::utility::vector::convertNumericVector<double>(x);
898 auto bVi = storm::utility::vector::convertNumericVector<double>(b);
899 impreciseSolver->solveEquations(viEnv, dir, xVi, bVi);
900 initialSched = impreciseSolver->getSchedulerChoices();
901 }
902 STORM_LOG_INFO("Found initial policy using Value Iteration. Starting Policy iteration now.");
903 return performPolicyIteration(env, dir, x, b, std::move(initialSched));
904}
905
906template<typename ValueType, typename SolutionType>
907bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsRationalSearch(Environment const& env, OptimizationDirection dir,
908 std::vector<SolutionType>& x,
909 std::vector<ValueType> const& b) const {
910 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
911 STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Rational search does not handle interval-based models");
912 return false;
913 } else {
914 // Set up two value iteration operators. One for exact and one for imprecise computations
915 setUpViOperator();
916 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, false>> exactOp;
917 std::shared_ptr<helper::ValueIterationOperator<double, false>> impreciseOp;
918 std::function<bool(uint64_t, uint64_t)> fixedChoicesCallback;
919 if (this->choiceFixedForRowGroup) {
920 // Ignore those rows that are not selected
921 assert(this->initialScheduler);
922 fixedChoicesCallback = [&](uint64_t groupIndex, uint64_t localRowIndex) {
923 return this->choiceFixedForRowGroup->get(groupIndex) && this->initialScheduler->at(groupIndex) != localRowIndex;
924 };
925 }
926
927 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
928 exactOp = viOperatorNontriv;
929 impreciseOp = std::make_shared<helper::ValueIterationOperator<double, false>>();
930 impreciseOp->setMatrixBackwards(this->A->template toValueType<double>(), &this->A->getRowGroupIndices());
931 if (this->choiceFixedForRowGroup) {
932 impreciseOp->setIgnoredRows(true, fixedChoicesCallback);
933 }
934 } else if constexpr (std::is_same_v<ValueType, double>) {
935 impreciseOp = viOperatorNontriv;
936 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, false>>();
937 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>(), &this->A->getRowGroupIndices());
938 if (this->choiceFixedForRowGroup) {
939 exactOp->setIgnoredRows(true, fixedChoicesCallback);
940 }
941 }
942
944 uint64_t numIterations{0};
945 auto rsCallback = [&](SolverStatus const& current) {
946 this->showProgressIterative(numIterations);
947 return this->updateStatus(current, x, SolverGuarantee::None, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
948 };
949 this->startMeasureProgress();
950 auto status = rsHelper.RS(x, b, numIterations, storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()), dir, rsCallback);
951
952 this->reportStatus(status, numIterations);
953
954 // If requested, we store the scheduler for retrieval.
955 if (this->isTrackSchedulerSet()) {
956 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
957 }
958
959 if (!this->isCachingEnabled()) {
960 clearCache();
961 }
962
963 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
964 }
965}
966
967template<typename ValueType, typename SolutionType>
969 auxiliaryRowGroupVector.reset();
970 if (viOperatorTriv) {
971 viOperatorTriv.reset();
972 }
973 if (viOperatorNontriv) {
974 viOperatorNontriv.reset();
975 }
977}
978
982
983} // namespace solver
984} // namespace storm
SolverEnvironment & solver()
uint64_t const & getMaximalNumberOfIterations() const
storm::RationalNumber const & getPrecision() const
storm::solver::MinMaxMethod const & getMethod() const
bool const & getRelativeTerminationCriterion() const
std::optional< storm::RationalNumber > const & getUpperBoundGuessingFactor() const
OviSolverEnvironment const & ovi() const
MinMaxSolverEnvironment & minMax()
virtual void clearCache() const override
Clears the currently cached data that has been stored during previous calls of the solver.
IterativeMinMaxLinearEquationSolver(std::unique_ptr< LinearEquationSolverFactory< SolutionType > > &&linearEquationSolverFactory)
virtual bool internalSolveEquations(Environment const &env, OptimizationDirection dir, std::vector< SolutionType > &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
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 & requireValidInitialScheduler(bool critical=true)
MinMaxLinearEquationSolverRequirements & requireUpperBounds(bool critical=true)
Helper class to extract optimal scheduler choices from a MinMax equation system solution.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
void convertToEquationSystem()
Transforms the matrix into an equation system.
SparseMatrix selectRowsFromRowGroups(std::vector< index_type > const &rowGroupToRowIndexMapping, bool insertDiagonalEntries=true) const
Selects exactly one row from each row group of this matrix and returns the resulting matrix.
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_ERROR(message)
Definition logging.h:26
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
SFTBDDChecker::ValueType ValueType
bool constexpr maximize(OptimizationDirection d)
void preserveOldRelevantValues(std::vector< ValueType > const &allValues, storm::storage::BitVector const &relevantValues, std::vector< ValueType > &oldValues)
std::string toString(GurobiSolverMethod const &method)
Yields a string representation of the GurobiSolverMethod.
ValueType computeMaxAbsDiff(std::vector< ValueType > const &allValues, storm::storage::BitVector const &relevantValues, std::vector< ValueType > const &oldValues)
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
Definition vector.h:188
bool hasNonZeroEntry(std::vector< T > const &v)
Definition vector.h:1103
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)
Definition constants.cpp:38