14template<
bool Relative,
typename ValueType>
15static ValueType
diff(ValueType
const& oldValue, ValueType
const& newValue) {
16 if constexpr (Relative) {
17 return storm::utility::abs<ValueType>((newValue - oldValue) / newValue);
19 return storm::utility::abs<ValueType>(newValue - oldValue);
23template<
typename ValueType, storm::OptimizationDirection Dir,
bool Relative>
26 GSVIBackend(ValueType
const& precision) : precision{precision} {
34 void firstRow(ValueType&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
35 best = std::move(value);
38 void nextRow(ValueType&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
42 void applyUpdate(ValueType& currValue, [[maybe_unused]] uint64_t rowGroup) {
46 currValue = std::move(*best);
63 ValueType
const precision;
64 bool isConverged{
true};
67template<
typename ValueType,
bool TrivialRowGrouping>
68template<storm::OptimizationDirection Dir,
bool Relative>
69SolverStatus OptimisticValueIterationHelper<ValueType, TrivialRowGrouping>::GSVI(
70 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets, uint64_t& numIterations, ValueType
const& precision,
72 GSVIBackend<ValueType, Dir, Relative> backend{precision};
76 if (viOperator->applyInPlace(operand, offsets, backend)) {
78 }
else if (iterationCallback) {
79 status = iterationCallback(status, operand);
85template<
bool Relative,
typename ValueType>
86void guessCandidate(std::pair<std::vector<ValueType>, std::vector<ValueType>>& vu, ValueType
const& guessValue, std::optional<ValueType>
const& lowerBound,
87 std::optional<ValueType>
const& upperBound) {
88 std::function<ValueType(ValueType
const&)> guess;
89 [[maybe_unused]] ValueType factor = storm::utility::one<ValueType>() + guessValue;
90 if constexpr (Relative) {
92 if (lowerBound && *lowerBound < storm::utility::zero<ValueType>()) {
93 guess = [&guessValue](ValueType
const& val) {
return val + storm::utility::abs<ValueType>(val * guessValue); };
95 guess = [&factor](ValueType
const& val) {
return val * factor; };
98 guess = [&guessValue](ValueType
const& val) {
return storm::utility::isZero(val) ? storm::utility::zero<ValueType>() : val + guessValue; };
100 if (lowerBound || upperBound) {
101 std::function<ValueType(ValueType
const&)> guessAndClamp;
103 guessAndClamp = [&guess, &upperBound](ValueType
const& val) {
return std::min(guess(val), *upperBound); };
104 }
else if (!upperBound) {
105 guessAndClamp = [&guess, &lowerBound](ValueType
const& val) {
return std::max(guess(val), *lowerBound); };
107 guessAndClamp = [&guess, &lowerBound, &upperBound](ValueType
const& val) {
return std::clamp(guess(val), *lowerBound, *upperBound); };
115template<
typename ValueType, OptimizationDirection Dir,
bool Relative>
122 errorValue = storm::utility::zero<ValueType>();
125 void firstRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
126 vBest = std::move(value.first);
127 uBest = std::move(value.second);
130 void nextRow(std::pair<ValueType, ValueType>&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
131 vBest &= std::move(value.first);
132 uBest &= std::move(value.second);
135 void applyUpdate(ValueType& vCurr, ValueType& uCurr, [[maybe_unused]] uint64_t rowGroup) {
136 if (*vBest != storm::utility::zero<ValueType>()) {
137 errorValue &= diff<Relative>(vCurr, *vBest);
139 if (*uBest < uCurr) {
142 }
else if (*uBest > uCurr) {
156 return isAllDown || isAllUp;
177 bool isAllDown{
true};
183template<
typename ValueType,
bool TrivialRowGrouping>
186 : viOperator(viOperator) {
190template<
typename ValueType,
bool TrivialRowGrouping>
191template<OptimizationDirection Dir,
bool Relative>
193 std::pair<std::vector<ValueType>, std::vector<ValueType>>& vu, std::vector<ValueType>
const& offsets, uint64_t& numIterations, ValueType
const& precision,
194 ValueType
const& guessValue, std::optional<ValueType>
const& lowerBound, std::optional<ValueType>
const& upperBound,
196 ValueType currentGuessValue = guessValue;
197 for (uint64_t numTries = 1;
true; ++numTries) {
198 if (
SolverStatus status = GSVI<Dir, Relative>(vu.first, offsets, numIterations, currentGuessValue, iterationCallback);
202 guessCandidate<Relative>(vu, precision, lowerBound, upperBound);
206 maxIters = std::numeric_limits<uint64_t>::max();
208 maxIters = numIterations + storm::utility::convertNumber<uint64_t, ValueType>(
209 storm::utility::ceil<ValueType>(storm::utility::one<ValueType>() / currentGuessValue));
211 while (numIterations < maxIters) {
213 if (viOperator->applyInPlace(vu, offsets, backend)) {
217 assert(backend.
allUp());
221 if (backend.
abort()) {
224 if (iterationCallback) {
230 STORM_LOG_WARN_COND(numTries != 20,
"Optimistic Value Iteration did not terminate after 20 refinements. It might be stuck.");
231 currentGuessValue = backend.
error() / storm::utility::convertNumber<ValueType, uint64_t>(2u);
235template<
typename ValueType,
bool TrivialRowGrouping>
237 std::pair<std::vector<ValueType>, std::vector<ValueType>>& vu, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative,
238 ValueType
const& precision, std::optional<storm::OptimizationDirection>
const& dir, ValueType
const& guessValue, std::optional<ValueType>
const& lowerBound,
239 std::optional<ValueType>
const& upperBound,
243 if (lowerBound && upperBound) {
244 ValueType
diff = *upperBound - *lowerBound;
246 (!relative &&
diff <= precision)) {
247 vu.first.assign(vu.first.size(), *lowerBound);
248 vu.second.assign(vu.second.size(), *upperBound);
253 if (!dir.has_value() ||
maximize(*dir)) {
255 return OVI<OptimizationDirection::Maximize, true>(vu, offsets, numIterations, precision, guessValue, lowerBound, upperBound, iterationCallback);
257 return OVI<OptimizationDirection::Maximize, false>(vu, offsets, numIterations, precision, guessValue, lowerBound, upperBound, iterationCallback);
261 return OVI<OptimizationDirection::Minimize, true>(vu, offsets, numIterations, precision, guessValue, lowerBound, upperBound, iterationCallback);
263 return OVI<OptimizationDirection::Minimize, false>(vu, offsets, numIterations, precision, guessValue, lowerBound, upperBound, iterationCallback);
268template<
typename ValueType,
bool TrivialRowGrouping>
270 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets, uint64_t& numIterations,
bool relative, ValueType
const& precision,
271 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& guessValue, std::optional<ValueType>
const& lowerBound,
272 std::optional<ValueType>
const& upperBound,
275 std::pair<std::vector<ValueType>, std::vector<ValueType>> vu;
276 auto& auxVector = viOperator->allocateAuxiliaryVector(operand.size());
277 vu.first.swap(operand);
278 vu.second.swap(auxVector);
279 auto doublePrec = precision + precision;
280 if constexpr (std::is_same_v<ValueType, double>) {
281 doublePrec -= precision * 1e-6;
283 auto status = OVI(vu, offsets, numIterations, relative, doublePrec, dir, guessValue ? *guessValue : doublePrec, lowerBound, upperBound, iterationCallback);
284 auto two = storm::utility::convertNumber<ValueType>(2.0);
286 storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(
287 vu.first, vu.second, vu.first, [&two](ValueType
const& a, ValueType
const& b) -> ValueType { return (a + b) / two; });
289 vu.first.swap(operand);
290 vu.second.swap(auxVector);
291 viOperator->freeAuxiliaryVector();
295template<
typename ValueType,
bool TrivialRowGrouping>
297 std::vector<ValueType>& operand, std::vector<ValueType>
const& offsets,
bool relative, ValueType
const& precision,
298 std::optional<storm::OptimizationDirection>
const& dir, std::optional<ValueType>
const& guessValue, std::optional<ValueType>
const& lowerBound,
299 std::optional<ValueType>
const& upperBound,
301 uint64_t numIterations = 0;
302 return OVI(operand, offsets, numIterations, relative, precision, dir, guessValue, lowerBound, upperBound, iterationCallback);
void endOfIteration() const
void firstRow(ValueType &&value, uint64_t rowGroup, uint64_t row)
GSVIBackend(ValueType const &precision)
void nextRow(ValueType &&value, uint64_t rowGroup, uint64_t row)
void applyUpdate(ValueType &currValue, uint64_t rowGroup)
bool constexpr abort() const
void firstRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
void endOfIteration() const
void applyUpdate(ValueType &vCurr, ValueType &uCurr, uint64_t rowGroup)
void nextRow(std::pair< ValueType, ValueType > &&value, uint64_t rowGroup, uint64_t row)
Implements Optimistic value iteration.
OptimisticValueIterationHelper(std::shared_ptr< ValueIterationOperator< ValueType, TrivialRowGrouping > > viOperator)
SolverStatus OVI(std::pair< std::vector< ValueType >, std::vector< ValueType > > &vu, std::vector< ValueType > const &offsets, uint64_t &numIterations, ValueType const &precision, ValueType const &guessValue, std::optional< ValueType > const &lowerBound={}, std::optional< ValueType > const &upperBound={}, std::function< SolverStatus(SolverStatus const &, std::vector< ValueType > const &)> const &iterationCallback={}) const
This class represents the Value Iteration Operator (also known as Bellman operator).
Stores and manages an extremal (maximal or minimal) value.
#define STORM_LOG_WARN_COND(cond, message)
void guessCandidate(std::pair< std::vector< ValueType >, std::vector< ValueType > > &vu, ValueType const &guessValue, std::optional< ValueType > const &lowerBound, std::optional< ValueType > const &upperBound)
static ValueType diff(ValueType const &oldValue, ValueType const &newValue)
bool constexpr maximize(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)