Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GuessingValueIterationHelper.cpp
Go to the documentation of this file.
2
4
8
10
12
13namespace storm::solver::helper {
14
15namespace gviinternal {
16
17template<typename ValueType>
19
20template<typename ValueType>
21void IterationHelper<ValueType>::swipeWeights() {
22 const auto& rowGroupIndices = matrix.getRowGroupIndices();
23 IndexType i = weights.size();
24 while (i > 0) {
25 --i;
26
27 auto den = storm::utility::convertNumber<ValueType>(rowGroupIndices[i + 1] - rowGroupIndices[i]);
28 auto weightOnGroup = weights[i] / den;
29 weights[i] = 0;
30
31 for (auto row = rowGroupIndices[i]; row < rowGroupIndices[i + 1]; ++row) {
32 for (auto const& entry : matrix.getRow(row)) {
33 weights[entry.getColumn()] += weightOnGroup * entry.getValue();
34 }
35 }
36 }
37}
38
39template<typename ValueType>
40IndexType IterationHelper<ValueType>::selectRowGroupToGuess(const std::vector<ValueType>& lowerX, const std::vector<ValueType>& upperX) {
41 weights = upperX;
42 // upper - lower
44
45 std::vector<ValueType> additiveWeights(weights.size(), 0);
46 for (int i = 0; i < 50; i++) {
47 swipeWeights();
48 storm::utility::vector::addScaledVector(additiveWeights, weights, 1);
49 }
50
51 return static_cast<IndexType>(std::max_element(additiveWeights.begin(), additiveWeights.end()) - additiveWeights.begin());
52}
53
54template<typename ValueType>
55bool IterationHelper<ValueType>::checkConvergence(std::vector<ValueType> const& lower, std::vector<ValueType> const& upper, bool relative,
56 ValueType const& precision) const {
57 auto itL = lower.begin();
58 const auto itLEnd = lower.end();
59 auto itU = upper.begin();
60
61 for (; itL != itLEnd; ++itL, ++itU) {
62 ValueType threshold = 2 * precision;
63 if (relative) {
64 // Scaling by the endpoint closest to zero bounds the relative error of the midpoint.
65 if (*itL > 0) {
66 threshold *= *itL;
67 } else if (*itU < 0) {
68 threshold *= -*itU;
69 } else if (*itL != *itU) {
70 return false;
71 }
72 }
73 if (*itU - *itL > threshold) {
74 return false;
75 }
76 }
77 return true;
78}
79
80template<typename ValueType>
81ValueType IterationHelper<ValueType>::getSumLength(std::vector<ValueType>& lower, std::vector<ValueType>& upper) const {
82 ValueType sum = 0;
83 auto itL = lower.begin();
84 const auto itLEnd = lower.end();
85 auto itU = upper.begin();
86
87 for (; itL != itLEnd; ++itL, ++itU) {
88 sum += *itU - *itL;
89 }
90 return sum;
91}
92
93} // namespace gviinternal
94
95template<typename ValueType, storm::OptimizationDirection Dir>
97 public:
98 ValueType xGuessed, yGuessed;
99
101 GVIBackend(uint64_t guessedRowGroup) : guessedRowGroup(guessedRowGroup) {}
102
104
105 void firstRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
106 xBest = std::move(value.first);
107 yBest = std::move(value.second);
108 }
109
110 void nextRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
111 xBest &= std::move(value.first);
112 yBest &= std::move(value.second);
113 }
114
115 void applyUpdate(ValueType& xCurr, ValueType& yCurr, uint64_t rowGroup) {
116 if (guessedRowGroup && rowGroup == *guessedRowGroup) {
117 xGuessed = *xBest;
118 yGuessed = *yBest;
119 return;
120 }
121 xCurr = std::max(xCurr, *xBest);
122 yCurr = std::min(yCurr, *yBest);
123 }
124
125 void endOfIteration() const {}
126
127 bool constexpr converged() const {
128 return false;
129 }
130
131 bool constexpr abort() const {
132 return false;
133 }
134
135 private:
137 std::optional<uint64_t> guessedRowGroup;
138};
139
140template<typename ValueType, bool TrivialRowGrouping>
141gviinternal::IndexType GuessingValueIterationHelper<ValueType, TrivialRowGrouping>::selectRowGroupToGuess(std::vector<ValueType>& lowerX,
142 std::vector<ValueType>& upperX) {
143 return iterationHelper.selectRowGroupToGuess(lowerX, upperX);
144}
145
146template<typename ValueType, bool TrivialRowGrouping>
147template<OptimizationDirection Dir>
148void GuessingValueIterationHelper<ValueType, TrivialRowGrouping>::applyInPlace(std::vector<ValueType>& lowerX, std::vector<ValueType>& upperX,
149 const std::vector<ValueType>& b, GVIBackend<ValueType, Dir>& backend) {
150 std::pair<std::vector<ValueType>, std::vector<ValueType>> xy;
151 xy.first.swap(lowerX);
152 xy.second.swap(upperX);
153 viOperator->applyInPlace(xy, b, backend);
154 lowerX.swap(xy.first);
155 upperX.swap(xy.second);
156}
157
158template<typename ValueType, bool TrivialRowGrouping>
159template<OptimizationDirection Dir>
160std::pair<gviinternal::VerifyResult, SolverStatus> GuessingValueIterationHelper<ValueType, TrivialRowGrouping>::tryVerify(
161 std::vector<ValueType>& lowerX, std::vector<ValueType>& upperX, const std::vector<ValueType>& b, uint64_t& numIterations,
162 gviinternal::IndexType rowGroupToGuess, ValueType guessValue, bool relative, ValueType precision,
163 std::function<SolverStatus(GVIData<ValueType> const&)> const& iterationCallback) {
164 guessLower = lowerX;
165 guessUpper = upperX;
166 guessLower[rowGroupToGuess] = guessUpper[rowGroupToGuess] = guessValue;
167 ValueType sumLengthBefore = 0;
168 GVIBackend<ValueType, Dir> guessingBackend(rowGroupToGuess);
169 GVIBackend<ValueType, Dir> iiBackend;
170
171 auto status = SolverStatus::InProgress;
172 while (status == SolverStatus::InProgress) {
173 ++numIterations;
174 applyInPlace<Dir>(lowerX, upperX, b, iiBackend);
175 applyInPlace<Dir>(guessLower, guessUpper, b, guessingBackend);
176 auto guessedNewLower = guessingBackend.xGuessed;
177 auto guessedNewUpper = guessingBackend.yGuessed;
178 if (guessValue <= guessedNewLower) {
179 lowerX = guessLower;
180 lowerX[rowGroupToGuess] = guessedNewLower;
182 }
183 if (guessedNewUpper <= guessValue) {
184 upperX = guessUpper;
185 upperX[rowGroupToGuess] = guessedNewUpper;
187 }
188
189 auto sumLength = iterationHelper.getSumLength(guessLower, guessUpper);
190 if (sumLength == sumLengthBefore) {
191 // nothing changed. abort the verification
193 }
194 sumLengthBefore = sumLength;
195
196 if (iterationHelper.checkConvergence(lowerX, upperX, relative, precision)) {
198 }
199
200 if (iterationCallback) {
201 status = iterationCallback({lowerX, upperX, status});
202 }
203 }
205}
206
207template<typename ValueType, bool TrivialRowGrouping>
209 std::vector<ValueType>& lowerX, std::vector<ValueType>& upperX, const std::vector<ValueType>& b, uint64_t& numIterations, bool relative,
210 ValueType precision, std::optional<storm::solver::OptimizationDirection> dir,
211 std::function<SolverStatus(GVIData<ValueType> const&)> const& iterationCallback) {
212 if (!dir.has_value() || minimize(dir.value())) {
213 return solveEquations<OptimizationDirection::Minimize>(lowerX, upperX, b, numIterations, relative, precision, iterationCallback);
214 } else {
215 return solveEquations<OptimizationDirection::Maximize>(lowerX, upperX, b, numIterations, relative, precision, iterationCallback);
216 }
217}
218
219template<typename ValueType, bool TrivialRowGrouping>
220template<OptimizationDirection Dir>
222 std::vector<ValueType>& lowerX, std::vector<ValueType>& upperX, const std::vector<ValueType>& b, uint64_t& numIterations, bool relative,
223 ValueType precision, std::function<SolverStatus(GVIData<ValueType> const&)> const& iterationCallback) {
224 // do n iterations first
225 auto status = doIterations<Dir>(lowerX, upperX, b, numIterations, lowerX.size(), relative, precision, iterationCallback);
226 if (status != SolverStatus::InProgress) {
227 return status;
228 }
229 while (status == SolverStatus::InProgress) {
230 auto rowGroupToGuess = selectRowGroupToGuess(lowerX, upperX);
231 bool didVerify = false;
232 // try verification using different fractions of the interval
233 for (int den = 2; den < 30 && !didVerify; ++den) {
234 for (int num = 1; num < den; num++) {
235 if (std::gcd(num, den) != 1) {
236 continue;
237 }
238 auto guessValue = (lowerX[rowGroupToGuess] * num + upperX[rowGroupToGuess] * (den - num)) / den;
239 auto [verifyResult, verifyStatus] =
240 tryVerify<Dir>(lowerX, upperX, b, numIterations, rowGroupToGuess, guessValue, relative, precision, iterationCallback);
241 status = verifyStatus;
242 if (verifyResult == gviinternal::VerifyResult::Verified || verifyResult == gviinternal::VerifyResult::Converged) {
243 didVerify = true;
244 break;
245 }
246 }
247 }
248 if (didVerify) {
249 if (iterationHelper.checkConvergence(lowerX, upperX, relative, precision)) {
251 }
252 } else {
253 break;
254 }
255 }
256
257 // verification has failed. iterate until convergence
258 return doIterations<Dir>(lowerX, upperX, b, numIterations, {}, relative, precision, iterationCallback);
259}
260
261template<typename ValueType, bool TrivialRowGrouping>
264 : viOperator(viOperator), iterationHelper(matrix) {
265 // Intentionally left empty.
266}
267
268template<typename ValueType, bool TrivialRowGrouping>
269template<OptimizationDirection Dir>
270SolverStatus GuessingValueIterationHelper<ValueType, TrivialRowGrouping>::doIterations(
271 std::vector<ValueType>& lowerX, std::vector<ValueType>& upperX, const std::vector<ValueType>& b, uint64_t& numIterations,
272 std::optional<uint64_t> maxIterations, bool relative, const ValueType& precision,
273 std::function<SolverStatus(GVIData<ValueType> const&)> const& iterationCallback) {
274 ValueType sumLengthBefore = 0;
275 uint64_t localIterations = 0;
277 auto status = SolverStatus::InProgress;
278 while (status == SolverStatus::InProgress && (!maxIterations.has_value() || localIterations < maxIterations)) {
279 applyInPlace<Dir>(lowerX, upperX, b, iiBackend);
280 ++localIterations;
281 ++numIterations;
282
283 auto sumLength = iterationHelper.getSumLength(lowerX, upperX);
284 if (sumLengthBefore == sumLength) {
285 // nothing changed. abort the iterations.
287 }
288 sumLengthBefore = sumLength;
289
290 if (iterationHelper.checkConvergence(lowerX, upperX, relative, precision)) {
292 }
293 if (iterationCallback) {
294 status = iterationCallback(GVIData<ValueType>{lowerX, upperX, status});
295 }
296 }
297
298 if (iterationHelper.checkConvergence(lowerX, upperX, relative, precision)) {
300 }
301 return status;
302}
303
308} // namespace storm::solver::helper
void nextRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
void applyUpdate(ValueType &xCurr, ValueType &yCurr, uint64_t rowGroup)
void firstRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
GuessingValueIterationHelper(std::shared_ptr< ValueIterationOperator< ValueType, TrivialRowGrouping > > viOperator, const storage::SparseMatrix< ValueType > &matrix)
SolverStatus solveEquations(std::vector< ValueType > &lowerX, std::vector< ValueType > &upperX, const std::vector< ValueType > &b, uint64_t &numIterations, bool relative, ValueType precision, std::optional< storm::solver::OptimizationDirection > dir, std::function< SolverStatus(GVIData< ValueType > const &)> const &iterationCallback)
This class represents the Value Iteration Operator (also known as Bellman operator).
ValueType getSumLength(std::vector< ValueType > &lower, std::vector< ValueType > &upper) const
bool checkConvergence(std::vector< ValueType > const &lower, std::vector< ValueType > const &upper, bool relative, ValueType const &precision) const
IndexType selectRowGroupToGuess(const std::vector< ValueType > &lowerX, const std::vector< ValueType > &upperX)
IterationHelper(storm::storage::SparseMatrix< ValueType > const &matrix_)
A class that holds a possibly non-square matrix in the compressed row storage format.
Stores and manages an extremal (maximal or minimal) value.
Definition Extremum.h:17
bool constexpr minimize(OptimizationDirection d)
void addScaledVector(std::vector< InValueType1 > &firstOperand, std::vector< InValueType2 > const &secondOperand, InValueType3 const &factor)
Computes x:= x + a*y, i.e., adds each element of the first vector and (the corresponding element of t...
Definition vector.h:461
TargetType convertNumber(SourceType const &number)