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