Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
Initialization.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <map>
5
13#include "storm/logic/Formula.h"
22
23namespace storm::bisimulation {
24
25namespace {
26auto resolveStateLabelPreservationOption(StateLabelPreservation input, bool haveFormulas) {
27 using enum StateLabelPreservation;
28 if (input == Default) {
29 return haveFormulas ? FormulaPropositional : All;
30 }
31 return input;
32}
33
34} // namespace
35template<typename ValueType>
37 auto const stateLabelPreservation = resolveStateLabelPreservationOption(options.stateLabelPreservation, !formulas.empty());
39 // Add all labels that appear in all formulas
40 for (auto const& f : formulas) {
41 f->gatherReferencedRewardModels(information.preservedRewardModels);
42 // A reward operator without a reward model name refers to the unique reward model of the model, cf. Model::getRewardModel.
43 if (information.preservedRewardModels.contains("") && !model.getRewardModels().contains("")) {
44 information.preservedRewardModels.erase("");
45 information.preservedRewardModels.insert(model.getUniqueRewardModelName());
46 }
47 if (stateLabelPreservation != StateLabelPreservation::None) {
48 for (auto const& l : f->getAtomicLabelFormulas()) {
49 information.preservedStateLabels.insert(l->getLabel());
50 }
51 for (auto const& e : f->getAtomicExpressionFormulas()) {
52 information.preservedStateLabels.insert(e->toString());
53 }
54 }
55 STORM_LOG_ASSERT(std::all_of(information.preservedStateLabels.begin(), information.preservedStateLabels.end(),
56 [this](std::string const& label) { return model.getStateLabeling().containsLabel(label); }),
57 "Formula " << *f << " uses a label that is not known in the model.");
58 STORM_LOG_ASSERT(std::all_of(information.preservedRewardModels.begin(), information.preservedRewardModels.end(),
59 [this](std::string const& rew) { return model.getRewardModels().contains(rew); }),
60 "Formula " << *f << " uses a reward model that is not known in the model.");
61 }
62 // if requested, also add all state labels of the model
63 if (stateLabelPreservation == StateLabelPreservation::All) {
64 for (auto const& label : model.getStateLabeling().getLabels()) {
65 if (label == "init") {
66 // The quotient gets the "init" label from the union over the states of a block. Preserving it would only make the quotient finer without need.
67 // Note that a formula referring to "init" still adds the label in the loop above, i.e. "init" is preserved iff a formula actually uses it.
68 continue;
69 }
70 information.preservedStateLabels.insert(label);
71 }
72 }
73 if (options.preserveAllRewards.value_or(formulas.empty())) {
74 for (auto const& [name, rm] : model.getRewardModels()) {
75 information.preservedRewardModels.insert(name);
76 }
77 }
78 // if requested and available, preserve the choice labels
79 if (options.preserveChoiceLabels && model.hasChoiceLabeling()) {
80 for (auto const& label : model.getChoiceLabeling().getLabels()) {
81 information.preservedChoiceLabels.insert(label);
82 }
83 }
84 return information;
85}
86
87template<typename ValueType>
89 std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas)
90 : model(model), options(options), formulas(formulas) {
91 // sanity checks
92 if (options.bisimulationType == BisimulationType::Weak) {
93 STORM_LOG_THROW(model.isOfType(storm::models::ModelType::Dtmc) || model.isOfType(storm::models::ModelType::Ctmc),
94 storm::exceptions::NotSupportedException,
95 "Weak bisimulation is only implemented for DTMCs and CTMCs, but the given model is of type " << model.getType() << ".");
96 // Weak bisimulation generally does not preserve formulas that depend on the number of steps taken.
97 storm::logic::FragmentSpecification preservedFragment = storm::logic::propositional();
98 preservedFragment.setProbabilityOperatorsAllowed(true)
99 .setUntilFormulasAllowed(true)
100 .setReachabilityProbabilityFormulasAllowed(true)
101 .setGloballyFormulasAllowed(true)
102 .setRewardOperatorsAllowed(true)
103 .setReachabilityRewardFormulasAllowed(true);
104 if (model.isOfType(storm::models::ModelType::Ctmc)) {
105 // Weak bisimulation on a CTMC preserves the distribution of the time spent within a block, so time bounds and expected times are fine.
106 // Step (and reward) bounds are not, which is why we only enable the time-based cases here.
107 preservedFragment.setBoundedUntilFormulasAllowed(true)
108 .setTimeBoundedUntilFormulasAllowed(true)
109 .setStepBoundedUntilFormulasAllowed(false)
110 .setRewardOperatorsAllowed(false)
111 .setTimeOperatorsAllowed(true)
112 .setReachbilityTimeFormulasAllowed(true);
113 }
114 std::string unsupportedFormulas;
115 for (auto const& f : this->formulas) {
116 if (!f->isInFragment(preservedFragment)) {
117 unsupportedFormulas += (unsupportedFormulas.empty() ? "" : ", ") + f->toString();
118 }
119 }
120 STORM_LOG_THROW(unsupportedFormulas.empty(), storm::exceptions::IllegalFunctionCallException,
121 "The formula(s) " << unsupportedFormulas << " are not known to be preserved by weak bisimulation.");
122 }
123
124 auto const preservationInformation = getPreservationInformation();
125 auto const stateLabelPreservation = resolveStateLabelPreservationOption(options.stateLabelPreservation, !formulas.empty());
126
127 // Go through all relevant labels / rewards / model components and add them to the corresponding preserved annotations.
128 if (stateLabelPreservation != StateLabelPreservation::FormulaPropositional) {
129 // Each preserved label is considered on its own. Note that nothing is preserved for StateLabelPreservation::None.
130 for (auto const& label : preservationInformation.preservedStateLabels) {
131 preservedStateAnnotations.addBoolean(model.getStateLabeling().getStates(label));
132 }
133 } else {
134 // The formulas only observe the labels through their maximal propositional subformulas, so it suffices to preserve the truth values of those.
136 PropositionalChecker propositionalChecker(model);
137 auto const collectPropositionalStates = [this, &propositionalChecker](storm::logic::Formula const& subformula) {
138 if (!propositionalChecker.canHandle(subformula)) {
139 // The checker handles exactly the propositional formulas, so we have to continue with the subformulas.
140 return true;
141 } else if (subformula.isBooleanLiteralFormula()) {
142 // Boolean literals do not distinguish any states, so we can skip them.
143 return false;
144 }
145 auto states = propositionalChecker.check(subformula)
146 ->template asExplicitQualitativeCheckResult<typename PropositionalChecker::SolutionType>()
147 .getTruthValuesVector();
148 // A subformula that holds in all or no states, or in the same states as a previous one, does not distinguish any further states.
149 if (!states.empty() && !states.full() &&
150 std::ranges::none_of(preservedStateAnnotations.getBooleans(), [&states](auto const& b) { return b == states; })) {
151 preservedStateAnnotations.addBoolean(std::move(states));
152 }
153 return false;
154 };
155 for (auto const& f : this->formulas) {
156 f->traverse(collectPropositionalStates);
157 }
158 // If a formula considers the init label, we need to preserve it precisely. Otherwise, e.g. "P=? [ F "init" & "a" ]" would be handled incorrectly:
159 // A block containing some "init"-states and some "a"-states (but no "init" & "a"-states) could get collapsed into a state with both, the "init"-label
160 // and the "a" label (obtained through the representative state)
161 if (preservationInformation.preservedStateLabels.contains("init")) {
162 preservedStateAnnotations.addBoolean(model.getStateLabeling().getStates("init"));
163 }
164 }
165 for (auto const& label : preservationInformation.preservedChoiceLabels) {
166 STORM_LOG_ASSERT(model.hasChoiceLabeling(), "Preserving choice labels is only possible if the model has a choice labeling.");
167 preservedChoiceAnnotations.addBoolean(model.getChoiceLabeling().getChoices(label));
168 }
169 if (model.hasChoiceOrigins() && options.preserveChoiceOrigins) {
170 preservedChoiceAnnotations.integers.emplace_back(model.getChoiceOrigins()->getIdentifiers());
171 }
172 for (auto const& rewName : preservationInformation.preservedRewardModels) {
173 auto const& rewardModel = model.getRewardModel(rewName);
174 STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException,
175 "Bisimulation initialization does not handle transition rewards of reward model '" << rewName << "'.");
176 // Weak bisimulation on a CTMC drops the transitions within a block. The state rewards of a CTMC are rate rewards, so they are unaffected (the
177 // distribution of the time spent within a block is preserved), but the state-action rewards are earned per taken transition and thus are not.
179 !(options.bisimulationType == BisimulationType::Weak && model.isOfType(storm::models::ModelType::Ctmc) && rewardModel.hasStateActionRewards()),
180 storm::exceptions::NotSupportedException,
181 "Weak bisimulation on CTMCs does not preserve the state-action rewards of reward model '" << rewName << "'.");
182 if (rewardModel.hasStateRewards()) {
183 preservedStateAnnotations.values.emplace_back(rewardModel.getStateRewardVector());
184 }
185 if (rewardModel.hasStateActionRewards()) {
186 preservedChoiceAnnotations.values.emplace_back(rewardModel.getStateActionRewardVector());
187 }
188 }
189 using enum storm::models::ModelType;
190 if (model.isOfType(MarkovAutomaton)) {
191 auto const& ma = model.template as<storm::models::sparse::MarkovAutomaton<ValueType>>();
192 preservedStateAnnotations.addBoolean(ma->getMarkovianStates());
193 preservedStateAnnotations.values.emplace_back(ma->getExitRates());
194 if (!ma->isClosed()) {
195 // A hybrid state has a Markovian choice (its first one) next to probabilistic ones. Those must never be merged with each other, which the
196 // distributions alone do not rule out.
197 storm::storage::BitVector markovianChoices(model.getNumberOfChoices(), false);
198 for (uint64_t const markovianState : ma->getMarkovianStates()) {
199 markovianChoices.set(model.getTransitionMatrix().getRowGroupIndices()[markovianState]);
200 }
201 preservedChoiceAnnotations.addBoolean(std::move(markovianChoices));
202 }
203 } else {
204 STORM_LOG_THROW(model.isOfType(Dtmc) || model.isOfType(Ctmc) || model.isOfType(Mdp), storm::exceptions::NotSupportedException,
205 "Bisimulation initialization is not implemented for model type '" << model.getType() << "'.");
206 }
207}
208
209template<typename ValueType>
210void Initialization<ValueType>::PreservedAnnotations::addBoolean(storm::storage::BitVector const& annotation) {
211 optionallyOwnedBooleans.emplace_back(std::cref(annotation));
212}
213
214template<typename ValueType>
215void Initialization<ValueType>::PreservedAnnotations::addBoolean(storm::storage::BitVector&& annotation) {
216 optionallyOwnedBooleans.emplace_back(std::move(annotation));
217}
218
219template<typename ValueType>
220bool Initialization<ValueType>::PreservedAnnotations::empty() const {
221 return optionallyOwnedBooleans.empty() && integers.empty() && values.empty();
222}
223
224template<typename ValueType>
225void Initialization<ValueType>::PreservedAnnotations::applySplit(Partition& partition, ValueType const& tolerance,
226 std::vector<uint64_t> const& extraAnnotation) const {
227 uint64_t const numElements = partition.getNumberOfElements();
228
229 // Split according to Boolean annotations
230 for (auto const& b : getBooleans()) {
231 STORM_LOG_ASSERT(numElements == b.size(), "Boolean annotation has wrong size.");
232 partition.forEachBlock([&b, &numElements, &partition](auto const& block) {
233 // No need to split singleton blocks.
234 if (block.size() > 1) {
235 // For large blocks, it's likely cheaper to iterate over the 'true' elements of b
236 if (block.size() * 64 > numElements) {
237 partition.splitBlockByRange(block, b);
238 } else {
239 partition.splitBlockByPredicate(block, [&b](uint64_t const e) { return b.get(e); });
240 }
241 }
242 });
243 }
244
245 // Helper for the remaining numeric annotations
246 // TODO: Consider extending storm::utility::ConstantsComparator::isLess to handle the comparison to zero below and use the comparator here.
247 auto splitByNumeric = [&numElements, &partition, &tolerance](auto const& v) {
248 STORM_LOG_ASSERT(numElements == v.size(), "Annotation has wrong size.");
249 auto const less = [&v](auto const& a, auto const& b) { return v[a] < v[b]; };
250 if constexpr (std::is_same_v<ValueType, typename std::remove_cvref_t<decltype(v)>::value_type>) {
251 if (!storm::utility::isZero(tolerance)) {
252 auto const lessTol = [&v, &tolerance](auto const& a, auto const& b) {
253 // A zero value is never grouped with a non-zero one, whether an annotation (e.g. reward) is zero can be semantically relevant
255 return v[a] < v[b];
256 }
257 return v[a] + tolerance < v[b];
258 };
259 partition.forEachBlock([&partition, &less, &lessTol](auto const& block) {
260 if (block.size() > 1) { // No need to split singleton blocks
261 partition.splitBlockByOrder(block, less, lessTol);
262 }
263 });
264 return;
265 }
266 }
267 partition.forEachBlock([&partition, &less](auto const& block) {
268 if (block.size() > 1) {
269 partition.splitBlockByOrder(block, less);
270 }
271 });
272 };
273
274 for (auto const& v : integers) {
275 splitByNumeric(v);
276 }
277 for (auto const& v : values) {
278 splitByNumeric(v);
279 }
280 if (!extraAnnotation.empty()) {
281 splitByNumeric(extraAnnotation);
282 }
283}
284
285template<typename ValueType>
286std::optional<std::vector<uint64_t>> Initialization<ValueType>::getChoiceClasses() const {
287 // Terminate early if there aren't any choice classes.
288 if (preservedChoiceAnnotations.empty() && (!options.actionSensitive || !model.isNondeterministicModel())) {
289 return std::nullopt;
290 }
291 // Auxiliary vector, will later hold the resulting classes.
292 std::vector<uint64_t> auxVector;
293
294 // Create a partition of the choices
295 Partition choicePartition(model.getNumberOfChoices());
296 if (options.actionSensitive && model.isNondeterministicModel()) {
297 auxVector.reserve(model.getNumberOfChoices());
298 // fill the auxVector with the local choice indices and treat it as any other annotation.
299 for (uint64_t state = 0; state < model.getNumberOfStates(); ++state) {
300 for (uint64_t act = 0; act < model.getTransitionMatrix().getRowGroupSize(state); ++act) {
301 auxVector.push_back(act);
302 }
303 }
304 }
305 // Split the partition based on preserved choice annotations.
306 preservedChoiceAnnotations.applySplit(choicePartition, storm::utility::convertNumber<ValueType>(options.tolerance.value()), auxVector);
307
308 // Catch the case where all choices are equal so we don't have to deal with choice classes.
309 if (choicePartition.getNumberOfBlocks() == 1) {
310 return std::nullopt;
311 }
312
313 // Prepare the result
314 auxVector.resize(model.getNumberOfChoices());
315 uint64_t choiceClass = 0;
316 choicePartition.forEachBlock([&choiceClass, &auxVector](auto const& block) {
317 for (uint64_t const choice : block) {
318 auxVector[choice] = choiceClass;
319 }
320 ++choiceClass;
321 });
322 return std::optional<std::vector<uint64_t>>(std::move(auxVector));
323}
324
325template<typename ValueType>
326Partition Initialization<ValueType>::getInitialStatePartition(std::optional<std::vector<uint64_t>> const& choiceClasses) const {
327 ValueType const tolerance = storm::utility::convertNumber<ValueType>(options.tolerance.value());
328 Partition statePartition(model.getNumberOfStates());
329 if (choiceClasses && model.isNondeterministicModel()) {
330 auto const& groupIndices = model.getTransitionMatrix().getRowGroupIndices();
331 // We need to distinguish the states by the available choice classes
332 // We compute for each state the actionSignature, which is the set of available choice classes represented as ascending, deduplicated vector
333 using Signature = std::vector<uint64_t>;
334 std::vector<uint64_t> stateActionSignature;
335 stateActionSignature.reserve(model.getNumberOfStates());
336 std::map<Signature, uint64_t> actionSignatureToIndex;
337 for (uint64_t state = 0; state < model.getNumberOfStates(); ++state) {
338 // Compute signature
339 Signature actionSignature(choiceClasses->begin() + groupIndices[state], choiceClasses->begin() + groupIndices[state + 1]);
340 std::sort(actionSignature.begin(), actionSignature.end());
341 actionSignature.erase(std::unique(actionSignature.begin(), actionSignature.end()), actionSignature.end());
342 // Retrieve the index of the signature, potentially adding a new signature if it is not already known.
343 uint64_t const signatureIndex = actionSignatureToIndex.emplace(actionSignature, actionSignatureToIndex.size()).first->second;
344 // Store the signature index for this state.
345 stateActionSignature.push_back(signatureIndex);
346 }
347 // split the partition based on preserved state annotations and the state action signatures.
348 preservedStateAnnotations.applySplit(statePartition, tolerance, stateActionSignature);
349 } else if (choiceClasses && !model.isNondeterministicModel()) {
350 STORM_LOG_ASSERT(choiceClasses->size() == model.getNumberOfStates(), "Unexpected number of choice classes.");
351 // As there is exactly one choice per state, there is no need for computing the action signatures
352 preservedStateAnnotations.applySplit(statePartition, tolerance, choiceClasses.value());
353 } else {
354 preservedStateAnnotations.applySplit(statePartition, tolerance);
355 }
356 return statePartition;
357}
358
359namespace detail {
360
364template<typename ValueType>
366 storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Partition const& partition) {
367 // We compute the complement: a state with a transition leaving its block is non-divergent, and so is every state that can reach such a state without
368 // leaving the block. We do a backwards DFS from the states that are at the boundary of the block.
369 storm::storage::BitVector divergentStates(partition.getNumberOfElements(), true);
370 std::vector<uint64_t> stateStack;
371 partition.forEachBlock([&transitions, &backwardTransitions, &partition, &divergentStates, &stateStack](Partition::Block const& block) {
372 // Find the states that can leave the block in a single step.
373 for (uint64_t const state : block) {
374 for (auto const& entry : transitions.getRow(state)) {
375 if (!storm::utility::isZero(entry.getValue()) && !partition.isBlockOfElement(block, entry.getColumn())) {
376 divergentStates.set(state, false);
377 stateStack.push_back(state);
378 break;
379 }
380 }
381 }
382 // Everything that reaches one of those can leave the block as well.
383 while (!stateStack.empty()) {
384 uint64_t const currentState = stateStack.back();
385 stateStack.pop_back();
386 for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) {
387 uint64_t const predecessor = predecessorEntry.getColumn();
388 if (divergentStates.get(predecessor) && partition.isBlockOfElement(block, predecessor)) {
389 divergentStates.set(predecessor, false);
390 stateStack.push_back(predecessor);
391 }
392 }
393 }
394 });
395 return divergentStates;
396}
397
401template<typename ValueType>
403 storm::storage::BitVector silentStates(partition.getNumberOfElements(), false);
404 for (uint64_t state = 0; state < partition.getNumberOfElements(); ++state) {
405 silentStates.set(state, isSilentState(transitions, partition, state));
406 }
407 return silentStates;
408}
409
410} // namespace detail
411
412template<typename ValueType>
414 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
415 PreservationInformation const& preservationInformation) const {
416 STORM_LOG_ASSERT(options.bisimulationType == BisimulationType::Weak, "Weak bisimulation data requested for a non-weak bisimulation.");
417 STORM_LOG_ASSERT(!model.isNondeterministicModel(), "Weak bisimulation is only supported for deterministic models.");
418 uint64_t const numberOfStates = model.getNumberOfStates();
419 // Identify step-sensitive states: those states where the number of block-internal steps taken affects the semantics. This concerns rewards in DTMC.
420 // We will apply strong bisimulation on those states.
421 // The result is homogeneous per block, since the initial partition never groups a zero reward with a non-zero one, cf. PreservedAnnotations::applySplit.
422 storm::storage::BitVector stepSensitiveStates(numberOfStates, false);
423 if (model.isDiscreteTimeModel()) {
424 for (auto const& rewardModelName : preservationInformation.preservedRewardModels) {
425 stepSensitiveStates |= ~model.getRewardModel(rewardModelName).getStatesWithZeroReward(model.getTransitionMatrix());
426 }
427 } else {
428 // This step does not apply to CTMC: The state rewards of a CTMC are rate rewards. We have already excluded non-state rewards for weak bisimulation
429 // above. Weak bisimulation preserves the distribution of the time spent within a block and thus the accumulated (state) reward.
430 STORM_LOG_ASSERT(std::all_of(preservationInformation.preservedRewardModels.begin(), preservationInformation.preservedRewardModels.end(),
431 [this](std::string const& rewardModelName) {
432 auto const& rewardModel = model.getRewardModel(rewardModelName);
433 return !rewardModel.hasStateActionRewards() && !rewardModel.hasTransitionRewards();
434 }),
435 "Weak bisimulation on a continuous-time model preserves a reward model with state-action or transition rewards, which the "
436 "constructor should have rejected.");
437 }
438
439 storm::storage::BitVector divergentStates = detail::computeDivergentStates(model.getTransitionMatrix(), backwardTransitions, partition);
440
441 // Split off the divergent states so that the partition becomes homogeneous. Note that this cannot invalidate the step sensitive states, since a block is
442 // only ever split into sub-blocks that all carry the flag of their super-block.
443 if (!divergentStates.empty()) {
444 partition.forEachBlock([&partition, &divergentStates](Partition::Block const& block) {
445 if (block.size() > 1) {
446 partition.splitBlockByPredicate(block, [&divergentStates](uint64_t const state) { return divergentStates.get(state); });
447 }
448 });
449 }
450
451 WeakBisimulationData result(std::move(divergentStates), std::move(stepSensitiveStates),
452 detail::computeSilentStates(model.getTransitionMatrix(), partition));
453 STORM_LOG_ASSERT(result.checkBlockHomogeneity(partition), "Partition is not homogeneous with respect to the weak bisimulation data.");
454 return result;
455}
456
457template class Initialization<double>;
462
463} // namespace storm::bisimulation
storm::models::sparse::Dtmc< double > Dtmc
storm::models::sparse::Mdp< double > Mdp
Initialization(storm::models::sparse::Model< ValueType > const &model, Options const &options, std::vector< std::shared_ptr< storm::logic::Formula const > > const &formulas)
WeakBisimulationData getWeakBisimulationData(Partition &partition, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, PreservationInformation const &preservationInformation) const
Computes the state-level information that weak bisimulation requires on top of the partition,...
std::optional< std::vector< uint64_t > > getChoiceClasses() const
Partition getInitialStatePartition(std::optional< std::vector< uint64_t > > const &choiceClasses={}) const
PreservationInformation getPreservationInformation() const
Represents a partition of a set of consecutive indices.
Definition Partition.h:20
void forEachBlock(Func const &f) const
Applies the given function on each block in the partition.
Definition Partition.h:184
std::size_t getNumberOfBlocks() const
Retrieves the number of blocks.
Definition Partition.cpp:61
bool isBlockOfElement(Block const &block, ElementIndex const &element) const
std::size_t getNumberOfElements() const
Retrieves the number of elements.
Definition Partition.cpp:66
std::span< ElementIndex const > Block
A contiguous range of elements that form a (possibly proper super-) block of the partition.
Definition Partition.h:27
std::pair< Block, Block > splitBlockByPredicate(Block const &block, SplittingPredicate const &f)
Splits the given block according to the given predicate.
Definition Partition.h:329
Base class for all sparse models.
Definition Model.h:34
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
bool empty() const
Retrieves whether no bits 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.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
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.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
storm::storage::BitVector computeDivergentStates(storm::storage::SparseMatrix< ValueType > const &transitions, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, Partition const &partition)
Computes the states from which no state outside of their own block can be reached.
storm::storage::BitVector computeSilentStates(storm::storage::SparseMatrix< ValueType > const &transitions, Partition const &partition)
Computes the states all of whose transitions stay within their own block.
bool isSilentState(storm::storage::SparseMatrix< ValueType > const &transitions, Partition const &partition, uint64_t const state)
StateLabelPreservation
The way state labels will be preserved by the bisimulation.
Definition Options.h:14
SFTBDDChecker::ValueType ValueType
bool isZero(ValueType const &a)
Definition constants.cpp:44
TargetType convertNumber(SourceType const &number)
The state-level information that weak bisimulation requires on top of the partition.
bool checkBlockHomogeneity(Partition const &partition) const
Checks that every block of the given partition is homogeneous with respect to divergentStates and ste...