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.");
81 STORM_LOG_THROW(method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch ||
82 method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::IntervalIteration ||
83 method == MinMaxMethod::OptimisticValueIteration || method == MinMaxMethod::GuessingValueIteration || method == MinMaxMethod::ViToPi,
84 storm::exceptions::InvalidEnvironmentException,
"This solver does not support the selected method '" <<
toString(method) <<
"'.");
88template<
typename ValueType,
typename SolutionType>
90 std::vector<SolutionType>& x, std::vector<ValueType>
const& b)
const {
93 case MinMaxMethod::ValueIteration:
94 result = solveEquationsValueIteration(env, dir, x, b);
96 case MinMaxMethod::OptimisticValueIteration:
97 result = solveEquationsOptimisticValueIteration(env, dir, x, b);
99 case MinMaxMethod::GuessingValueIteration:
100 result = solveEquationsGuessingValueIteration(env, dir, x, b);
102 case MinMaxMethod::PolicyIteration:
103 result = solveEquationsPolicyIteration(env, dir, x, b);
105 case MinMaxMethod::RationalSearch:
106 result = solveEquationsRationalSearch(env, dir, x, b);
108 case MinMaxMethod::IntervalIteration:
109 result = solveEquationsIntervalIteration(env, dir, x, b);
111 case MinMaxMethod::SoundValueIteration:
112 result = solveEquationsSoundValueIteration(env, dir, x, b);
114 case MinMaxMethod::ViToPi:
115 result = solveEquationsViToPi(env, dir, x, b);
118 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"This solver does not implement the selected solution method");
124template<
typename ValueType,
typename SolutionType>
126 if (!viOperatorTriv && !viOperatorNontriv) {
127 if (this->A->hasTrivialRowGrouping()) {
129 viOperatorTriv = std::make_shared<helper::ValueIterationOperator<ValueType, true, SolutionType>>();
130 viOperatorTriv->setMatrixBackwards(*this->A);
131 if constexpr (!std::is_same_v<ValueType, storm::Interval>) {
136 viOperatorNontriv = std::make_shared<helper::ValueIterationOperator<ValueType, false, SolutionType>>();
137 viOperatorNontriv->setMatrixBackwards(*this->A);
141 viOperatorNontriv = std::make_shared<helper::ValueIterationOperator<ValueType, false, SolutionType>>();
142 viOperatorNontriv->setMatrixBackwards(*this->A);
145 if (this->choiceFixedForRowGroup) {
147 assert(this->initialScheduler);
148 auto callback = [&](uint64_t groupIndex, uint64_t localRowIndex) {
149 return this->choiceFixedForRowGroup->get(groupIndex) && this->initialScheduler->at(groupIndex) != localRowIndex;
151 if (viOperatorTriv) {
152 viOperatorTriv->setIgnoredRows(
true, callback);
154 if (viOperatorNontriv) {
155 viOperatorNontriv->setIgnoredRows(
true, callback);
160template<
typename ValueType,
typename SolutionType>
161void IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::extractScheduler(std::vector<SolutionType>& x, std::vector<ValueType>
const& b,
163 if (std::is_same_v<ValueType, storm::Interval> && this->A->hasTrivialRowGrouping()) {
165 if (!this->robustSchedulerIndex) {
166 this->robustSchedulerIndex = std::vector<uint64_t>(x.size(), 0);
168 this->robustSchedulerIndex->resize(x.size(), 0);
170 uint64_t numSchedulerChoices = 0;
171 for (uint64_t row = 0; row < this->A->getRowCount(); ++row) {
172 this->robustSchedulerIndex->at(row) = numSchedulerChoices;
173 numSchedulerChoices += this->A->getRow(row).getNumberOfEntries();
176 if (!this->schedulerChoices) {
177 this->schedulerChoices = std::vector<uint64_t>(numSchedulerChoices, 0);
179 this->schedulerChoices->resize(numSchedulerChoices, 0);
183 if (!this->schedulerChoices) {
184 this->schedulerChoices = std::vector<uint64_t>(x.size(), 0);
186 this->schedulerChoices->resize(x.size(), 0);
191 if (!viOperatorTriv && !viOperatorNontriv) {
192 STORM_LOG_WARN(
"Expected VI operator to be initialized for scheduler extraction. Initializing now, but this is inefficient.");
195 if (viOperatorTriv) {
196 if constexpr (std::is_same<ValueType, storm::Interval>() && std::is_same<SolutionType, double>()) {
198 schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, robust, updateX ? &x : nullptr, this->robustSchedulerIndex);
200 STORM_LOG_ERROR(
"SchedulerTrackingHelper not implemented for this setting (trivial row grouping but not Interval->double).");
203 if (viOperatorNontriv) {
205 schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, robust, updateX ? &x : nullptr, this->robustSchedulerIndex);
209template<
typename ValueType,
typename SolutionType>
210bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveInducedEquationSystem(
211 Environment
const& env, std::unique_ptr<LinearEquationSolver<SolutionType>>& linearEquationSolver, std::vector<uint64_t>
const& scheduler,
212 std::vector<SolutionType>& x, std::vector<ValueType>& subB, std::vector<ValueType>
const& originalB,
OptimizationDirection dir)
const {
213 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
214 if constexpr (std::is_same_v<SolutionType, storm::Interval>) {
216 "We did not implement solving induced equation systems for interval-based models outside of robust VI.");
220 STORM_LOG_ASSERT(subB.size() == x.size(),
"Sizes of subB and x do not coincide.");
221 STORM_LOG_ASSERT(this->linearEquationSolverFactory !=
nullptr,
"Wrong constructor was called.");
223 storm::exceptions::NotImplementedException,
"Solving induced system of Interval Model not supported for the selected equation solver");
228 auto schedulerIterator = scheduler.begin();
229 for (uint64_t rowIndex = 0; rowIndex < this->A->getRowCount(); rowIndex++) {
230 auto const& row = this->A->getRow(rowIndex);
232 static std::vector<SolutionType> tmp;
235 SolutionType probLeft = storm::utility::one<SolutionType>();
237 for (
auto const& entry : row) {
238 tmp.push_back(entry.getValue().lower());
239 probLeft -= entry.getValue().lower();
242 auto const& rowIter = row.begin();
243 for (uint64_t i = 0;
i < row.getNumberOfEntries();
i++, schedulerIterator++) {
245 auto const& entry = rowIter[*schedulerIterator];
246 auto const diameter = entry.getValue().upper() - entry.getValue().lower();
248 tmp[*schedulerIterator] += value;
255 for (uint64_t i = 0;
i < row.getNumberOfEntries();
i++) {
256 auto const& entry = rowIter[
i];
257 newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), tmp[i]);
260 STORM_LOG_ASSERT(schedulerIterator == scheduler.end(),
"Offset issue in scheduler?");
264 std::vector<SolutionType> b;
265 for (
auto const& entry : subB) {
266 if (dir == OptimizationDirection::Maximize) {
267 b.push_back(entry.upper());
269 b.push_back(entry.lower());
273 auto const submatrix = newMatrixBuilder.build();
276 if (!linearEquationSolver) {
278 linearEquationSolver = this->linearEquationSolverFactory->create(env, std::move(submatrix));
279 linearEquationSolver->setBoundsFromOtherSolver(*
this);
280 linearEquationSolver->setCachingEnabled(
true);
283 linearEquationSolver->setMatrix(std::move(submatrix));
286 return linearEquationSolver->solveEquations(env, x, b);
288 STORM_LOG_ASSERT(subB.size() == x.size(),
"Sizes of subB and x do not coincide.");
289 STORM_LOG_ASSERT(this->linearEquationSolverFactory !=
nullptr,
"Wrong constructor was called.");
296 if (convertToEquationSystem) {
299 storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, this->A->getRowGroupIndices(), originalB);
302 if (!linearEquationSolver) {
304 linearEquationSolver = this->linearEquationSolverFactory->create(env, std::move(submatrix));
305 linearEquationSolver->setBoundsFromOtherSolver(*
this);
306 linearEquationSolver->setCachingEnabled(
true);
309 linearEquationSolver->setMatrix(std::move(submatrix));
312 return linearEquationSolver->solveEquations(env, x, subB);
316template<
typename ValueType,
typename SolutionType>
317bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsPolicyIteration(Environment
const& env,
OptimizationDirection dir,
318 std::vector<SolutionType>& x,
319 std::vector<ValueType>
const& b)
const {
320 std::vector<storm::storage::sparse::state_type> scheduler =
321 this->hasInitialScheduler() ? this->getInitialScheduler() :
std::vector<
storm::storage::sparse::
state_type>(this->A->getRowGroupCount());
322 return performPolicyIteration(env, dir, x, b, std::move(scheduler));
325template<
typename ValueType,
typename SolutionType>
326bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::performPolicyIteration(
327 Environment
const& env,
OptimizationDirection dir, std::vector<SolutionType>& x, std::vector<ValueType>
const& b,
328 std::vector<storm::storage::sparse::state_type>&& initialPolicy)
const {
329 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
330 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement policy iteration for interval-based models.");
333 std::vector<storm::storage::sparse::state_type> scheduler = std::move(initialPolicy);
335 if (!auxiliaryRowGroupVector) {
336 auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
338 std::vector<ValueType>& subB = *auxiliaryRowGroupVector;
341 std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver;
343 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
344 auto precOfSolver = env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType());
346 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().minMax().getPrecision();
347 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().minMax().getRelativeTerminationCriterion();
348 if (changePrecision || changeRelative) {
349 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
350 boost::optional<storm::RationalNumber> newPrecision;
351 boost::optional<bool> newRelative;
352 if (changePrecision) {
353 newPrecision = env.solver().minMax().getPrecision();
355 if (changeRelative) {
358 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
361 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
364 uint64_t iterations = 0;
365 this->startMeasureProgress();
368 solveInducedEquationSystem(environmentOfSolver, solver, scheduler, x, subB, b, dir);
371 bool schedulerImproved =
false;
373 for (uint_fast64_t group = 0; group < this->A->getRowGroupCount(); ++group) {
374 if (!this->choiceFixedForRowGroup || !this->choiceFixedForRowGroup.get()[group]) {
376 uint_fast64_t currentChoice = scheduler[group];
377 for (uint_fast64_t choice = this->A->getRowGroupIndices()[group]; choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
379 if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
384 ValueType choiceValue = storm::utility::zero<ValueType>();
385 for (
auto const& entry : this->A->getRow(choice)) {
386 choiceValue += entry.getValue() * x[entry.getColumn()];
388 choiceValue += b[choice];
393 if (valueImproved(dir, x[group], choiceValue)) {
394 schedulerImproved =
true;
395 scheduler[group] = choice - this->A->getRowGroupIndices()[group];
396 x[group] = std::move(choiceValue);
403 if (!schedulerImproved) {
411 iterations, env.solver().minMax().getMaximalNumberOfIterations());
414 this->showProgressIterative(iterations);
418 this->reportStatus(status, iterations);
421 if (this->isTrackSchedulerSet()) {
422 this->schedulerChoices = std::move(scheduler);
425 if (!this->isCachingEnabled()) {
433template<
typename ValueType,
typename SolutionType>
434bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::valueImproved(
OptimizationDirection dir, ValueType
const& value1,
435 ValueType
const& value2)
const {
436 if (dir == OptimizationDirection::Minimize) {
437 return value2 < value1;
439 return value2 > value1;
443template<
typename ValueType,
typename SolutionType>
445 Environment const& env, boost::optional<storm::solver::OptimizationDirection>
const& direction,
bool const& hasInitialScheduler)
const {
449 bool needsLinEqSolver =
false;
450 needsLinEqSolver |= method == MinMaxMethod::PolicyIteration;
451 needsLinEqSolver |= method == MinMaxMethod::ValueIteration && (this->hasInitialScheduler() || hasInitialScheduler);
452 needsLinEqSolver |= method == MinMaxMethod::ViToPi;
455 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
456 STORM_LOG_ASSERT(!needsLinEqSolver,
"Intervals should not require a linear equation solver.");
458 }
else if (needsLinEqSolver) {
464 if (method == MinMaxMethod::ValueIteration) {
465 if (!this->hasUniqueSolution()) {
471 if (!direction || direction.get() == OptimizationDirection::Maximize) {
474 if (!direction || direction.get() == OptimizationDirection::Minimize) {
479 }
else if (method == MinMaxMethod::OptimisticValueIteration) {
481 if (!this->hasUniqueSolution()) {
486 }
else if (method == MinMaxMethod::GuessingValueIteration) {
488 if (!this->hasUniqueSolution()) {
492 }
else if (method == MinMaxMethod::IntervalIteration) {
494 if (!this->hasUniqueSolution()) {
498 }
else if (method == MinMaxMethod::RationalSearch) {
502 if (!this->hasUniqueSolution()) {
507 }
else if (method == MinMaxMethod::PolicyIteration) {
512 if (!this->hasNoEndComponents() && !this->hasInitialScheduler()) {
515 }
else if (method == MinMaxMethod::SoundValueIteration) {
516 if (!this->hasUniqueSolution()) {
520 }
else if (method == MinMaxMethod::ViToPi) {
522 if (!this->hasUniqueSolution()) {
526 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"Unsupported technique for iterative MinMax linear equation solver.");
531template<
typename ValueType,
typename SolutionType>
533 ValueType result = storm::utility::zero<ValueType>();
534 auto oldValueIt = oldValues.begin();
535 for (
auto value : relevantValues) {
536 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
542template<
typename ValueType,
typename SolutionType>
543ValueType
computeMaxAbsDiff(std::vector<ValueType>
const& allOldValues, std::vector<ValueType>
const& allNewValues,
545 ValueType result = storm::utility::zero<ValueType>();
546 for (
auto value : relevantValues) {
547 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
552template<
typename ValueType,
typename SolutionType>
554 std::vector<SolutionType>& x,
555 std::vector<ValueType>
const& b)
const {
556 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
557 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement optimistic value iteration for interval-based models.");
562 x.assign(x.size(), storm::utility::zero<SolutionType>());
563 if (this->isTrackSchedulerSet()) {
564 this->schedulerChoices = std::vector<uint_fast64_t>(x.size(), 0);
571 helper::OptimisticValueIterationHelper<ValueType, false> oviHelper(viOperatorNontriv);
573 std::optional<ValueType> lowerBound, upperBound;
574 if (this->hasLowerBound()) {
575 lowerBound = this->getLowerBound(
true);
577 if (this->hasUpperBound()) {
578 upperBound = this->getUpperBound(
true);
580 uint64_t numIterations{0};
581 auto oviCallback = [&](
SolverStatus const& current, std::vector<ValueType>
const& v) {
582 this->showProgressIterative(numIterations);
585 this->createLowerBoundsVector(x);
586 std::optional<ValueType> guessingFactor;
590 this->startMeasureProgress();
592 upperBound, oviCallback);
593 this->reportStatus(status, numIterations);
596 if (this->isTrackSchedulerSet()) {
597 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
600 if (!this->isCachingEnabled()) {
608template<
typename ValueType,
typename SolutionType>
609bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsGuessingValueIteration(Environment
const& env,
OptimizationDirection dir,
610 std::vector<SolutionType>& x,
611 std::vector<ValueType>
const& b)
const {
612 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
613 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement guessing value iteration for interval-based models.");
619 auto upperX = std::make_unique<std::vector<SolutionType>>(x.size());
623 uint64_t numIterations{0};
625 auto gviCallback = [&](helper::GVIData<ValueType>
const& data) {
626 this->showProgressIterative(numIterations);
627 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x,
SolverGuarantee::LessOrEqual) &&
629 return this->updateStatus(data.status, terminateEarly, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
632 this->createLowerBoundsVector(lowerX);
633 this->createUpperBoundsVector(*upperX);
635 this->startMeasureProgress();
636 auto statusIters = helper.solveEquations(lowerX, *upperX, b, numIterations,
637 storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()), dir, gviCallback);
638 auto two = storm::utility::convertNumber<ValueType>(2.0);
639 storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(
640 lowerX, *upperX, x, [&two](ValueType
const& a, ValueType
const& b) -> ValueType {
return (a + b) / two; });
642 this->reportStatus(statusIters, numIterations);
645 if (this->isTrackSchedulerSet()) {
646 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
649 if (!this->isCachingEnabled()) {
657template<
typename ValueType,
typename SolutionType>
658bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsValueIteration(Environment
const& env,
OptimizationDirection dir,
659 std::vector<SolutionType>& x,
660 std::vector<ValueType>
const& b)
const {
665 if (this->hasInitialScheduler()) {
666 if (!auxiliaryRowGroupVector) {
667 auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
670 std::unique_ptr<storm::solver::LinearEquationSolver<SolutionType>> linEqSolver;
672 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
673 auto precOfSolver = env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType());
675 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().minMax().getPrecision();
676 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().minMax().getRelativeTerminationCriterion();
677 if (changePrecision || changeRelative) {
678 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
679 boost::optional<storm::RationalNumber> newPrecision;
680 boost::optional<bool> newRelative;
681 if (changePrecision) {
682 newPrecision = env.solver().minMax().getPrecision();
684 if (changeRelative) {
687 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
690 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
692 bool success = solveInducedEquationSystem(environmentOfSolver, linEqSolver, this->getInitialScheduler(), x, *auxiliaryRowGroupVector, b, dir);
700 }
else if (!this->hasUniqueSolution()) {
702 this->createLowerBoundsVector(x);
705 this->createUpperBoundsVector(x);
708 }
else if (this->hasCustomTerminationCondition()) {
710 this->createLowerBoundsVector(x);
713 this->createUpperBoundsVector(x);
718 uint64_t numIterations{0};
720 this->showProgressIterative(numIterations);
721 return this->updateStatus(current, x, guarantee, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
723 this->startMeasureProgress();
725 if (this->A->hasTrivialRowGrouping()) {
728 auto status = viHelper.VI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(),
729 storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision()), dir, viCallback,
730 env.solver().minMax().getMultiplicationStyle(), this->isUncertaintyRobust());
731 this->reportStatus(status, numIterations);
734 if (this->isTrackSchedulerSet()) {
735 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
738 if (!this->isCachingEnabled()) {
746 auto status = viHelper.VI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(),
747 storm::utility::convertNumber<SolutionType>(env.solver().minMax().getPrecision()), dir, viCallback,
748 env.solver().minMax().getMultiplicationStyle(), this->isUncertaintyRobust());
749 this->reportStatus(status, numIterations);
752 if (this->isTrackSchedulerSet()) {
753 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
756 if (!this->isCachingEnabled()) {
764template<
typename ValueType,
typename SolutionType>
775template<
typename ValueType,
typename SolutionType>
777 std::vector<SolutionType>& x,
778 std::vector<ValueType>
const& b)
const {
779 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
780 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"We did not implement intervaliteration for interval-based models");
784 helper::IntervalIterationHelper<ValueType, false> iiHelper(viOperatorNontriv);
786 auto lowerBoundsCallback = [&](std::vector<SolutionType>& vector) { this->createLowerBoundsVector(vector); };
787 auto upperBoundsCallback = [&](std::vector<SolutionType>& vector) { this->createUpperBoundsVector(vector); };
789 uint64_t numIterations{0};
790 auto iiCallback = [&](helper::IIData<ValueType>
const& data) {
791 this->showProgressIterative(numIterations);
792 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x,
SolverGuarantee::LessOrEqual) &&
796 std::optional<storm::storage::BitVector> optionalRelevantValues;
797 if (this->hasRelevantValues()) {
798 optionalRelevantValues = this->getRelevantValues();
800 this->startMeasureProgress();
802 dir, iiCallback, optionalRelevantValues);
803 this->reportStatus(status, numIterations);
806 if (this->isTrackSchedulerSet()) {
807 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
810 if (!this->isCachingEnabled()) {
818template<
typename ValueType,
typename SolutionType>
819bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsSoundValueIteration(Environment
const& env,
OptimizationDirection dir,
820 std::vector<SolutionType>& x,
821 std::vector<ValueType>
const& b)
const {
822 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
823 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"SoundVI does not handle interval-based models");
827 assert(x.size() == this->A->getRowGroupCount());
829 std::optional<ValueType> lowerBound, upperBound;
830 if (this->hasLowerBound()) {
831 lowerBound = this->getLowerBound(
true);
833 if (this->hasUpperBound()) {
834 upperBound = this->getUpperBound(
true);
839 auto precision = storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision());
840 uint64_t numIterations{0};
841 auto sviCallback = [&](
typename helper::SoundValueIterationHelper<ValueType, false>::SVIData
const& current) {
842 this->showProgressIterative(numIterations);
843 return this->updateStatus(current.status,
844 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
845 numIterations, env.solver().minMax().getMaximalNumberOfIterations());
847 this->startMeasureProgress();
848 helper::SoundValueIterationHelper<ValueType, false> sviHelper(viOperatorNontriv);
849 std::optional<storm::storage::BitVector> optionalRelevantValues;
850 if (this->hasRelevantValues()) {
851 optionalRelevantValues = this->getRelevantValues();
853 auto status = sviHelper.SVI(x, b, numIterations, env.solver().minMax().getRelativeTerminationCriterion(), precision, dir, lowerBound, upperBound,
854 sviCallback, optionalRelevantValues);
857 if (this->isTrackSchedulerSet()) {
858 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
861 this->reportStatus(status, numIterations);
863 if (!this->isCachingEnabled()) {
871template<
typename ValueType,
typename SolutionType>
872bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsViToPi(Environment
const& env,
OptimizationDirection dir,
873 std::vector<SolutionType>& x, std::vector<ValueType>
const& b)
const {
874 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
875 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"ViToPi does not handle interval-based models");
879 std::vector<storm::storage::sparse::state_type> initialSched;
881 Environment viEnv = env;
882 viEnv.solver().minMax().setMethod(MinMaxMethod::ValueIteration);
883 viEnv.solver().setForceExact(
false);
884 viEnv.solver().setForceSoundness(
false);
885 auto impreciseSolver = GeneralMinMaxLinearEquationSolverFactory<double>().create(viEnv, this->A->template toValueType<double>());
886 impreciseSolver->setHasUniqueSolution(this->hasUniqueSolution());
887 impreciseSolver->setTrackScheduler(
true);
888 if (this->hasInitialScheduler()) {
889 auto initSched = this->getInitialScheduler();
890 impreciseSolver->setInitialScheduler(std::move(initSched));
892 auto impreciseSolverReq = impreciseSolver->getRequirements(viEnv, dir);
893 STORM_LOG_THROW(!impreciseSolverReq.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
894 "The value-iteration based solver has an unmet requirement: " << impreciseSolverReq.getEnabledRequirementsAsString());
895 impreciseSolver->setRequirementsChecked(
true);
896 auto xVi = storm::utility::vector::convertNumericVector<double>(x);
897 auto bVi = storm::utility::vector::convertNumericVector<double>(b);
898 impreciseSolver->solveEquations(viEnv, dir, xVi, bVi);
899 initialSched = impreciseSolver->getSchedulerChoices();
901 STORM_LOG_INFO(
"Found initial policy using Value Iteration. Starting Policy iteration now.");
902 return performPolicyIteration(env, dir, x, b, std::move(initialSched));
905template<
typename ValueType,
typename SolutionType>
906bool IterativeMinMaxLinearEquationSolver<ValueType, SolutionType>::solveEquationsRationalSearch(Environment
const& env,
OptimizationDirection dir,
907 std::vector<SolutionType>& x,
908 std::vector<ValueType>
const& b)
const {
909 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
910 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Rational search does not handle interval-based models");
915 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, false>> exactOp;
916 std::shared_ptr<helper::ValueIterationOperator<double, false>> impreciseOp;
917 std::function<bool(uint64_t, uint64_t)> fixedChoicesCallback;
918 if (this->choiceFixedForRowGroup) {
920 assert(this->initialScheduler);
921 fixedChoicesCallback = [&](uint64_t groupIndex, uint64_t localRowIndex) {
922 return this->choiceFixedForRowGroup->get(groupIndex) && this->initialScheduler->at(groupIndex) != localRowIndex;
926 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
927 exactOp = viOperatorNontriv;
928 impreciseOp = std::make_shared<helper::ValueIterationOperator<double, false>>();
929 impreciseOp->setMatrixBackwards(this->A->template toValueType<double>(), &this->A->getRowGroupIndices());
930 if (this->choiceFixedForRowGroup) {
931 impreciseOp->setIgnoredRows(
true, fixedChoicesCallback);
933 }
else if constexpr (std::is_same_v<ValueType, double>) {
934 impreciseOp = viOperatorNontriv;
935 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, false>>();
936 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>(), &this->A->getRowGroupIndices());
937 if (this->choiceFixedForRowGroup) {
938 exactOp->setIgnoredRows(
true, fixedChoicesCallback);
943 uint64_t numIterations{0};
945 this->showProgressIterative(numIterations);
946 return this->updateStatus(current, x,
SolverGuarantee::None, numIterations, env.solver().minMax().getMaximalNumberOfIterations());
948 this->startMeasureProgress();
949 auto status = rsHelper.RS(x, b, numIterations, storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()), dir, rsCallback);
951 this->reportStatus(status, numIterations);
954 if (this->isTrackSchedulerSet()) {
955 this->extractScheduler(x, b, dir, this->isUncertaintyRobust());
958 if (!this->isCachingEnabled()) {
966template<
typename ValueType,
typename SolutionType>
968 auxiliaryRowGroupVector.reset();
969 if (viOperatorTriv) {
970 viOperatorTriv.reset();
972 if (viOperatorNontriv) {
973 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)
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)