Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
BaierUpperRewardBoundsComputer.cpp
Go to the documentation of this file.
2
11
13
14template<typename ValueType>
16 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
17 std::vector<ValueType> const& oneStepTargetProbabilities,
18 std::function<uint64_t(uint64_t)> const& stateToScc)
19 : transitionMatrix(transitionMatrix),
20 backwardTransitions(&backwardTransitions),
21 stateToScc(stateToScc),
22 oneStepTargetProbabilities(oneStepTargetProbabilities) {
23 // Intentionally left empty.
24}
25
26template<typename ValueType>
28 std::vector<ValueType> const& oneStepTargetProbabilities,
29 std::function<uint64_t(uint64_t)> const& stateToScc)
30 : transitionMatrix(transitionMatrix), backwardTransitions(nullptr), stateToScc(stateToScc), oneStepTargetProbabilities(oneStepTargetProbabilities) {
31 // Intentionally left empty.
32}
33
34template<typename ValueType>
36 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& oneStepTargetProbabilities) {
37 return computeUpperBoundOnExpectedVisitingTimes(transitionMatrix, transitionMatrix.transpose(true), oneStepTargetProbabilities);
38}
39
40template<typename ValueType>
42 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
43 std::vector<ValueType> const& oneStepTargetProbabilities) {
44 std::vector<uint64_t> stateToScc =
45 storm::storage::StronglyConnectedComponentDecomposition<ValueType>(transitionMatrix).computeStateToSccIndexMap(transitionMatrix.getRowGroupCount());
46 return computeUpperBoundOnExpectedVisitingTimes(transitionMatrix, backwardTransitions, oneStepTargetProbabilities,
47 [&stateToScc](uint64_t s) { return stateToScc[s]; });
48}
49
50template<typename ValueType>
52 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
53 std::vector<ValueType> const& oneStepTargetProbabilities, std::function<uint64_t(uint64_t)> const& stateToScc) {
54 // This first computes for every state s a non-zero lower bound d_s for the probability that starting at s, we never reach s again
55 // An upper bound on the expected visiting times is given by 1/d_s
56 // More precisely, we maintain a set of processed states.
57 // Given a processed states s, let T be the union of the target states and the states processed *before* s.
58 // Then, the value d_s for s is a lower bound for the probability that from the next step on we always stay in T.
59 // Since T does not contain s, d_s is thus also a lower bound for the probability that we never reach s again.
60 // Very roughly, the procedure can be seen as a quantitative variant of 'performProb1A'.
61 // Note: We slightly deviate from the description of Baier et al. http://doi.org/10.1007/978-3-319-63387-9_8.
62 // While they only consider processed states from a previous iteration step, we immediately consider them once they are processed
63
64 auto const numStates = transitionMatrix.getRowGroupCount();
65 assert(transitionMatrix.getRowCount() == oneStepTargetProbabilities.size());
66 assert(backwardTransitions.getRowCount() == numStates);
67 assert(backwardTransitions.getColumnCount() == numStates);
68 auto const& rowGroupIndices = transitionMatrix.getRowGroupIndices();
69
70 // Initialize the 'valid' choices.
71 // A choice is valid iff it goes to processed states with non-zero probability.
72 // Initially, mark all choices as valid that have non-zero probability to go to the target states *or* to a different Scc.
73 auto validChoices = storm::utility::vector::filterGreaterZero(oneStepTargetProbabilities);
74 for (uint64_t state = 0; state < numStates; ++state) {
75 auto const scc = stateToScc(state);
76 for (auto rowIndex = rowGroupIndices[state], rowEnd = rowGroupIndices[state + 1]; rowIndex < rowEnd; ++rowIndex) {
77 auto const row = transitionMatrix.getRow(rowIndex);
78 if (std::any_of(row.begin(), row.end(), [&stateToScc, &scc](auto const& entry) { return scc != stateToScc(entry.getColumn()); })) {
79 validChoices.set(rowIndex, true);
80 }
81 }
82 }
83
84 // Vector that holds the result.
85 std::vector<ValueType> result(numStates, storm::utility::one<ValueType>());
86 // The states that we already have assigned a value for.
87 storm::storage::BitVector processedStates(numStates, false);
88
89 // Auxiliary function that checks if the given row is valid, i.e. has a transition to a different SCC or to an already processed state.
90 // Since a once valid choice can never become invalid, we cache valid choices
91 auto isValidChoice = [&transitionMatrix, &validChoices, &processedStates](uint64_t const& choice) {
92 if (validChoices.get(choice)) {
93 return true;
94 }
95 auto row = transitionMatrix.getRow(choice);
96 // choices that lead to different SCCs already have been marked as valid above.
97 // Hence, we don't have to check for SCCs anymore.
98 if (std::any_of(row.begin(), row.end(), [&processedStates](auto const& entry) { return processedStates.get(entry.getColumn()); })) {
99 validChoices.set(choice, true); // cache for next time
100 return true;
101 } else {
102 return false;
103 }
104 };
105
106 // Auxiliary function that computes the value of a given row
107 auto getChoiceValue = [&transitionMatrix, &oneStepTargetProbabilities, &processedStates, &stateToScc, &result](uint64_t const& rowIndex,
108 uint64_t const& sccIndex) {
109 ValueType rowValue = oneStepTargetProbabilities[rowIndex];
110 for (auto const& entry : transitionMatrix.getRow(rowIndex)) {
111 if (auto successorState = entry.getColumn(); sccIndex != stateToScc(successorState)) {
112 rowValue += entry.getValue(); // * 1
113 } else if (processedStates.get(successorState)) {
114 rowValue += entry.getValue() * result[successorState];
115 }
116 }
117 return rowValue;
118 };
119
120 // For efficiency, we maintain a set of candidateStates that satisfy some necessary conditions for being processed.
121 // A candidateState s is unprocessed, but has a processed successor state s' such that s' was processed *after* the previous time we checked candidate s.
122 storm::storage::BitVector candidateStates(numStates, true);
123
124 // We usually get the best performance by processing states in inverted order.
125 // This is because in the sparse engine the states are explored with a BFS/DFS
126 uint64_t unprocessedEnd = numStates;
127 auto candidateStateIt = candidateStates.rbegin();
128 auto const candidateStateItEnd = candidateStates.rend();
129 while (true) {
130 // Assert invariant: all states with index >= unprocessedEnd are processed and state (unprocessedEnd - 1) is not processed
131 STORM_LOG_ASSERT(processedStates.getNextUnsetIndex(unprocessedEnd) == processedStates.size(), "Invalid index for last unexplored state");
132 STORM_LOG_ASSERT(candidateStates.isSubsetOf(~processedStates), "");
133 uint64_t const state = *candidateStateIt;
134 auto const group = transitionMatrix.getRowGroupIndices(state);
135 if (std::all_of(group.begin(), group.end(), [&isValidChoice](auto const& choice) { return isValidChoice(choice); })) {
136 // Compute the state value
137 storm::utility::Minimum<ValueType> minimalStateValue;
138 auto const scc = stateToScc(state);
139 for (auto choice : group) {
140 minimalStateValue &= getChoiceValue(choice, scc);
141 }
142 result[state] = *minimalStateValue;
143 processedStates.set(state);
144 if (state == unprocessedEnd - 1) {
145 unprocessedEnd = processedStates.getStartOfOneSequenceBefore(unprocessedEnd - 1);
146 if (unprocessedEnd == 0) {
147 STORM_LOG_ASSERT(processedStates.full(), "Expected all states to be processed");
148 break;
149 }
150 }
151 // Iterate through predecessors that might become valid in the next run
152 for (auto const& predEntry : backwardTransitions.getRow(state)) {
153 if (!processedStates.get(predEntry.getColumn())) {
154 candidateStates.set(predEntry.getColumn());
155 }
156 }
157 }
158 // state is no longer a candidate
159 candidateStates.set(state, false);
160 // Get next candidate state
161 ++candidateStateIt;
162 if (candidateStateIt == candidateStateItEnd) {
163 // wrap around
164 candidateStateIt = candidateStates.rbegin(unprocessedEnd);
165 // If there are no new candidates, we likely have mecs consisting of unprocessed states. For those, there is no upper bound on the EVTs.
166 STORM_LOG_THROW(candidateStateIt != candidateStateItEnd, storm::exceptions::InvalidOperationException,
167 "Unable to compute finite upper bounds for visiting times: No more candidates.");
168 }
169 }
170
171 // Transform the d_t to an upper bound for zeta(t) (i.e. the expected number of visits of t
172 storm::utility::vector::applyPointwise(result, result, [](ValueType const& r) -> ValueType { return storm::utility::one<ValueType>() / r; });
173 return result;
174}
175
176template<typename ValueType>
178 std::vector<ValueType> const& rewards) {
179 STORM_LOG_TRACE("Computing upper reward bounds using variant-2 of Baier et al.");
180
181 // Ensure backward transitions are available.
182 storm::storage::SparseMatrix<ValueType> computedBackwardTransitions;
183 if (!backwardTransitions) {
184 computedBackwardTransitions = transitionMatrix.transpose(true);
185 }
186 auto const& backwardTransRef = backwardTransitions ? *backwardTransitions : computedBackwardTransitions;
187
188 // Ensure SCCs are available.
189 std::vector<uint64_t> computedStateToSccVec;
190 std::function<uint64_t(uint64_t)> stateToSccFct;
191 if (this->stateToScc) {
192 stateToSccFct = this->stateToScc;
193 } else {
194 // If no stateToScc function is given, we compute the SCCs from the transition matrix.
195 computedStateToSccVec =
196 storm::storage::StronglyConnectedComponentDecomposition<ValueType>(transitionMatrix).computeStateToSccIndexMap(transitionMatrix.getRowGroupCount());
197 stateToSccFct = [&computedStateToSccVec](uint64_t s) { return computedStateToSccVec[s]; };
198 }
199
200 auto expVisits = computeUpperBoundOnExpectedVisitingTimes(transitionMatrix, backwardTransRef, oneStepTargetProbabilities, stateToSccFct);
201
203 for (uint64_t state = 0; state < expVisits.size(); ++state) {
204 // Get the maximum / minimum reward that can be collected in the state.
205 // We distinguish between choices that surely exit the current SCC and those that do not.
206 // The former can only be taken at most once so we do not have to multiply with the upper bound on the expected visits.
207 auto const currScc = stateToSccFct(state);
210 // By starting the maxRewards with zero, negative rewards are essentially ignored which is necessary to provide a valid upper bound
211 for (auto rowIndex : transitionMatrix.getRowGroupIndices(state)) {
212 auto const row = transitionMatrix.getRow(rowIndex);
213 bool const exitingChoice =
214 std::all_of(row.begin(), row.end(), [&stateToSccFct, &currScc](auto const& entry) { return currScc != stateToSccFct(entry.getColumn()); });
215 if (exitingChoice) {
216 maxReward &= rewards[rowIndex];
217 minReward &= rewards[rowIndex];
218 } else {
219 maxRewardStay &= rewards[rowIndex];
220 minRewardStay &= rewards[rowIndex];
221 }
222 }
223 if (!maxRewardStay.empty()) {
224 maxReward &= (*maxRewardStay * expVisits[state]);
225 }
226 result.upper += *maxReward;
227 if (!minRewardStay.empty()) {
228 minReward &= (*minRewardStay * expVisits[state]);
229 }
230 result.lower += *minReward;
231 }
232
233 STORM_LOG_TRACE("Baier algorithm for reward bound computation (variant 2) computed bounds [" << result.lower << ", " << result.upper << "].");
234 return result;
235}
236
239} // namespace storm::modelchecker::helper
BaierUpperRewardBoundsComputer(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, std::vector< ValueType > const &oneStepTargetProbabilities, std::function< uint64_t(uint64_t)> const &stateToScc={})
Creates an object that can compute upper bounds on the maximal expected rewards for the provided MDP.
Bounds computeTotalRewardBounds(std::vector< ValueType > const &rewards)
Computes a lower and an upper bound on the expected total rewards.
static std::vector< ValueType > computeUpperBoundOnExpectedVisitingTimes(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, std::vector< ValueType > const &oneStepTargetProbabilities)
Computes for each state an upper bound for the maximal expected times each state is visited.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
const_reverse_iterator rbegin() const
Returns a reverse iterator to the indices of the set bits in the bit vector.
bool full() const
Retrieves whether all bits are set in this bit vector.
const_reverse_iterator rend() const
Returns a reverse iterator pointing at the element past the front of the bit vector.
bool isSubsetOf(BitVector const &other) const
Checks whether all bits that are set in the current bit vector are also set in the given bit vector.
uint64_t getNextUnsetIndex(uint64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to false in the bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
uint64_t getStartOfOneSequenceBefore(uint64_t endIndex) const
Retrieves the smallest index i such that all bits in the range [i,endIndex) are 1.
A class that holds a possibly non-square matrix in the compressed row storage format.
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
This class represents the decomposition of a graph-like structure into its strongly connected compone...
std::vector< uint64_t > computeStateToSccIndexMap(uint64_t numberOfStates) const
Computes a vector that for each state has the index of the scc of that state in it.
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
storm::storage::BitVector filterGreaterZero(std::vector< T > const &values)
Retrieves a bit vector containing all the indices for which the value at this position is greater tha...
Definition vector.h:508
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...
Definition vector.h:374
Extremum< storm::OptimizationDirection::Maximize, ValueType > Maximum
Definition Extremum.h:127
Extremum< storm::OptimizationDirection::Minimize, ValueType > Minimum
Definition Extremum.h:129
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19