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 if (!
a.has_value() || !
b.has_value()) {
234 auto [min, max] = std::minmax(*
a, *
b);
235 uint64_t
const size =
xy.first.size();
236 for (uint64_t i = 0; i < size; ++i) {
239 ValueType xi =
xy.first[i];
240 ValueType yi =
xy.second[i];
241 lowerOut[i] = xi + min * yi;
242 upperOut[i] = xi + max * yi;
247template<
typename ValueType,
bool TrivialRowGrouping>
250 if (
a.has_value() &&
b.has_value()) {
252 auto max = std::max(*
a, *
b);
255 auto min = std::min(*
a, *
b);
262template<
typename ValueType,
bool TrivialRowGrouping>
264 std::function<
void()>
const& getNextConvergenceCheckState,
265 bool relative, ValueType
const& precision)
const {
266 if (!
a.has_value() || !
b.has_value()) {
273 auto [min, max] = std::minmax(*
a, *
b);
275 ValueType
const val = (max - min) / precision - min;
276 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
277 if (!
storm::utility::isZero(
xy.second[convergenceCheckState]) && val >
xy.first[convergenceCheckState] /
xy.second[convergenceCheckState]) {
282 ValueType
const val = (min - max) / precision - max;
283 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
284 if (!
storm::utility::isZero(
xy.second[convergenceCheckState]) && val <
xy.first[convergenceCheckState] /
xy.second[convergenceCheckState]) {
289 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
290 ValueType l =
xy.first[convergenceCheckState] + min *
xy.second[convergenceCheckState];
291 ValueType u =
xy.first[convergenceCheckState] + max *
xy.second[convergenceCheckState];
294 if ((u - l) > l * precision) {
298 if ((l - u) < u * precision) {
310 for (; convergenceCheckState <
xy.first.size(); getNextConvergenceCheckState()) {
311 if (
xy.second[convergenceCheckState] > val) {
319template<
typename ValueType,
bool TrivialRowGrouping>
320template<
typename BackendType>
322 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::pair<std::vector<ValueType>
const*, ValueType>
const& offsets, uint64_t& numIterations,
323 bool relative, ValueType
const& precision, BackendType&& backend, std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback,
324 std::optional<storm::storage::BitVector>
const& relevantValues, uint64_t convergenceCheckState)
const {
328 convergenceCheckState = relevantValues.has_value() ? relevantValues->getNextSetIndex(0ull) : 0ull;
330 std::function<void()> getNextConvergenceCheckState;
331 if (relevantValues) {
332 getNextConvergenceCheckState = [&convergenceCheckState, &relevantValues]() {
333 convergenceCheckState = relevantValues->getNextSetIndex(++convergenceCheckState);
336 getNextConvergenceCheckState = [&convergenceCheckState]() { ++convergenceCheckState; };
341 viOperator->applyInPlace(xy, offsets, backend);
343 if (data.
checkConvergence(convergenceCheckState, getNextConvergenceCheckState, relative, precision)) {
346 if (iterationCallback) {
348 data.
status = iterationCallback(data);
353 if (backend.moveToNextStage()) {
354 switch (backend.getNextStage()) {
356 return SVI(xy, offsets, numIterations, relative, precision, backend.template createBackendForNextStage<SVIStage::y_less_1>(),
357 iterationCallback, relevantValues, convergenceCheckState);
359 return SVI(xy, offsets, numIterations, relative, precision, backend.template createBackendForNextStage<SVIStage::b_eq_d>(),
360 iterationCallback, relevantValues, convergenceCheckState);
369template<
typename ValueType,
bool TrivialRowGrouping>
370template<storm::OptimizationDirection Dir>
372 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::pair<std::vector<ValueType>
const*, ValueType>
const& offsets, uint64_t& numIterations,
373 bool relative, ValueType
const& precision, std::optional<ValueType>
const& a, std::optional<ValueType>
const& b,
374 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues)
const {
376 rowValueStorage.resize(sizeOfLargestRowGroup - 1);
377 return SVI(xy, offsets, numIterations, relative, precision,
SVIBackend<ValueType, Dir, SVIStage::Initial, TrivialRowGrouping>(rowValueStorage, a, b),
378 iterationCallback, relevantValues);
381template<
typename ValueType,
bool TrivialRowGrouping>
383 std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative,
384 ValueType
const& precision, std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound,
385 std::optional<ValueType>
const& upperBound, std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback,
386 std::optional<storm::storage::BitVector>
const& relevantValues)
const {
388 if (!dir.has_value() ||
maximize(*dir)) {
399template<
typename ValueType,
bool TrivialRowGrouping>
401 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative, ValueType
const& precision,
402 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
403 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues,
406 std::pair<std::vector<ValueType>, std::vector<ValueType>> xy;
407 auto& auxVector = viOperator->allocateAuxiliaryVector(operand.size());
408 xy.first.swap(operand);
409 xy.second.swap(auxVector);
410 auto doublePrec = precision + precision;
411 if constexpr (std::is_same_v<ValueType, double>) {
412 doublePrec -= precision * 1e-6;
414 auto res =
SVI(xy, offsets, numIterations, relative, doublePrec, dir, lowerBound, upperBound, iterationCallback, relevantValues);
415 if (solutionBounds.has_value()) {
417 std::vector<ValueType>& lower = solutionBounds->lower.emplace(xy.first.size());
418 std::vector<ValueType>& upper = solutionBounds->upper.emplace(xy.first.size());
419 if (!res.trySetLowerUpper(lower, upper)) {
420 solutionBounds->clear();
423 res.trySetAverage(xy.first);
425 xy.first.swap(operand);
426 xy.second.swap(auxVector);
427 viOperator->freeAuxiliaryVector();
431template<
typename ValueType,
bool TrivialRowGrouping>
433 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets,
bool relative, ValueType
const& precision,
434 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
435 std::function<
SolverStatus(
SVIData const&)>
const& iterationCallback, std::optional<storm::storage::BitVector>
const& relevantValues,
437 uint64_t numIterations = 0;
438 return SVI(operand, offsets, numIterations, relative, precision, dir, lowerBound, upperBound, iterationCallback, relevantValues, solutionBounds);
Helper class that optionally holds a reference to an object of type T.
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)
Sound lower and upper bounds on a solution vector.
bool checkConvergence(uint64_t &convergenceCheckState, std::function< void()> const &getNextConvergenceCheckState, bool relative, ValueType const &precision) const
std::optional< ValueType > const b
bool trySetLowerUpper(std::vector< ValueType > &lowerOut, std::vector< ValueType > &upperOut) const
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