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