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 STORM_LOG_ASSERT(currRowValuesIndex == 0,
"Expected currRowValuesIndex to be 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) {
67 STORM_LOG_ASSERT(currRowValuesIndex < currRowValues.size(),
"CurrRowValuesIndex out of range.");
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);
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 STORM_LOG_ASSERT(currRowValuesIndex == 0,
"Expected currRowValuesIndex to be 0.");
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 {
162 return aValue.getOptionalValue();
165 std::optional<ValueType>
b()
const {
166 return bValue.getOptionalValue();
169 std::optional<ValueType>
d()
const {
170 return dValue.getOptionalValue();
174 return nextStage != Stage;
177 template<SVIStage NewStage>
179 std::optional<ValueType>
d;
193 static bool better(ValueType
const& lhs, ValueType
const& rhs) {
204 ExtremumDir aValue, dValue;
205 ExtremumInvDir bValue;
210 ExtremumInvDir curr_a;
213 std::pair<ValueType, ValueType> best;
214 ExtremumDir bestValue;
216 uint64_t currRowValuesIndex{0};
219template<
typename ValueType,
bool TrivialRowGrouping>
221 if (
a.has_value() &&
b.has_value()) {
224 [&abAvg](ValueType
const& xVal, ValueType
const& yVal) -> ValueType { return xVal + abAvg * yVal; });
228template<
typename ValueType,
bool TrivialRowGrouping>
230 std::vector<ValueType>& upperOut)
const {
231 auto [min, max] = std::minmax(*
a, *
b);
232 uint64_t
const size =
xy.first.size();
233 for (uint64_t i = 0; i < size; ++i) {
236 ValueType xi =
xy.first[i];
237 ValueType yi =
xy.second[i];
238 lowerOut[i] = xi + min * yi;
239 upperOut[i] = xi + max * yi;
243template<
typename ValueType,
bool TrivialRowGrouping>
246 if (
a.has_value() &&
b.has_value()) {
248 auto max = std::max(*
a, *
b);
251 auto min = std::min(*
a, *
b);
258template<
typename ValueType,
bool TrivialRowGrouping>
260 std::function<
void()>
const& getNextConvergenceCheckState,
261 bool relative, ValueType
const& precision)
const {
262 if (!
a.has_value() || !
b.has_value()) {
269 auto [min, max] = std::minmax(*
a, *
b);
271 ValueType
const val = (max - min) / precision - min;
272 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
273 if (!
storm::utility::isZero(
xy.second[convergenceCheckState]) && val >
xy.first[convergenceCheckState] /
xy.second[convergenceCheckState]) {
278 ValueType
const val = (min - max) / precision - max;
279 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
280 if (!
storm::utility::isZero(
xy.second[convergenceCheckState]) && val <
xy.first[convergenceCheckState] /
xy.second[convergenceCheckState]) {
285 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
286 ValueType l =
xy.first[convergenceCheckState] + min *
xy.second[convergenceCheckState];
287 ValueType u =
xy.first[convergenceCheckState] + max *
xy.second[convergenceCheckState];
290 if ((u - l) > l * precision) {
294 if ((l - u) < u * precision) {
306 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
307 if (
xy.second[convergenceCheckState] > val) {
315template<
typename ValueType,
bool TrivialRowGrouping>
316template<
typename BackendType>
318 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::pair<std::vector<ValueType>
const*, ValueType>
const& offsets, uint64_t& numIterations,
319 bool relative, ValueType
const& precision, BackendType&& backend, std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback,
320 std::optional<storm::storage::BitVector>
const& relevantValues, uint64_t convergenceCheckState)
const {
324 convergenceCheckState = relevantValues.has_value() ? relevantValues->getNextSetIndex(0ull) : 0ull;
326 std::function<void()> getNextConvergenceCheckState;
327 if (relevantValues) {
328 getNextConvergenceCheckState = [&convergenceCheckState, &relevantValues]() {
329 convergenceCheckState = relevantValues->getNextSetIndex(++convergenceCheckState);
332 getNextConvergenceCheckState = [&convergenceCheckState]() { ++convergenceCheckState; };
337 viOperator->applyInPlace(xy, offsets, backend);
339 if (data.
checkConvergence(convergenceCheckState, getNextConvergenceCheckState, relative, precision)) {
342 if (iterationCallback) {
344 data.
status = iterationCallback(data);
349 if (backend.moveToNextStage()) {
350 switch (backend.getNextStage()) {
352 return SVI(xy, offsets, numIterations, relative, precision, backend.template createBackendForNextStage<SVIStage::y_less_1>(),
353 iterationCallback, relevantValues, convergenceCheckState);
355 return SVI(xy, offsets, numIterations, relative, precision, backend.template createBackendForNextStage<SVIStage::b_eq_d>(),
356 iterationCallback, relevantValues, convergenceCheckState);
365template<
typename ValueType,
bool TrivialRowGrouping>
366template<storm::OptimizationDirection Dir>
368 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::pair<std::vector<ValueType>
const*, ValueType>
const& offsets, uint64_t& numIterations,
369 bool relative, ValueType
const& precision, std::optional<ValueType>
const& a, std::optional<ValueType>
const& b,
370 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
372 rowValueStorage.resize(sizeOfLargestRowGroup - 1);
373 return SVI(xy, offsets, numIterations, relative, precision,
SVIBackend<ValueType, Dir, SVIStage::Initial, TrivialRowGrouping>(rowValueStorage, a, b),
374 iterationCallback, relevantValues);
377template<
typename ValueType,
bool TrivialRowGrouping>
379 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative,
380 ValueType
const& precision, std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound,
381 std::optional<ValueType>
const& upperBound, std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback,
382 std::optional<storm::storage::BitVector>
const& relevantValues)
const {
384 if (!dir.has_value() ||
maximize(*dir)) {
395template<
typename ValueType,
bool TrivialRowGrouping>
397 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative, ValueType
const& precision,
398 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
399 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
401 std::pair<std::vector<ValueType>, std::vector<ValueType>> xy;
402 auto& auxVector = viOperator->allocateAuxiliaryVector(operand.size());
403 xy.first.swap(operand);
404 xy.second.swap(auxVector);
405 auto doublePrec = precision + precision;
406 if constexpr (std::is_same_v<ValueType, double>) {
407 doublePrec -= precision * 1e-6;
409 auto res =
SVI(xy, offsets, numIterations, relative, doublePrec, dir, lowerBound, upperBound, iterationCallback, relevantValues);
410 res.trySetAverage(xy.first);
412 xy.first.swap(operand);
413 xy.second.swap(auxVector);
414 viOperator->freeAuxiliaryVector();
418template<
typename ValueType,
bool TrivialRowGrouping>
420 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets,
bool relative, ValueType
const& precision,
421 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
422 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
423 uint64_t numIterations = 0;
424 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
std::vector< std::pair< ValueType, ValueType > > RowValueStorageType
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)
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.
#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)
ValueType abs(ValueType const &number)
TargetType convertNumber(SourceType const &number)
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