Storm
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 choicesInMec = 0;
83 uint64_t trivialMecs = 0;
84 uint64_t smallestSize = std::numeric_limits<uint64_t>::max();
85 uint64_t largestSize = 0;
86 for (auto const& mec : this->blocks) {
87 statesInMec += mec.size();
88 if (mec.size() == 1u) {
89 ++trivialMecs;
90 } else {
91 smallestSize = std::min<uint64_t>(smallestSize, mec.size());
92 largestSize = std::max<uint64_t>(largestSize, mec.size());
93 }
94 }
95 uint64_t const statesInNonTrivialMec = statesInMec - trivialMecs;
96 auto getPercentage = [&totalNumberOfStates](uint64_t states) -> double {
97 return (totalNumberOfStates == 0) ? 0.0 : (100.0 * states / totalNumberOfStates);
98 };
99 std::stringstream ss;
100 ss << "MEC decomposition statistics: ";
101 ss << "There are " << this->size() << " MECs out of which " << trivialMecs << " are trivial, i.e., consist of a single state.";
102 ss << " " << statesInMec << " out of " << totalNumberOfStates << " states (" << getPercentage(statesInMec) << "%) are on some MEC. "
103 << statesInNonTrivialMec << " states (" << getPercentage(statesInNonTrivialMec) << "%) are on a non-trivial mec. ";
104 if (largestSize > 0) {
105 ss << "The smallest non-trivial MEC has " << smallestSize << " states and the largest non-trivial MEC has " << largestSize << " states.";
106 }
107 return ss.str();
108}
109
117void getFlatSccDecomposition(SccDecompositionResult const& sccDecRes, std::vector<uint64_t>& sccStates, std::vector<uint64_t>& sccIndications) {
118 // initialize result vectors with correct size
119 sccIndications.assign(sccDecRes.sccCount + 1, 0ull);
120 sccStates.resize(sccDecRes.nonTrivialStates.getNumberOfSetBits());
121
122 // 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
123 std::span<uint64_t> sccCounts(sccIndications.begin() + 1, sccIndications.end());
124 for (auto state : sccDecRes.nonTrivialStates) {
125 ++sccCounts[sccDecRes.stateToSccMapping[state]];
126 }
127
128 // Now establish that sccCounts[i] points to the first entry in sccStates for SCC i
129 // This means that sccCounts[i] is the sum of all scc sizes for all SCCs j < i
130 uint64_t sum = 0;
131 for (auto& count : sccCounts) {
132 auto const oldSum = sum;
133 sum += count;
134 count = oldSum;
135 }
136
137 // Now fill the sccStates vector
138 for (auto state : sccDecRes.nonTrivialStates) {
139 auto const sccIndex = sccDecRes.stateToSccMapping[state];
140 sccStates[sccCounts[sccIndex]] = state;
141 ++sccCounts[sccIndex];
142 }
143
144 // At this point, sccCounts[i] points to the first entry in sccStates for SCC i+1
145 // Since sccCounts[i] = sccIndications[i+1], we are done already
146}
147
148template<typename ValueType>
149void MaximalEndComponentDecomposition<ValueType>::performMaximalEndComponentDecomposition(storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
150 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
153 // Get some data for convenient access.
154 auto const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
155
156 storm::storage::BitVector remainingEcCandidates, ecChoices;
157 SccDecompositionResult sccDecRes;
158 SccDecompositionMemoryCache sccDecCache;
159 StronglyConnectedComponentDecompositionOptions sccDecOptions;
160 sccDecOptions.dropNaiveSccs();
161 if (states) {
162 sccDecOptions.subsystem(*states);
163 }
164 if (choices) {
165 ecChoices = *choices;
166 sccDecOptions.choices(ecChoices);
167 } else {
168 ecChoices.resize(transitionMatrix.getRowCount(), true);
169 }
170
171 // Reserve storage for the mapping of SCC indices to
172 std::vector<uint64_t> ecSccStates, ecSccIndications;
173 auto getSccStates = [&ecSccStates, &ecSccIndications](uint64_t const sccIndex) {
174 return std::span(ecSccStates).subspan(ecSccIndications[sccIndex], ecSccIndications[sccIndex + 1] - ecSccIndications[sccIndex]);
175 };
176 while (true) {
177 performSccDecomposition(transitionMatrix, sccDecOptions, sccDecRes, sccDecCache);
178 getFlatSccDecomposition(sccDecRes, ecSccStates, ecSccIndications);
179
180 remainingEcCandidates = sccDecRes.nonTrivialStates;
181 storm::storage::BitVector ecSccIndices(sccDecRes.sccCount, true);
182 storm::storage::BitVector nonTrivSccIndices(sccDecRes.sccCount, false);
183 // find the choices that do not stay in their SCC
184 for (auto state : remainingEcCandidates) {
185 auto const sccIndex = sccDecRes.stateToSccMapping[state];
186 nonTrivSccIndices.set(sccIndex, true);
187 bool stateCanStayInScc = false;
188 for (auto const choice : transitionMatrix.getRowGroupIndices(state)) {
189 if (!ecChoices.get(choice)) {
190 continue;
191 }
192 auto row = transitionMatrix.getRow(choice);
193 if (std::any_of(row.begin(), row.end(), [&sccIndex, &sccDecRes](auto const& entry) {
194 return sccIndex != sccDecRes.stateToSccMapping[entry.getColumn()] && !storm::utility::isZero(entry.getValue());
195 })) {
196 ecChoices.set(choice, false); // The choice leaves the SCC
197 ecSccIndices.set(sccIndex, false); // This SCC is not 'stable' yet
198 } else {
199 stateCanStayInScc = true; // The choice stays in the SCC
200 }
201 }
202 if (!stateCanStayInScc) {
203 remainingEcCandidates.set(state, false); // This state is not in an EC
204 }
205 }
206
207 // process the MECs that we've found, i.e. SCCs where every state can stay inside the SCC
208 ecSccIndices &= nonTrivSccIndices;
209 for (auto sccIndex : ecSccIndices) {
210 MaximalEndComponent newMec;
211 for (auto const state : getSccStates(sccIndex)) {
212 // This is no longer a candidate
213 remainingEcCandidates.set(state, false);
214 // Add choices to the MEC
215 MaximalEndComponent::set_type containedChoices;
216 for (auto ecChoiceIt = ecChoices.begin(nondeterministicChoiceIndices[state]); *ecChoiceIt < nondeterministicChoiceIndices[state + 1];
217 ++ecChoiceIt) {
218 containedChoices.insert(*ecChoiceIt);
219 }
220 STORM_LOG_ASSERT(!containedChoices.empty(), "The contained choices of any state in an MEC must be non-empty.");
221 newMec.addState(state, std::move(containedChoices));
222 }
223 this->blocks.emplace_back(std::move(newMec));
224 }
225
226 if (nonTrivSccIndices == ecSccIndices) {
227 // All non trivial SCCs are MECs, nothing left to do!
228 break;
229 }
230
231 // prepare next iteration.
232 // It suffices to keep the candidates that have the possibility to always stay in the candidate set
233 remainingEcCandidates = storm::utility::graph::performProbGreater0A(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions,
234 remainingEcCandidates, ~remainingEcCandidates, false, 0, ecChoices);
235 remainingEcCandidates.complement();
236 sccDecOptions.subsystem(remainingEcCandidates);
237 sccDecOptions.choices(ecChoices);
238 }
239
240 STORM_LOG_DEBUG("MEC decomposition found " << this->size() << " MEC(s).");
241}
242
243// Explicitly instantiate the MEC decomposition.
244template class MaximalEndComponentDecomposition<double>;
246
247template class MaximalEndComponentDecomposition<storm::RationalNumber>;
250
251template class MaximalEndComponentDecomposition<storm::Interval>;
254
255template class MaximalEndComponentDecomposition<storm::RationalFunction>;
258
259} // namespace storage
260} // 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:18
void complement()
Negates all bits in the bit vector.
void resize(uint_fast64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
bool get(uint_fast64_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:23
#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:857
LabParser.cpp.
Definition cli.cpp:18
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.