Storm 1.11.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MaximalEndComponentDecomposition.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <span>
3#include <sstream>
4
6
10#include "storm/utility/graph.h"
11
12namespace storm {
13namespace storage {
14
15template<typename ValueType>
19
20template<typename ValueType>
21template<typename RewardModelType>
26
27template<typename ValueType>
29 storm::storage::SparseMatrix<ValueType> const& backwardTransitions) {
30 performMaximalEndComponentDecomposition(transitionMatrix, backwardTransitions);
31}
32
33template<typename ValueType>
35 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
36 storm::storage::BitVector const& states) {
37 performMaximalEndComponentDecomposition(transitionMatrix, backwardTransitions, states);
38}
39
40template<typename ValueType>
42 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
43 storm::storage::BitVector const& states,
44 storm::storage::BitVector const& choices) {
45 performMaximalEndComponentDecomposition(transitionMatrix, backwardTransitions, states, choices);
46}
47
48template<typename ValueType>
53
54template<typename ValueType>
58
59template<typename ValueType>
64
65template<typename ValueType>
69
70template<typename ValueType>
75
76template<typename ValueType>
77std::string MaximalEndComponentDecomposition<ValueType>::statistics(uint64_t totalNumberOfStates) const {
78 if (this->empty()) {
79 return "Empty MEC decomposition.";
80 }
81 uint64_t statesInMec = 0;
82 uint64_t trivialMecs = 0;
83 uint64_t smallestSize = std::numeric_limits<uint64_t>::max();
84 uint64_t largestSize = 0;
85 for (auto const& mec : this->blocks) {
86 statesInMec += mec.size();
87 if (mec.size() == 1u) {
88 ++trivialMecs;
89 } else {
90 smallestSize = std::min<uint64_t>(smallestSize, mec.size());
91 largestSize = std::max<uint64_t>(largestSize, mec.size());
92 }
93 }
94 uint64_t const statesInNonTrivialMec = statesInMec - trivialMecs;
95 auto getPercentage = [&totalNumberOfStates](uint64_t states) -> double {
96 return (totalNumberOfStates == 0) ? 0.0 : (100.0 * states / totalNumberOfStates);
97 };
98 std::stringstream ss;
99 ss << "MEC decomposition statistics: ";
100 ss << "There are " << this->size() << " MECs out of which " << trivialMecs << " are trivial, i.e., consist of a single state.";
101 ss << " " << statesInMec << " out of " << totalNumberOfStates << " states (" << getPercentage(statesInMec) << "%) are on some MEC. "
102 << statesInNonTrivialMec << " states (" << getPercentage(statesInNonTrivialMec) << "%) are on a non-trivial mec. ";
103 if (largestSize > 0) {
104 ss << "The smallest non-trivial MEC has " << smallestSize << " states and the largest non-trivial MEC has " << largestSize << " states.";
105 }
106 return ss.str();
107}
108
116void getFlatSccDecomposition(SccDecompositionResult const& sccDecRes, std::vector<uint64_t>& sccStates, std::vector<uint64_t>& sccIndications) {
117 // initialize result vectors with correct size
118 sccIndications.assign(sccDecRes.sccCount + 1, 0ull);
119 sccStates.resize(sccDecRes.nonTrivialStates.getNumberOfSetBits());
120
121 // count the number of states in each SCC. For efficiency, we re-use storage from sccIndications but make sure that sccIndications[0]==0 remains true
122 std::span<uint64_t> sccCounts(sccIndications.begin() + 1, sccIndications.end());
123 for (auto state : sccDecRes.nonTrivialStates) {
124 ++sccCounts[sccDecRes.stateToSccMapping[state]];
125 }
126
127 // Now establish that sccCounts[i] points to the first entry in sccStates for SCC i
128 // This means that sccCounts[i] is the sum of all scc sizes for all SCCs j < i
129 uint64_t sum = 0;
130 for (auto& count : sccCounts) {
131 auto const oldSum = sum;
132 sum += count;
133 count = oldSum;
134 }
135
136 // Now fill the sccStates vector
137 for (auto state : sccDecRes.nonTrivialStates) {
138 auto const sccIndex = sccDecRes.stateToSccMapping[state];
139 sccStates[sccCounts[sccIndex]] = state;
140 ++sccCounts[sccIndex];
141 }
142
143 // At this point, sccCounts[i] points to the first entry in sccStates for SCC i+1
144 // Since sccCounts[i] = sccIndications[i+1], we are done already
145}
146
147template<typename ValueType>
148void MaximalEndComponentDecomposition<ValueType>::performMaximalEndComponentDecomposition(storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
149 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
152 // Get some data for convenient access.
153 auto const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
154
155 storm::storage::BitVector remainingEcCandidates, ecChoices;
156 SccDecompositionResult sccDecRes;
157 SccDecompositionMemoryCache sccDecCache;
158 StronglyConnectedComponentDecompositionOptions sccDecOptions;
159 sccDecOptions.dropNaiveSccs();
160 if (states) {
161 sccDecOptions.subsystem(*states);
162 }
163 if (choices) {
164 ecChoices = *choices;
165 sccDecOptions.choices(ecChoices);
166 } else {
167 ecChoices.resize(transitionMatrix.getRowCount(), true);
168 }
169
170 // Reserve storage for the mapping of SCC indices to
171 std::vector<uint64_t> ecSccStates, ecSccIndications;
172 auto getSccStates = [&ecSccStates, &ecSccIndications](uint64_t const sccIndex) {
173 return std::span(ecSccStates).subspan(ecSccIndications[sccIndex], ecSccIndications[sccIndex + 1] - ecSccIndications[sccIndex]);
174 };
175 while (true) {
176 performSccDecomposition(transitionMatrix, sccDecOptions, sccDecRes, sccDecCache);
177 getFlatSccDecomposition(sccDecRes, ecSccStates, ecSccIndications);
178
179 remainingEcCandidates = sccDecRes.nonTrivialStates;
180 storm::storage::BitVector ecSccIndices(sccDecRes.sccCount, true);
181 storm::storage::BitVector nonTrivSccIndices(sccDecRes.sccCount, false);
182 // find the choices that do not stay in their SCC
183 for (auto state : remainingEcCandidates) {
184 auto const sccIndex = sccDecRes.stateToSccMapping[state];
185 nonTrivSccIndices.set(sccIndex, true);
186 bool stateCanStayInScc = false;
187 for (auto const choice : transitionMatrix.getRowGroupIndices(state)) {
188 if (!ecChoices.get(choice)) {
189 continue;
190 }
191 auto row = transitionMatrix.getRow(choice);
192 if (std::any_of(row.begin(), row.end(), [&sccIndex, &sccDecRes](auto const& entry) {
193 return sccIndex != sccDecRes.stateToSccMapping[entry.getColumn()] && !storm::utility::isZero(entry.getValue());
194 })) {
195 ecChoices.set(choice, false); // The choice leaves the SCC
196 ecSccIndices.set(sccIndex, false); // This SCC is not 'stable' yet
197 } else {
198 stateCanStayInScc = true; // The choice stays in the SCC
199 }
200 }
201 if (!stateCanStayInScc) {
202 remainingEcCandidates.set(state, false); // This state is not in an EC
203 }
204 }
205
206 // process the MECs that we've found, i.e. SCCs where every state can stay inside the SCC
207 ecSccIndices &= nonTrivSccIndices;
208 for (auto sccIndex : ecSccIndices) {
209 MaximalEndComponent newMec;
210 for (auto const state : getSccStates(sccIndex)) {
211 // This is no longer a candidate
212 remainingEcCandidates.set(state, false);
213 // Add choices to the MEC
214 MaximalEndComponent::set_type containedChoices;
215 for (auto ecChoiceIt = ecChoices.begin(nondeterministicChoiceIndices[state]); *ecChoiceIt < nondeterministicChoiceIndices[state + 1];
216 ++ecChoiceIt) {
217 containedChoices.insert(*ecChoiceIt);
218 }
219 STORM_LOG_ASSERT(!containedChoices.empty(), "The contained choices of any state in an MEC must be non-empty.");
220 newMec.addState(state, std::move(containedChoices));
221 }
222 this->blocks.emplace_back(std::move(newMec));
223 }
224
225 if (nonTrivSccIndices == ecSccIndices) {
226 // All non trivial SCCs are MECs, nothing left to do!
227 break;
228 }
229
230 // prepare next iteration.
231 // It suffices to keep the candidates that have the possibility to always stay in the candidate set
232 remainingEcCandidates = storm::utility::graph::performProbGreater0A(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions,
233 remainingEcCandidates, ~remainingEcCandidates, false, 0, ecChoices);
234 remainingEcCandidates.complement();
235 sccDecOptions.subsystem(remainingEcCandidates);
236 sccDecOptions.choices(ecChoices);
237 }
238
239 STORM_LOG_DEBUG("MEC decomposition found " << this->size() << " MEC(s).");
240}
241
242// Explicitly instantiate the MEC decomposition.
243template class MaximalEndComponentDecomposition<double>;
245
246template class MaximalEndComponentDecomposition<storm::RationalNumber>;
249
250template class MaximalEndComponentDecomposition<storm::Interval>;
253
254template class MaximalEndComponentDecomposition<storm::RationalFunction>;
257
258} // namespace storage
259} // namespace storm
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:197
storm::storage::SparseMatrix< ValueType > getBackwardTransitions() const
Retrieves the backward transition relation of the model, i.e.
Definition Model.cpp:152
The base class of sparse nondeterministic models.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void complement()
Negates all bits in the bit vector.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
void resize(uint64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
This class represents the decomposition of a model into blocks which are of the template type.
Decomposition & operator=(Decomposition const &other)
Assigns the contents of the given decomposition to the current one by copying the contents.
This class represents the decomposition of a nondeterministic model into its maximal end components.
MaximalEndComponentDecomposition & operator=(MaximalEndComponentDecomposition const &other)
Assigns the contents of the given MEC decomposition to the current one by copying its contents.
std::string statistics(uint64_t totalNumberOfStates) const
Returns a string containing statistics about the MEC decomposition, e.g., the number of (trivial/non-...
storm::storage::FlatSet< sparse::state_type > set_type
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.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
#define STORM_LOG_DEBUG(message)
Definition logging.h:18
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
void getFlatSccDecomposition(SccDecompositionResult const &sccDecRes, std::vector< uint64_t > &sccStates, std::vector< uint64_t > &sccIndications)
Compute a mapping from SCC index to the set of states in that SCC.
void performSccDecomposition(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, StronglyConnectedComponentDecompositionOptions const &options, SccDecompositionResult &result)
Computes an SCC decomposition for the given matrix and options.
storm::storage::BitVector performProbGreater0A(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability greater 0 of satisfying phi until psi under any pos...
Definition graph.cpp:847
LabParser.cpp.
Holds the result data of the implemented SCC decomposition algorithm.
std::vector< uint64_t > stateToSccMapping
Number of found SCCs.
storm::storage::BitVector nonTrivialStates
Mapping from states to the SCC it belongs to.
uint64_t sccCount
True if an SCC is associated to the given state.