33template<
typename ValueType,
typename SolutionType>
36 : linearEquationSolverFactory(
std::move(linearEquationSolverFactory)) {
40template<
typename ValueType,
typename SolutionType>
47template<
typename ValueType,
typename SolutionType>
54template<
typename ValueType,
typename SolutionType>
59 if (isExactMode && method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch && method != MinMaxMethod::ViToPi) {
62 "Selecting 'Policy iteration' as the solution technique to guarantee exact results. If you want to override this, please explicitly specify a "
64 method = MinMaxMethod::PolicyIteration;
66 STORM_LOG_WARN(
"The selected solution method " <<
toString(method) <<
" does not guarantee exact results.");
68 }
else if (env.
solver().
isForceSoundness() && method != MinMaxMethod::SoundValueIteration && method != MinMaxMethod::IntervalIteration &&
69 method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch && method != MinMaxMethod::OptimisticValueIteration &&
70 method != MinMaxMethod::GuessingValueIteration) {
72 method = MinMaxMethod::OptimisticValueIteration;
76 <<
"' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
78 STORM_LOG_WARN(
"The selected solution method does not guarantee sound results.");
83 if (storm::IsIntervalType<ValueType> && method != MinMaxMethod::ValueIteration) {
84 STORM_LOG_WARN(
"Selected method is not supported for this solver and interval models, switching to robust value iteration.");
85 method = MinMaxMethod::ValueIteration;
88 STORM_LOG_THROW(method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch ||
89 method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::IntervalIteration ||
90 method == MinMaxMethod::OptimisticValueIteration || method == MinMaxMethod::GuessingValueIteration || method == MinMaxMethod::ViToPi,
91 storm::exceptions::InvalidEnvironmentException,
"This solver does not support the selected method '" <<
toString(method) <<
"'.");
95template<
typename ValueType,
typename SolutionType>
97 std::vector<SolutionType>& x, std::vector<ValueType>
const& b)
const {
100 case MinMaxMethod::ValueIteration:
101 result = solveEquationsValueIteration(env, dir, x, b);
103 case MinMaxMethod::OptimisticValueIteration:
104 result = solveEquationsOptimisticValueIteration(env, dir, x, b);
106 case MinMaxMethod::GuessingValueIteration:
107 result = solveEquationsGuessingValueIteration(env, dir, x, b);
109 case MinMaxMethod::PolicyIteration:
110 result = solveEquationsPolicyIteration(env, dir, x, b);
112 case MinMaxMethod::RationalSearch:
113 result = solveEquationsRationalSearch(env, dir, x, b);
115 case MinMaxMethod::IntervalIteration:
116 result = solveEquationsIntervalIteration(env, dir, x, b);
118 case MinMaxMethod::SoundValueIteration:
119 result = solveEquationsSoundValueIteration(env, dir, x, b);
121 case MinMaxMethod::ViToPi:
122 result = solveEquationsViToPi(env, dir, x, b);
125 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"This solver does not implement the selected solution method");
131template<
typename ValueType,
typename SolutionType>
133 if (!viOperatorTriv && !viOperatorNontriv) {
134 if (this->A->hasTrivialRowGrouping()) {
136 viOperatorTriv = std::make_shared<helper::ValueIterationOperator<ValueType, true, SolutionType>>();
137 viOperatorTriv->setMatrixBackwards(*this->A);
138 if constexpr (!std::is_same_v<ValueType, storm::Interval>) {
143 viOperatorNontriv = std::make_shared<helper::ValueIterationOperator<ValueType, false, SolutionType>>();
144 viOperatorNontriv->setMatrixBackwards(*this->A);
148 viOperatorNontriv = std::make_shared<helper::ValueIterationOperator<ValueType, false, SolutionType>>();
149 viOperatorNontriv->setMatrixBackwards(*this->A);
152 if (this->choiceFixedForRowGroup) {
154 assert(this->initialScheduler);
155 auto callback = [&](uint64_t groupIndex, uint64_t localRowIndex) {
156 return this->choiceFixedForRowGroup->get(groupIndex) && this->initialScheduler->at(groupIndex) != localRowIndex;
158 if (viOperatorTriv) {
159 viOperatorTriv->setIgnoredRows(
true, callback);
161 if (viOperatorNontriv) {
162 viOperatorNontriv->setIgnoredRows(
true, callback);
167template<
typename ValueType,
typename SolutionType>
168void IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::extractScheduler(std::vector<SolutionType>& x, std::vector<ValueType>
const& b,
171 if (std::is_same_v<ValueType, storm::Interval> && this->A->hasTrivialRowGrouping()) {
173 if (!this->robustSchedulerIndex) {
174 this->robustSchedulerIndex = std::vector<uint64_t>(x.size(), 0);
176 this->robustSchedulerIndex->resize(x.size(), 0);
178 uint64_t numSchedulerChoices = 0;
179 for (uint64_t row = 0; row < this->A->getRowCount(); ++row) {
180 this->robustSchedulerIndex->at(row) = numSchedulerChoices;
181 numSchedulerChoices += this->A->getRow(row).getNumberOfEntries();
184 if (!this->schedulerChoices) {
185 this->schedulerChoices = std::vector<uint64_t>(numSchedulerChoices, 0);
187 this->schedulerChoices->resize(numSchedulerChoices, 0);
191 if (!this->schedulerChoices) {
192 this->schedulerChoices = std::vector<uint64_t>(x.size(), 0);
194 this->schedulerChoices->resize(x.size(), 0);
199 if (!viOperatorTriv && !viOperatorNontriv) {
200 STORM_LOG_WARN(
"Expected VI operator to be initialized for scheduler extraction. Initializing now, but this is inefficient.");
203 if (viOperatorTriv) {
204 if constexpr (std::is_same<ValueType, storm::Interval>() && std::is_same<SolutionType, double>()) {
206 schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, uncertaintyResolutionMode, updateX ? &x : nullptr, this->robustSchedulerIndex);
208 STORM_LOG_ERROR(
"SchedulerTrackingHelper not implemented for this setting (trivial row grouping but not Interval->double).");
211 if (viOperatorNontriv) {
213 schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, uncertaintyResolutionMode, updateX ? &x : nullptr, this->robustSchedulerIndex);
217template<
typename ValueType,
typename SolutionType>
218bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveInducedEquationSystem(
219 Environment
const& env, std::unique_ptr<LinearEquationSolver<SolutionType>>& linearEquationSolver, std::vector<uint64_t>
const& scheduler,
220 std::vector<SolutionType>& x, std::vector<ValueType>& subB, std::vector<ValueType>
const& originalB,
OptimizationDirection dir)
const {
221 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
222 if constexpr (std::is_same_v<SolutionType, storm::Interval>) {
224 "We did not implement solving induced equation systems for interval-based models outside of robust VI.");
228 STORM_LOG_ASSERT(subB.size() == x.size(),
"Sizes of subB and x do not coincide.");
229 STORM_LOG_ASSERT(this->linearEquationSolverFactory !=
nullptr,
"Wrong constructor was called.");
231 storm::exceptions::NotImplementedException,
"Solving induced system of Interval Model not supported for the selected equation solver");
236 auto schedulerIterator = scheduler.begin();
237 for (uint64_t rowIndex = 0; rowIndex < this->A->getRowCount(); rowIndex++) {
238 auto const& row = this->A->getRow(rowIndex);
240 static std::vector<SolutionType> tmp;
243 SolutionType probLeft = storm::utility::one<SolutionType>();
245 for (
auto const& entry : row) {
246 tmp.push_back(entry.getValue().lower());
247 probLeft -= entry.getValue().lower();
250 auto const& rowIter = row.begin();
251 for (uint64_t i = 0;
i < row.getNumberOfEntries();
i++, schedulerIterator++) {
253 auto const& entry = rowIter[*schedulerIterator];
254 auto const diameter = entry.getValue().upper() - entry.getValue().lower();
256 tmp[*schedulerIterator] += value;
263 for (uint64_t i = 0;
i < row.getNumberOfEntries();
i++) {
264 auto const& entry = rowIter[
i];
265 newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), tmp[i]);
268 STORM_LOG_ASSERT(schedulerIterator == scheduler.end(),
"Offset issue in scheduler?");
272 std::vector<SolutionType> b;
273 for (
auto const& entry : subB) {
274 if (dir == OptimizationDirection::Maximize) {
275 b.push_back(entry.upper());
277 b.push_back(entry.lower());
281 auto const submatrix = newMatrixBuilder.build();
284 if (!linearEquationSolver) {
286 linearEquationSolver = this->linearEquationSolverFactory->create(env, std::move(submatrix));
287 linearEquationSolver->setBoundsFromOtherSolver(*
this);
288 linearEquationSolver->setCachingEnabled(
true);
291 linearEquationSolver->setMatrix(std::move(submatrix));
294 return linearEquationSolver->solveEquations(env, x, b);
296 STORM_LOG_ASSERT(subB.size() == x.size(),
"Sizes of subB and x do not coincide.");
297 STORM_LOG_ASSERT(this->linearEquationSolverFactory !=
nullptr,
"Wrong constructor was called.");
304 if (convertToEquationSystem) {
307 storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, this->A->getRowGroupIndices(), originalB);
310 if (!linearEquationSolver) {
312 linearEquationSolver = this->linearEquationSolverFactory->create(env, std::move(submatrix));
313 linearEquationSolver->setBoundsFromOtherSolver(*
this);
314 linearEquationSolver->setCachingEnabled(
true);
317 linearEquationSolver->setMatrix(std::move(submatrix));
320 return linearEquationSolver->solveEquations(env, x, subB);
324template<
typename ValueType,
typename SolutionType>
325bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsPolicyIteration(Environment
const& env,
OptimizationDirection dir,
326 std::vector<SolutionType>& x,
327 std::vector<ValueType>
const& b)
const {
328 std::vector<storm::storage::sparse::state_type> scheduler =
329 this->hasInitialScheduler() ? this->getInitialScheduler() :
std::vector<
storm::storage::sparse::
state_type>(this->A->getRowGroupCount());
330 return performPolicyIteration(env, dir, x, b, std::move(scheduler));
333template<
typename ValueType,
typename SolutionType>
334bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::performPolicyIteration(
335 Environment
const& env,
OptimizationDirection dir, std::vector<SolutionType>& x, std::vector<ValueType>
const& b,
336 std::vector<storm::storage::sparse::state_type>&& initialPolicy)
const {
337 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
338 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement policy iteration for interval-based models.");
341 std::vector<storm::storage::sparse::state_type> scheduler = std::move(initialPolicy);
343 if (!auxiliaryRowGroupVector) {
344 auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
346 std::vector<ValueType>& subB = *auxiliaryRowGroupVector;
349 std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver;
351 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
352 auto precOfSolver = env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType());
354 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().minMax().getPrecision();
355 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().minMax().getRelativeTerminationCriterion();
356 if (changePrecision || changeRelative) {
357 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
358 boost::optional<storm::RationalNumber> newPrecision;
359 boost::optional<bool> newRelative;
360 if (changePrecision) {
361 newPrecision = env.solver().minMax().getPrecision();
363 if (changeRelative) {
366 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
369 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
372 uint64_t iterations = 0;
373 this->startMeasureProgress();
376 solveInducedEquationSystem(environmentOfSolver, solver, scheduler, x, subB, b, dir);
379 bool schedulerImproved =
false;
381 for (uint_fast64_t group = 0; group < this->A->getRowGroupCount(); ++group) {
382 if (!this->choiceFixedForRowGroup || !this->choiceFixedForRowGroup.get()[group]) {
384 uint_fast64_t currentChoice = scheduler[group];
385 for (uint_fast64_t choice = this->A->getRowGroupIndices()[group]; choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
387 if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
392 ValueType choiceValue = storm::utility::zero<ValueType>();
393 for (
auto const& entry : this->A->getRow(choice)) {
394 choiceValue += entry.getValue() * x[entry.getColumn()];
396 choiceValue += b[choice];
401 if (valueImproved(dir, x[group], choiceValue)) {
402 schedulerImproved =
true;
403 scheduler[group] = choice - this->A->getRowGroupIndices()[group];
404 x[group] = std::move(choiceValue);
411 if (!schedulerImproved) {
419 iterations, env.solver().minMax().getMaximalNumberOfIterations());
422 this->showProgressIterative(iterations);
426 this->reportStatus(status, iterations);
429 if (this->isTrackSchedulerSet()) {
430 this->schedulerChoices = std::move(scheduler);
433 if (!this->isCachingEnabled()) {
441template<
typename ValueType,
typename SolutionType>
442bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::valueImproved(
OptimizationDirection dir, ValueType
const& value1,
443 ValueType
const& value2)
const {
444 if (dir == OptimizationDirection::Minimize) {
445 return value2 < value1;
447 return value2 > value1;
451template<
typename ValueType,
typename SolutionType>
453 Environment const& env, boost::optional<storm::solver::OptimizationDirection>
const& direction,
bool const& hasInitialScheduler)
const {
457 bool needsLinEqSolver =
false;
458 needsLinEqSolver |= method == MinMaxMethod::PolicyIteration;
459 needsLinEqSolver |= method == MinMaxMethod::ValueIteration && (this->hasInitialScheduler() || hasInitialScheduler);
460 needsLinEqSolver |= method == MinMaxMethod::ViToPi;
463 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
464 STORM_LOG_ASSERT(!needsLinEqSolver,
"Intervals should not require a linear equation solver.");
466 }
else if (needsLinEqSolver) {
472 if (method == MinMaxMethod::ValueIteration) {
473 if (!this->hasUniqueSolution()) {
479 if (!direction || direction.get() == OptimizationDirection::Maximize) {
482 if (!direction || direction.get() == OptimizationDirection::Minimize) {
487 }
else if (method == MinMaxMethod::OptimisticValueIteration) {
489 if (!this->hasUniqueSolution()) {
494 }
else if (method == MinMaxMethod::GuessingValueIteration) {
496 if (!this->hasUniqueSolution()) {
500 }
else if (method == MinMaxMethod::IntervalIteration) {
502 if (!this->hasUniqueSolution()) {
506 }
else if (method == MinMaxMethod::RationalSearch) {
510 if (!this->hasUniqueSolution()) {
515 }
else if (method == MinMaxMethod::PolicyIteration) {
520 if (!this->hasNoEndComponents() && !this->hasInitialScheduler()) {
523 }
else if (method == MinMaxMethod::SoundValueIteration) {
524 if (!this->hasUniqueSolution()) {
528 }
else if (method == MinMaxMethod::ViToPi) {
530 if (!this->hasUniqueSolution()) {
534 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"Unsupported technique for iterative MinMax linear equation solver.");
539template<
typename ValueType,
typename SolutionType>
541 ValueType result = storm::utility::zero<ValueType>();
542 auto oldValueIt = oldValues.begin();
543 for (
auto value : relevantValues) {
544 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
550template<
typename ValueType,
typename SolutionType>
551ValueType
computeMaxAbsDiff(std::vector<ValueType>
const& allOldValues, std::vector<ValueType>
const& allNewValues,
553 ValueType result = storm::utility::zero<ValueType>();
554 for (
auto value : relevantValues) {
555 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
560template<
typename ValueType,
typename SolutionType>
562 std::vector<SolutionType>& x,
563 std::vector<ValueType>
const& b)
const {
564 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
565 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement optimistic value iteration for interval-based models.");
570 x.assign(x.size(), storm::utility::zero<SolutionType>());
571 if (this->isTrackSchedulerSet()) {
572 this->schedulerChoices = std::vector<uint_fast64_t>(x.size(), 0);
579 helper::OptimisticValueIterationHelper<ValueType, false> oviHelper(viOperatorNontriv);
581 std::optional<ValueType> lowerBound, upperBound;
582 if (this->hasLowerBound()) {
583 lowerBound = this->getLowerBound(
true);
585 if (this->hasUpperBound()) {
586 upperBound = this->getUpperBound(
true);
588 uint64_t numIterations{0};
589 auto oviCallback = [&](
SolverStatus const& current, std::vector<ValueType>
const& v) {
590 this->showProgressIterative(numIterations);
593 this->createLowerBoundsVector(x);
594 std::optional<ValueType> guessingFactor;
598 this->startMeasureProgress();
600 upperBound, oviCallback);
601 this->reportStatus(status, numIterations);
604 if (this->isTrackSchedulerSet()) {
605 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
608 if (!this->isCachingEnabled()) {
616template<
typename ValueType,
typename SolutionType>
617bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsGuessingValueIteration(Environment
const& env,
OptimizationDirection dir,
618 std::vector<SolutionType>& x,
619 std::vector<ValueType>
const& b)
const {
620 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
621 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement guessing value iteration for interval-based models.");
627 auto upperX = std::make_unique<std::vector<SolutionType>>(x.size());
631 uint64_t numIterations{0};
633 auto gviCallback = [&](helper::GVIData<ValueType>
const& data) {
634 this->showProgressIterative(numIterations);
635 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x,
SolverGuarantee::LessOrEqual) &&
637 return this->updateStatus(data.status, terminateEarly, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
640 this->createLowerBoundsVector(lowerX);
641 this->createUpperBoundsVector(*upperX);
643 this->startMeasureProgress();
644 auto statusIters = helper.solveEquations(lowerX, *upperX, b, numIterations,
645 storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()), dir, gviCallback);
646 auto two = storm::utility::convertNumber<ValueType>(2.0);
647 storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(
648 lowerX, *upperX, x, [&two](ValueType
const& first, ValueType
const& second) -> ValueType {
return (first + second) / two; });
650 this->reportStatus(statusIters, numIterations);
653 if (this->isTrackSchedulerSet()) {
654 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
657 if (!this->isCachingEnabled()) {
665template<
typename ValueType,
typename SolutionType>
666bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsValueIteration(Environment
const& env,
OptimizationDirection dir,
667 std::vector<SolutionType>& x,
668 std::vector<ValueType>
const& b)
const {
673 if (this->hasInitialScheduler()) {
674 if (!auxiliaryRowGroupVector) {
675 auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
678 std::unique_ptr<storm::solver::LinearEquationSolver<SolutionType>> linEqSolver;
680 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
681 auto precOfSolver = env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType());
683 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().minMax().getPrecision();
684 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().minMax().getRelativeTerminationCriterion();
685 if (changePrecision || changeRelative) {
686 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
687 boost::optional<storm::RationalNumber> newPrecision;
688 boost::optional<bool> newRelative;
689 if (changePrecision) {
690 newPrecision = env.solver().minMax().getPrecision();
692 if (changeRelative) {
695 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
698 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
700 bool success = solveInducedEquationSystem(environmentOfSolver, linEqSolver, this->getInitialScheduler(), x, *auxiliaryRowGroupVector, b, dir);
708 }
else if (!this->hasUniqueSolution()) {
710 this->createLowerBoundsVector(x);
713 this->createUpperBoundsVector(x);
716 }
else if (this->hasCustomTerminationCondition()) {
718 this->createLowerBoundsVector(x);
721 this->createUpperBoundsVector(x);
726 uint64_t numIterations{0};
728 this->showProgressIterative(numIterations);
729 return this->updateStatus(current, x, guarantee, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
731 this->startMeasureProgress();
733 if (this->A->hasTrivialRowGrouping()) {
736 auto status = viHelper.VI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(),
737 storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision()), dir, viCallback,
738 env.solver().minMax().getMultiplicationStyle(), this->getUncertaintyResolutionMode());
739 this->reportStatus(status, numIterations);
742 if (this->isTrackSchedulerSet()) {
743 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
746 if (!this->isCachingEnabled()) {
754 auto status = viHelper.VI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(),
755 storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision()), dir, viCallback,
756 env.solver().minMax().getMultiplicationStyle(), this->getUncertaintyResolutionMode());
757 this->reportStatus(status, numIterations);
760 if (this->isTrackSchedulerSet()) {
761 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
764 if (!this->isCachingEnabled()) {
772template<
typename ValueType,
typename SolutionType>
783template<
typename ValueType,
typename SolutionType>
785 std::vector<SolutionType>& x,
786 std::vector<ValueType>
const& b)
const {
787 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
788 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement intervaliteration for interval-based models");
792 helper::IntervalIterationHelper<ValueType, false> iiHelper(viOperatorNontriv);
794 auto lowerBoundsCallback = [&](std::vector<SolutionType>& vector) { this->createLowerBoundsVector(vector); };
795 auto upperBoundsCallback = [&](std::vector<SolutionType>& vector) { this->createUpperBoundsVector(vector); };
797 uint64_t numIterations{0};
798 auto iiCallback = [&](helper::IIData<ValueType>
const& data) {
799 this->showProgressIterative(numIterations);
800 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x,
SolverGuarantee::LessOrEqual) &&
804 std::optional<storm::storage::BitVector> optionalRelevantValues;
805 if (this->hasRelevantValues()) {
806 optionalRelevantValues = this->getRelevantValues();
808 this->startMeasureProgress();
810 dir, iiCallback, optionalRelevantValues);
811 this->reportStatus(status, numIterations);
814 if (this->isTrackSchedulerSet()) {
815 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
818 if (!this->isCachingEnabled()) {
826template<
typename ValueType,
typename SolutionType>
827bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsSoundValueIteration(Environment
const& env,
OptimizationDirection dir,
828 std::vector<SolutionType>& x,
829 std::vector<ValueType>
const& b)
const {
830 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
831 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"SoundVI does not handle interval-based models");
835 assert(x.size() == this->A->getRowGroupCount());
837 std::optional<ValueType> lowerBound, upperBound;
838 if (this->hasLowerBound()) {
839 lowerBound = this->getLowerBound(
true);
841 if (this->hasUpperBound()) {
842 upperBound = this->getUpperBound(
true);
847 auto precision = storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision());
848 uint64_t numIterations{0};
849 auto sviCallback = [&](
typename helper::SoundValueIterationHelper<ValueType, false>::SVIData
const& current) {
850 this->showProgressIterative(numIterations);
851 return this->updateStatus(current.status,
852 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
853 numIterations, env.solver().minMax().getMaximalNumberOfIterations());
855 this->startMeasureProgress();
856 helper::SoundValueIterationHelper<ValueType, false> sviHelper(viOperatorNontriv);
857 std::optional<storm::storage::BitVector> optionalRelevantValues;
858 if (this->hasRelevantValues()) {
859 optionalRelevantValues = this->getRelevantValues();
861 auto status = sviHelper.SVI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(), precision, dir, lowerBound, upperBound,
862 sviCallback, optionalRelevantValues);
865 if (this->isTrackSchedulerSet()) {
866 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
869 this->reportStatus(status, numIterations);
871 if (!this->isCachingEnabled()) {
879template<
typename ValueType,
typename SolutionType>
880bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsViToPi(Environment
const& env,
OptimizationDirection dir,
881 std::vector<SolutionType>& x, std::vector<ValueType>
const& b)
const {
882 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
883 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"ViToPi does not handle interval-based models");
887 std::vector<storm::storage::sparse::state_type> initialSched;
889 Environment viEnv = env;
890 viEnv.solver().minMax().setMethod(MinMaxMethod::ValueIteration);
891 viEnv.solver().setForceExact(
false);
892 viEnv.solver().setForceSoundness(
false);
893 auto impreciseSolver = GeneralMinMaxLinearEquationSolverFactory<double>().create(viEnv, this->A->template toValueType<double>());
894 impreciseSolver->setHasUniqueSolution(this->hasUniqueSolution());
895 impreciseSolver->setTrackScheduler(
true);
896 if (this->hasInitialScheduler()) {
897 auto initSched = this->getInitialScheduler();
898 impreciseSolver->setInitialScheduler(std::move(initSched));
900 auto impreciseSolverReq = impreciseSolver->getRequirements(viEnv, dir);
901 STORM_LOG_THROW(!impreciseSolverReq.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
902 "The value-iteration based solver has an unmet requirement: " << impreciseSolverReq.getEnabledRequirementsAsString());
903 impreciseSolver->setRequirementsChecked(
true);
904 auto xVi = storm::utility::vector::convertNumericVector<double>(x);
905 auto bVi = storm::utility::vector::convertNumericVector<double>(b);
906 impreciseSolver->solveEquations(viEnv, dir, xVi, bVi);
907 initialSched = impreciseSolver->getSchedulerChoices();
909 STORM_LOG_INFO(
"Found initial policy using Value Iteration. Starting Policy iteration now.");
910 return performPolicyIteration(env, dir, x, b, std::move(initialSched));
913template<
typename ValueType,
typename SolutionType>
914bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsRationalSearch(Environment
const& env,
OptimizationDirection dir,
915 std::vector<SolutionType>& x,
916 std::vector<ValueType>
const& b)
const {
917 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
918 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Rational search does not handle interval-based models");
923 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, false>> exactOp;
924 std::shared_ptr<helper::ValueIterationOperator<double, false>> impreciseOp;
925 std::function<bool(uint64_t, uint64_t)> fixedChoicesCallback;
926 if (this->choiceFixedForRowGroup) {
928 assert(this->initialScheduler);
929 fixedChoicesCallback = [&](uint64_t groupIndex, uint64_t localRowIndex) {
930 return this->choiceFixedForRowGroup->get(groupIndex) && this->initialScheduler->at(groupIndex) != localRowIndex;
934 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
935 exactOp = viOperatorNontriv;
936 impreciseOp = std::make_shared<helper::ValueIterationOperator<double, false>>();
937 impreciseOp->setMatrixBackwards(this->A->template toValueType<double>(), &this->A->getRowGroupIndices());
938 if (this->choiceFixedForRowGroup) {
939 impreciseOp->setIgnoredRows(
true, fixedChoicesCallback);
941 }
else if constexpr (std::is_same_v<ValueType, double>) {
942 impreciseOp = viOperatorNontriv;
943 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, false>>();
944 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>(), &this->A->getRowGroupIndices());
945 if (this->choiceFixedForRowGroup) {
946 exactOp->setIgnoredRows(
true, fixedChoicesCallback);
951 uint64_t numIterations{0};
953 this->showProgressIterative(numIterations);
954 return this->updateStatus(current, x,
SolverGuarantee::None, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
956 this->startMeasureProgress();
957 auto status = rsHelper.RS(x, b, numIterations, storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()), dir, rsCallback);
959 this->reportStatus(status, numIterations);
962 if (this->isTrackSchedulerSet()) {
963 this->extractScheduler(x, b, dir, this->getUncertaintyResolutionMode());
966 if (!this->isCachingEnabled()) {
974template<
typename ValueType,
typename SolutionType>
976 auxiliaryRowGroupVector.reset();
977 if (viOperatorTriv) {
978 viOperatorTriv.reset();
980 if (viOperatorNontriv) {
981 viOperatorNontriv.reset();
SolverEnvironment & solver()
uint64_t const & getMaximalNumberOfIterations() const
storm::RationalNumber const & getPrecision() const
bool const & isMethodSetFromDefault() const
storm::solver::MinMaxMethod const & getMethod() const
bool isForceRequireUnique() const
bool const & getRelativeTerminationCriterion() const
std::optional< storm::RationalNumber > const & getUpperBoundGuessingFactor() const
OviSolverEnvironment const & ovi() const
MinMaxSolverEnvironment & minMax()
bool isForceExact() const
bool isForceSoundness() const
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)
Implements guessing value iteration.
Implements rational search.
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.
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)
#define STORM_LOG_WARN(message)
#define STORM_LOG_ERROR(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
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)
UncertaintyResolutionMode
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...
bool hasNonZeroEntry(std::vector< T > const &v)
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)