14template<
typename ValueType,
bool TrivialRowGrouping>
17 : viOperator(viOperator) {
18 sizeOfLargestRowGroup = 1;
19 if constexpr (!TrivialRowGrouping) {
20 auto it = viOperator->getRowGroupIndices().cbegin();
21 auto itEnd = viOperator->getRowGroupIndices().cend() - 1;
23 auto const curr = *it;
24 sizeOfLargestRowGroup = std::max(sizeOfLargestRowGroup, *(++it) - curr);
31template<
typename ValueType, OptimizationDirection Dir, SVIStage Stage,
bool TrivialRowGrouping>
38 std::optional<ValueType>
const&
d = {})
39 : currRowValues(rowValueStorage) {
57 void firstRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
58 assert(currRowValuesIndex == 0);
59 if constexpr (!TrivialRowGrouping) {
62 best = std::move(value);
65 void nextRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
66 assert(!TrivialRowGrouping);
67 assert(currRowValuesIndex < currRowValues.size());
69 if (value.second > best.second || (value.second == best.second && better(value.first, best.first))) {
70 std::swap(value, best);
72 currRowValues[currRowValuesIndex++] = std::move(value);
74 assert(!bValue.
empty());
76 if (bestValue.
empty()) {
77 bestValue = best.first +
b * best.second;
79 if (ValueType currentValue = value.first +
b * value.second; bestValue &= currentValue) {
80 std::swap(value, best);
83 currRowValues[currRowValuesIndex++] = std::move(value);
85 }
else if (best.second > value.second) {
86 if (*bestValue == currentValue) {
88 std::swap(value, best);
92 currRowValues[currRowValuesIndex++] = std::move(value);
98 void applyUpdate(ValueType& xCurr, ValueType& yCurr, [[maybe_unused]] uint64_t rowGroup) {
99 std::swap(xCurr, best.first);
100 std::swap(yCurr, best.second);
103 while (currRowValuesIndex) {
104 if (
auto const& rowVal = currRowValues[--currRowValuesIndex]; yCurr > rowVal.second) {
105 dValue &= (rowVal.first - xCurr) / (yCurr - rowVal.second);
109 assert(currRowValuesIndex == 0);
115 if (yCurr < storm::utility::one<ValueType>()) {
116 ValueType val = xCurr / (storm::utility::one<ValueType>() - yCurr);
124 STORM_LOG_ASSERT(yCurr < storm::utility::one<ValueType>(),
"Unexpected y value for this stage.");
125 ValueType val = xCurr / (storm::utility::one<ValueType>() - yCurr);
137 aValue &= std::move(*curr_a);
140 bValue &= std::move(*curr_b);
141 if (!dValue.
empty() && *bValue == *dValue) {
148 bValue &= std::move(*curr_b);
161 std::optional<ValueType>
a()
const {
165 std::optional<ValueType>
b()
const {
169 std::optional<ValueType>
d()
const {
174 return nextStage != Stage;
177 template<SVIStage NewStage>
179 std::optional<ValueType>
d;
192 static bool better(ValueType
const& lhs, ValueType
const& rhs) {
203 ExtremumDir aValue, dValue;
204 ExtremumInvDir bValue;
209 ExtremumInvDir curr_a;
212 std::pair<ValueType, ValueType> best;
213 ExtremumDir bestValue;
215 uint64_t currRowValuesIndex{0};
218template<
typename ValueType,
bool TrivialRowGrouping>
220 if (
a.has_value() &&
b.has_value()) {
221 ValueType abAvg = (*
a + *
b) / storm::utility::convertNumber<ValueType, uint64_t>(2);
223 [&abAvg](ValueType
const& xVal, ValueType
const& yVal) -> ValueType { return xVal + abAvg * yVal; });
227template<
typename ValueType,
bool TrivialRowGrouping>
229 std::vector<ValueType>& upperOut)
const {
230 auto [min, max] = std::minmax(*a, *b);
231 uint64_t
const size = xy.first.size();
232 for (uint64_t i = 0; i < size; ++i) {
235 ValueType xi = xy.first[i];
236 ValueType yi = xy.second[i];
237 lowerOut[i] = xi + min * yi;
238 upperOut[i] = xi + max * yi;
242template<
typename ValueType,
bool TrivialRowGrouping>
245 if (a.has_value() && b.has_value()) {
247 auto max = std::max(*a, *b);
250 auto min = std::min(*a, *b);
257template<
typename ValueType,
bool TrivialRowGrouping>
259 std::function<
void()>
const& getNextConvergenceCheckState,
260 bool relative, ValueType
const& precision)
const {
261 if (!a.has_value() || !b.has_value())
266 auto [min, max] = std::minmax(*a, *b);
267 if (min >= storm::utility::zero<ValueType>()) {
268 ValueType
const val = (max - min) / precision - min;
269 for (; convergenceCheckState < xy.first.size(); getNextConvergenceCheckState()) {
270 if (!
storm::utility::isZero(xy.second[convergenceCheckState]) && val > xy.first[convergenceCheckState] / xy.second[convergenceCheckState]) {
274 }
else if (max <= storm::utility::zero<ValueType>()) {
275 ValueType
const val = (min - max) / precision - max;
276 for (; convergenceCheckState < xy.first.size(); getNextConvergenceCheckState()) {
277 if (!
storm::utility::isZero(xy.second[convergenceCheckState]) && val < xy.first[convergenceCheckState] / xy.second[convergenceCheckState]) {
282 for (; convergenceCheckState < xy.first.size(); getNextConvergenceCheckState()) {
283 ValueType l = xy.first[convergenceCheckState] + min * xy.second[convergenceCheckState];
284 ValueType u = xy.first[convergenceCheckState] + max * xy.second[convergenceCheckState];
286 if (l > storm::utility::zero<ValueType>()) {
287 if ((u - l) > l * precision) {
290 }
else if (u < storm::utility::zero<ValueType>()) {
291 if ((l - u) < u * precision) {
302 ValueType val = precision / storm::utility::abs<ValueType>(*b - *a);
303 for (; convergenceCheckState < xy.first.size(); getNextConvergenceCheckState()) {
304 if (xy.second[convergenceCheckState] > val) {
312template<
typename ValueType,
bool TrivialRowGrouping>
313template<
typename BackendType>
315 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::pair<std::vector<ValueType>
const*, ValueType>
const& offsets, uint64_t& numIterations,
316 bool relative, ValueType
const& precision, BackendType&& backend, std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback,
317 std::optional<storm::storage::BitVector>
const& relevantValues, uint64_t convergenceCheckState)
const {
319 xy.first.assign(xy.first.size(), storm::utility::zero<ValueType>());
320 xy.second.assign(xy.first.size(), storm::utility::one<ValueType>());
321 convergenceCheckState = relevantValues.has_value() ? relevantValues->getNextSetIndex(0ull) : 0ull;
323 std::function<void()> getNextConvergenceCheckState;
324 if (relevantValues) {
325 getNextConvergenceCheckState = [&convergenceCheckState, &relevantValues]() {
326 convergenceCheckState = relevantValues->getNextSetIndex(++convergenceCheckState);
329 getNextConvergenceCheckState = [&convergenceCheckState]() { ++convergenceCheckState; };
334 viOperator->applyInPlace(xy, offsets, backend);
336 if (data.checkConvergence(convergenceCheckState, getNextConvergenceCheckState, relative, precision)) {
339 if (iterationCallback) {
341 data.status = iterationCallback(data);
346 if (backend.moveToNextStage()) {
347 switch (backend.getNextStage()) {
349 return SVI(xy, offsets, numIterations, relative, precision, backend.template createBackendForNextStage<SVIStage::y_less_1>(),
350 iterationCallback, relevantValues, convergenceCheckState);
352 return SVI(xy, offsets, numIterations, relative, precision, backend.template createBackendForNextStage<SVIStage::b_eq_d>(),
353 iterationCallback, relevantValues, convergenceCheckState);
362template<
typename ValueType,
bool TrivialRowGrouping>
363template<storm::OptimizationDirection Dir>
365 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::pair<std::vector<ValueType>
const*, ValueType>
const& offsets, uint64_t& numIterations,
366 bool relative, ValueType
const& precision, std::optional<ValueType>
const& a, std::optional<ValueType>
const& b,
367 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
369 rowValueStorage.resize(sizeOfLargestRowGroup - 1);
370 return SVI(xy, offsets, numIterations, relative, precision,
SVIBackend<ValueType, Dir, SVIStage::Initial, TrivialRowGrouping>(rowValueStorage, a, b),
371 iterationCallback, relevantValues);
374template<
typename ValueType,
bool TrivialRowGrouping>
376 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative,
377 ValueType
const& precision, std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound,
378 std::optional<ValueType>
const& upperBound, std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback,
379 std::optional<storm::storage::BitVector>
const& relevantValues)
const {
380 std::pair<std::vector<ValueType>
const*, ValueType> offsetsPair{&offsets, storm::utility::zero<ValueType>()};
381 if (!dir.has_value() ||
maximize(*dir)) {
383 return SVI<storm::OptimizationDirection::Maximize>(xy, offsetsPair, numIterations, relative, precision, lowerBound, upperBound, iterationCallback,
387 return SVI<storm::OptimizationDirection::Minimize>(xy, offsetsPair, numIterations, relative, precision, upperBound, lowerBound, iterationCallback,
392template<
typename ValueType,
bool TrivialRowGrouping>
394 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative, ValueType
const& precision,
395 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
396 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
398 std::pair<std::vector<ValueType>, std::vector<ValueType>> xy;
399 auto& auxVector = viOperator->allocateAuxiliaryVector(operand.size());
400 xy.first.swap(operand);
401 xy.second.swap(auxVector);
402 auto doublePrec = precision + precision;
403 if constexpr (std::is_same_v<ValueType, double>) {
404 doublePrec -= precision * 1e-6;
406 auto res =
SVI(xy, offsets, numIterations, relative, doublePrec, dir, lowerBound, upperBound, iterationCallback, relevantValues);
407 res.trySetAverage(xy.first);
409 xy.first.swap(operand);
410 xy.second.swap(auxVector);
411 viOperator->freeAuxiliaryVector();
415template<
typename ValueType,
bool TrivialRowGrouping>
417 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets,
bool relative, ValueType
const& precision,
418 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
419 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
420 uint64_t numIterations = 0;
421 return SVI(operand, offsets, numIterations, relative, precision, dir, lowerBound, upperBound, iterationCallback, relevantValues);
virtual bool terminateNow(std::vector< ValueType > const ¤tValues, SolverGuarantee const &guarantee=SolverGuarantee::None) const
Retrieves whether the guarantee provided by the solver for the current result is sufficient to termin...
virtual bool requiresGuarantee(SolverGuarantee const &guarantee) const =0
Retrieves whether the termination criterion requires the given guarantee in order to decide terminati...
bool constexpr abort() const
auto createBackendForNextStage() const
static const SVIStage CurrentStage
std::optional< ValueType > d() const
SVIBackend(RowValueStorageType &rowValueStorage, std::optional< ValueType > const &a, std::optional< ValueType > const &b, std::optional< ValueType > const &d={})
void nextRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
std::optional< ValueType > a() const
bool constexpr converged() const
SVIStage const & getNextStage() const
bool moveToNextStage() const
void firstRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
std::vector< std::pair< ValueType, ValueType > > RowValueStorageType
void applyUpdate(ValueType &xCurr, ValueType &yCurr, uint64_t rowGroup)
std::optional< ValueType > b() const
Implements sound value iteration.
SVIData SVI(std::pair< std::vector< ValueType >, std::vector< ValueType > > &xy, std::pair< std::vector< ValueType > const *, ValueType > const &offsets, uint64_t &numIterations, bool relative, ValueType const &precision, BackendType &&backend, std::function< SolverStatus(SVIData const &)> const &iterationCallback, std::optional< storm::storage::BitVector > const &relevantValues, uint64_t convergenceCheckState=0) const
SoundValueIterationHelper(std::shared_ptr< ValueIterationOperator< ValueType, TrivialRowGrouping > > viOperator)
This class represents the Value Iteration Operator (also known as Bellman operator).
Stores and manages an extremal (maximal or minimal) value.
std::optional< ValueType > getOptionalValue() const
void reset()
Forgets the extremal value so that this represents the extremum over an empty set.
#define STORM_LOG_ASSERT(cond, message)
bool constexpr maximize(OptimizationDirection d)
OptimizationDirection constexpr invert(OptimizationDirection d)
bool constexpr minimize(OptimizationDirection d)
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...
bool isZero(ValueType const &a)
bool checkConvergence(uint64_t &convergenceCheckState, std::function< void()> const &getNextConvergenceCheckState, bool relative, ValueType const &precision) const
void trySetLowerUpper(std::vector< ValueType > &lowerOut, std::vector< ValueType > &upperOut) const
std::optional< ValueType > const b
bool checkCustomTerminationCondition(storm::solver::TerminationCondition< ValueType > const &condition) const
void trySetAverage(std::vector< ValueType > &out) const
std::pair< std::vector< ValueType >, std::vector< ValueType > > const & xy
std::optional< ValueType > const a