Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
IntervalterationHelper.cpp
Go to the documentation of this file.
2
3#include <type_traits>
4
10
11namespace storm::solver::helper {
12
13template<typename ValueType, bool TrivialRowGrouping>
16 : viOperator(viOperator) {
17 // Intentionally left empty.
18}
19
20template<typename ValueType, OptimizationDirection Dir>
21class IIBackend {
22 public:
24
25 void firstRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
26 xBest = std::move(value.first);
27 yBest = std::move(value.second);
28 }
29
30 void nextRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
31 xBest &= std::move(value.first);
32 yBest &= std::move(value.second);
33 }
34
35 void applyUpdate(ValueType& xCurr, ValueType& yCurr, [[maybe_unused]] uint64_t rowGroup) {
36 xCurr = std::max(xCurr, *xBest);
37 yCurr = std::min(yCurr, *yBest);
38 }
39
40 void endOfIteration() const {
41 // intentionally left empty.
42 }
43
44 bool constexpr converged() const {
45 return false;
46 }
47
48 bool constexpr abort() const {
49 return false;
50 }
51
52 private:
54};
55
56template<typename ValueType>
57bool checkConvergence(std::pair<std::vector<ValueType>, std::vector<ValueType>> const& xy, uint64_t& convergenceCheckState,
58 std::function<void()> const& getNextConvergenceCheckState, bool relative, ValueType const& precision) {
59 if (relative) {
60 for (; convergenceCheckState < xy.first.size(); getNextConvergenceCheckState()) {
61 ValueType const& l = xy.first[convergenceCheckState];
62 ValueType const& u = xy.second[convergenceCheckState];
63 if (l > storm::utility::zero<ValueType>()) {
64 if ((u - l) > l * precision) {
65 return false;
66 }
67 } else if (u < storm::utility::zero<ValueType>()) {
68 if ((l - u) < u * precision) {
69 return false;
70 }
71 } else { // l <= 0 <= u
72 if (l != u) {
73 return false;
74 }
75 }
76 }
77 } else {
78 for (; convergenceCheckState < xy.first.size(); getNextConvergenceCheckState()) {
79 if (xy.second[convergenceCheckState] - xy.first[convergenceCheckState] > precision) {
80 return false;
81 }
82 }
83 }
84 return true;
85}
86
87template<typename ValueType, bool TrivialRowGrouping>
88template<OptimizationDirection Dir>
89SolverStatus IntervalIterationHelper<ValueType, TrivialRowGrouping>::II(std::pair<std::vector<ValueType>, std::vector<ValueType>>& xy,
90 std::vector<ValueType> const& offsets, uint64_t& numIterations, bool relative,
91 ValueType const& precision,
92 std::function<SolverStatus(IIData<ValueType> const&)> const& iterationCallback,
93 std::optional<storm::storage::BitVector> const& relevantValues) const {
96 uint64_t convergenceCheckState = 0;
97 std::function<void()> getNextConvergenceCheckState;
98 if (relevantValues) {
99 convergenceCheckState = relevantValues->getNextSetIndex(0);
100 getNextConvergenceCheckState = [&convergenceCheckState, &relevantValues]() {
101 convergenceCheckState = relevantValues->getNextSetIndex(++convergenceCheckState);
102 };
103 } else {
104 getNextConvergenceCheckState = [&convergenceCheckState]() { ++convergenceCheckState; };
105 }
106 while (status == SolverStatus::InProgress) {
107 ++numIterations;
108 viOperator->applyInPlace(xy, offsets, backend);
109 if (checkConvergence(xy, convergenceCheckState, getNextConvergenceCheckState, relative, precision)) {
111 } else if (iterationCallback) {
112 status = iterationCallback(IIData<ValueType>({xy.first, xy.second, status}));
113 }
114 }
115 return status;
116}
117
118template<typename ValueType, bool TrivialRowGrouping>
119SolverStatus IntervalIterationHelper<ValueType, TrivialRowGrouping>::II(std::vector<ValueType>& operand, std::vector<ValueType> const& offsets,
120 uint64_t& numIterations, bool relative, ValueType const& precision,
121 std::function<void(std::vector<ValueType>&)> const& prepareLowerBounds,
122 std::function<void(std::vector<ValueType>&)> const& prepareUpperBounds,
123 std::optional<storm::OptimizationDirection> const& dir,
124 std::function<SolverStatus(IIData<ValueType> const&)> const& iterationCallback,
125 std::optional<storm::storage::BitVector> const& relevantValues) const {
126 // Create two vectors x and y using the given operand plus an auxiliary vector.
127 std::pair<std::vector<ValueType>, std::vector<ValueType>> xy;
128 auto& auxVector = viOperator->allocateAuxiliaryVector(operand.size());
129 xy.first.swap(operand);
130 xy.second.swap(auxVector);
131 prepareLowerBounds(xy.first);
132 prepareUpperBounds(xy.second);
133 auto doublePrec = precision + precision;
134 if constexpr (std::is_same_v<ValueType, double>) {
135 doublePrec -= precision * 1e-6; // be slightly more precise to avoid a good chunk of floating point issues
136 }
137 SolverStatus status;
138 if (!dir.has_value() || maximize(*dir)) {
139 status = II<OptimizationDirection::Maximize>(xy, offsets, numIterations, relative, precision, iterationCallback, relevantValues);
140 } else {
141 status = II<OptimizationDirection::Minimize>(xy, offsets, numIterations, relative, precision, iterationCallback, relevantValues);
142 }
143 auto two = storm::utility::convertNumber<ValueType>(2.0);
144 // get the average of lower- and upper result
145 storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(
146 xy.first, xy.second, xy.first, [&two](ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });
147 // Swap operand and aux vector back to original positions.
148 xy.first.swap(operand);
149 xy.second.swap(auxVector);
150 viOperator->freeAuxiliaryVector();
151 return status;
152}
153
154template<typename ValueType, bool TrivialRowGrouping>
155SolverStatus IntervalIterationHelper<ValueType, TrivialRowGrouping>::II(std::vector<ValueType>& operand, std::vector<ValueType> const& offsets, bool relative,
156 ValueType const& precision,
157 std::function<void(std::vector<ValueType>&)> const& prepareLowerBounds,
158 std::function<void(std::vector<ValueType>&)> const& prepareUpperBounds,
159 std::optional<storm::OptimizationDirection> const& dir,
160 std::function<SolverStatus(IIData<ValueType> const&)> const& iterationCallback,
161 std::optional<storm::storage::BitVector> const& relevantValues) const {
162 uint64_t numIterations = 0;
163 return II(operand, offsets, numIterations, relative, precision, prepareLowerBounds, prepareUpperBounds, dir, iterationCallback, relevantValues);
164}
165
170
171} // namespace storm::solver::helper
void applyUpdate(ValueType &xCurr, ValueType &yCurr, uint64_t rowGroup)
void nextRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
void firstRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
IntervalIterationHelper(std::shared_ptr< ValueIterationOperator< ValueType, TrivialRowGrouping > > viOperator)
SolverStatus II(std::pair< std::vector< ValueType >, std::vector< ValueType > > &xy, std::vector< ValueType > const &offsets, uint64_t &numIterations, bool relative, ValueType const &precision, std::function< SolverStatus(IIData< ValueType > const &)> const &iterationCallback={}, std::optional< storm::storage::BitVector > const &relevantValues={}) const
This class represents the Value Iteration Operator (also known as Bellman operator).
Stores and manages an extremal (maximal or minimal) value.
Definition Extremum.h:15
bool checkConvergence(std::pair< std::vector< ValueType >, std::vector< ValueType > > const &xy, uint64_t &convergenceCheckState, std::function< void()> const &getNextConvergenceCheckState, bool relative, ValueType const &precision)
bool constexpr maximize(OptimizationDirection d)