Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MaximalEndComponentDecomposition.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <span>
5#include <sstream>
6
11#include "storm/utility/graph.h"
12
13namespace storm {
14namespace storage {
15
16template<typename ValueType>
20
21template<typename ValueType>
22template<typename RewardModelType>
27
28template<typename ValueType>
30 storm::storage::SparseMatrix<ValueType> const& backwardTransitions) {
31 performMaximalEndComponentDecomposition(transitionMatrix, backwardTransitions);
32}
33
34template<typename ValueType>
36 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
37 storm::storage::BitVector const& states) {
38 performMaximalEndComponentDecomposition(transitionMatrix, backwardTransitions, states);
39}
40
41template<typename ValueType>
43 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
44 storm::storage::BitVector const& states,
45 storm::storage::BitVector const& choices) {
46 performMaximalEndComponentDecomposition(transitionMatrix, backwardTransitions, states, choices);
47}
48
49template<typename ValueType>
54
55template<typename ValueType>
59
60template<typename ValueType>
65
66template<typename ValueType>
70
71template<typename ValueType>
76
77template<typename ValueType>
78std::string MaximalEndComponentDecomposition<ValueType>::statistics(uint64_t totalNumberOfStates) const {
79 if (this->empty()) {
80 return "Empty MEC decomposition.";
81 }
82 uint64_t statesInMec = 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 STORM_LOG_ASSERT(!states.has_value() || transitionMatrix.getRowGroupCount() == states->size(), "Unexpected size of states bitvector.");
154 STORM_LOG_ASSERT(!choices.has_value() || transitionMatrix.getRowCount() == choices->size(), "Unexpected size of choices bitvector.");
155 // Get some data for convenient access.
156 auto const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
157
158 storm::storage::BitVector remainingEcCandidates, ecChoices;
159 SccDecompositionResult sccDecRes;
160 SccDecompositionMemoryCache sccDecCache;
161 StronglyConnectedComponentDecompositionOptions sccDecOptions;
162 sccDecOptions.dropNaiveSccs();
163 if (states) {
164 sccDecOptions.subsystem(*states);
165 }
166 if (choices) {
167 ecChoices = *choices;
168 sccDecOptions.choices(ecChoices);
169 } else {
170 ecChoices.resize(transitionMatrix.getRowCount(), true);
171 }
172
173 // Reserve storage for the mapping of SCC indices to
174 std::vector<uint64_t> ecSccStates, ecSccIndications;
175 auto getSccStates = [&ecSccStates, &ecSccIndications](uint64_t const sccIndex) {
176 return std::span(ecSccStates).subspan(ecSccIndications[sccIndex], ecSccIndications[sccIndex + 1] - ecSccIndications[sccIndex]);
177 };
178 while (true) {
179 performSccDecomposition(transitionMatrix, sccDecOptions, sccDecRes, sccDecCache);
180 getFlatSccDecomposition(sccDecRes, ecSccStates, ecSccIndications);
181
182 remainingEcCandidates = sccDecRes.nonTrivialStates;
183 storm::storage::BitVector ecSccIndices(sccDecRes.sccCount, true);
184 storm::storage::BitVector nonTrivSccIndices(sccDecRes.sccCount, false);
185 // find the choices that do not stay in their SCC
186 for (auto state : remainingEcCandidates) {
187 auto const sccIndex = sccDecRes.stateToSccMapping[state];
188 nonTrivSccIndices.set(sccIndex, true);
189 bool stateCanStayInScc = false;
190 for (auto const choice : transitionMatrix.getRowGroupIndices(state)) {
191 if (!ecChoices.get(choice)) {
192 continue;
193 }
194 auto row = transitionMatrix.getRow(choice);
195 if (std::any_of(row.begin(), row.end(), [&sccIndex, &sccDecRes](auto const& entry) {
196 return sccIndex != sccDecRes.stateToSccMapping[entry.getColumn()] && !storm::utility::isZero(entry.getValue());
197 })) {
198 ecChoices.set(choice, false); // The choice leaves the SCC
199 ecSccIndices.set(sccIndex, false); // This SCC is not 'stable' yet
200 } else {
201 stateCanStayInScc = true; // The choice stays in the SCC
202 }
203 }
204 if (!stateCanStayInScc) {
205 remainingEcCandidates.set(state, false); // This state is not in an EC
206 }
207 }
208
209 // process the MECs that we've found, i.e. SCCs where every state can stay inside the SCC
210 ecSccIndices &= nonTrivSccIndices;
211 for (auto sccIndex : ecSccIndices) {
212 MaximalEndComponent newMec;
213 for (auto const state : getSccStates(sccIndex)) {
214 // This is no longer a candidate
215 remainingEcCandidates.set(state, false);
216 // Add choices to the MEC
217 MaximalEndComponent::set_type containedChoices;
218 for (auto ecChoiceIt = ecChoices.begin(nondeterministicChoiceIndices[state]); *ecChoiceIt < nondeterministicChoiceIndices[state + 1];
219 ++ecChoiceIt) {
220 containedChoices.insert(*ecChoiceIt);
221 }
222 STORM_LOG_ASSERT(!containedChoices.empty(), "The contained choices of any state in an MEC must be non-empty.");
223 newMec.addState(state, std::move(containedChoices));
224 }
225 this->blocks.emplace_back(std::move(newMec));
226 }
227
228 if (nonTrivSccIndices == ecSccIndices) {
229 // All non trivial SCCs are MECs, nothing left to do!
230 break;
231 }
232
233 // prepare next iteration.
234 // It suffices to keep the candidates that have the possibility to always stay in the candidate set
235 remainingEcCandidates = storm::utility::graph::performProbGreater0A(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions,
236 remainingEcCandidates, ~remainingEcCandidates, false, 0, ecChoices);
237 remainingEcCandidates.complement();
238 sccDecOptions.subsystem(remainingEcCandidates);
239 sccDecOptions.choices(ecChoices);
240 }
241
242 STORM_LOG_DEBUG("MEC decomposition found " << this->size() << " MEC(s).");
243}
244
245// Explicitly instantiate the MEC decomposition.
247template MaximalEndComponentDecomposition<double>::MaximalEndComponentDecomposition(storm::models::sparse::NondeterministicModel<double> const& model);
248
251 storm::models::sparse::NondeterministicModel<storm::RationalNumber> const& model);
252
255 storm::models::sparse::NondeterministicModel<storm::Interval> const& model);
256
259 storm::models::sparse::NondeterministicModel<storm::RationalInterval> const& model);
260
263 storm::models::sparse::NondeterministicModel<storm::RationalFunction> const& model);
264
265} // namespace storage
266} // namespace storm
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
bool has_value() const
Yields true iff this contains a reference.
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:198
storm::storage::SparseMatrix< ValueType > getBackwardTransitions() const
Retrieves the backward transition relation of the model, i.e.
Definition Model.cpp:158
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
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.
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-...
This class represents a maximal end-component of a nondeterministic model.
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.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
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:841
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.