Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SplitterBasedRefinement.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdint>
5#include <deque>
6#include <limits>
7#include <ranges>
8#include <type_traits>
9#include <vector>
10
21
22namespace storm::bisimulation {
23
24namespace detail {
25
26template<typename ValueType, SplitterRefinementMode Mode>
28 static constexpr bool isWeak = Mode != SplitterRefinementMode::Strong;
30
36
40 bool isStepSensitive(uint64_t const state) const
41 requires isWeak
42 {
43 if constexpr (isWeakDiscreteTime) {
44 return weakData->stepSensitiveStates.get(state);
45 } else {
46 // We ruled out all CTMCs with step-sensitive behavior (e.g. action-based rewards) during initialization.
47 return false;
48 }
49 }
50
54 ValueType const tolerance;
56
59
66
71 conditionalValues(partition.getNumberOfElements(), storm::utility::zero<ValueType>()),
72 temporaryStateClasses(partition.getNumberOfElements(), std::numeric_limits<uint64_t>::max()) {}
73
74 std::vector<ValueType> conditionalValues; // stores the 1-step probability of leaving the block for each state, conditioned on leaving the block at all
75 std::vector<uint64_t> frontierStates; // the non-silent states of the block currently being refined
76 std::vector<uint64_t> temporaryStateClasses; // temporarily classifies the states of the block currently being refined
77 std::deque<uint64_t> bfsQueue; // work list of the backward search that computes temporaryStateClasses
78 std::vector<uint64_t> nonSilentCandidates; // collects candidates of states that might become non-silent after refinement
79 };
80
82 using Cache = std::conditional_t<isWeakDiscreteTime, WeakDiscreteTimeCache, DefaultCache>;
84};
85
89template<typename ValueType, SplitterRefinementMode Mode>
91 requires(Mode != SplitterRefinementMode::Strong)
92{
93 // A state can only ever go from silent to non-silent.
94 for (uint64_t const state : candidates) {
95 context.weakData->silentStates.set(state, isSilentState(context.model.getTransitionMatrix(), context.partition, state));
96 }
97}
98
102template<typename ValueType, SplitterRefinementMode Mode>
104 requires(Mode != SplitterRefinementMode::Strong)
105{
106 for (uint64_t state = 0; state < context.partition.getNumberOfElements(); ++state) {
107 if (context.weakData->silentStates.get(state) != isSilentState(context.model.getTransitionMatrix(), context.partition, state)) {
108 return false;
109 }
110 }
111 return true;
112}
113
123template<typename ValueType, SplitterRefinementMode Mode>
125 bool const wasInQueue = context.queue.erase(splitBlock) > 0;
127 context.partition.forEachSubBlock(splitBlock, [&context, &largestSubBlock](auto const& subBlock) {
128 context.queue.insert(subBlock);
129 if (subBlock.size() > largestSubBlock.size()) {
130 largestSubBlock = subBlock;
131 }
132 });
133 if (Mode == SplitterRefinementMode::Strong && !wasInQueue) {
134 context.queue.erase(largestSubBlock);
135 }
136}
137
143template<typename ValueType, SplitterRefinementMode Mode>
145 auto& predecessorToSplitterProbabilities = context.cache.predecessorToSplitterProbabilities;
146
147 // First split the block by whether a state is a predecessor of the splitter block or not
148 // We do this by either iterating over the splitterPredecessors or the predecessorBlockToSplit, depending on what is shorter.
149 auto [noPredecessors, predecessors] =
150 predecessorToSplitterProbabilities.getNonDefaultStates().size() < predecessorBlockToSplit.size()
151 ? context.partition.splitBlockByRange(predecessorBlockToSplit, predecessorToSplitterProbabilities.getNonDefaultStates())
153 predecessorBlockToSplit, [&predecessorToSplitterProbabilities](
154 auto const& state) { return !storm::utility::isZero(predecessorToSplitterProbabilities.getValues()[state]); });
155
156 STORM_LOG_ASSERT(!predecessors.empty(), "The predecessor block should contain at least one predecessor state.");
157 bool wasSplit = noPredecessors.size() > 0;
158
159 // Splitting with interval probabilities is not trivial: it is not clear whether the entire toSplitterProbs interval (which might be the sum of several
160 // transitions) is feasible.
161 static_assert(!storm::IsIntervalType<ValueType>, "Interval-valued splitter probabilities are not supported.");
162 auto const& toSplitterProbs = predecessorToSplitterProbabilities.getValues();
163 auto const less = [&toSplitterProbs](uint64_t const state1, uint64_t const state2) { return toSplitterProbs[state1] < toSplitterProbs[state2]; };
164 if (storm::utility::isZero(context.tolerance)) {
165 // Attention: Do not short circuit, i.e., wasSplit = wasSplit || foo() might not execute foo()
166 wasSplit |= context.partition.splitBlockByOrder(predecessors, less);
167 } else {
168 auto const lessTolerance = [&toSplitterProbs, &context](uint64_t const state1, uint64_t const state2) {
169 return toSplitterProbs[state1] + context.tolerance < toSplitterProbs[state2];
170 };
171 // Attention: Do not short circuit
172 wasSplit |= context.partition.splitBlockByOrder(predecessors, less, lessTolerance);
173 }
174
175 if (wasSplit) {
176 enqueueSubBlocks(context, predecessorBlockToSplit);
177 }
178}
179
187template<typename ValueType>
189 storm::bisimulation::Partition::Block const splitterBlock) {
190 STORM_LOG_ASSERT(!context.weakData->isDivergent(block), "Assumed a non-divergent block as a predecessor block of the splitter.");
191
192 // Step 1: Gather the non-silent states of the given block. If the block is large, it is usually faster to iterate over the non-silent states
193 auto& frontierStates = context.cache.frontierStates;
194 auto const& silentStates = context.weakData->silentStates;
195 frontierStates.clear();
196 if (block.size() * 64 > silentStates.size()) {
197 for (uint64_t nonSilentState = silentStates.getNextUnsetIndex(0); nonSilentState < silentStates.size();
198 nonSilentState = silentStates.getNextUnsetIndex(nonSilentState + 1)) {
199 if (context.partition.contains(block, nonSilentState)) {
200 frontierStates.push_back(nonSilentState);
201 }
202 }
203 } else {
204 for (uint64_t const state : block) {
205 if (!silentStates.get(state)) {
206 frontierStates.push_back(state);
207 }
208 }
209 }
210
211 // Step 2: Compute conditional escape probability at frontier states
212 auto& conditionalValues = context.cache.conditionalValues;
213 auto const& toSplitterProbs = context.cache.predecessorToSplitterProbabilities.getValues();
214 for (uint64_t const state : frontierStates) {
215 if (storm::utility::isZero(toSplitterProbs[state])) {
216 conditionalValues[state] = storm::utility::zero<ValueType>(); // the state has no transition into the splitter
217 continue;
218 }
219 auto const row = context.model.getTransitionMatrix().getRow(state);
220 STORM_LOG_ASSERT(std::any_of(row.begin(), row.end(),
221 [&context, &splitterBlock](auto const& entry) {
222 return !storm::utility::isZero(entry.getValue()) && context.partition.contains(splitterBlock, entry.getColumn());
223 }),
224 "Expected a transition into a splitter, but none was found.");
225 ValueType escapeValue = storm::utility::zero<ValueType>();
226 bool leavesOnlyToSplitter = true;
227 for (auto const& entry : row) {
228 if (storm::utility::isZero(entry.getValue()) || context.partition.isSameBlock(state, entry.getColumn())) {
229 continue; // moves within the own block are unobservable
230 }
231 escapeValue += entry.getValue();
232 leavesOnlyToSplitter = leavesOnlyToSplitter && context.partition.contains(splitterBlock, entry.getColumn());
233 }
234 STORM_LOG_ASSERT(!storm::utility::isZero(escapeValue), "A non-silent state must be able to leave its block.");
235
236 // If there are only transitions to the splitter, we set the conditional probability to 1 explicitly.
237 // This avoids numerical issues where toSplitterProbs[state] and escapeValue do not match exactly.
238 conditionalValues[state] = leavesOnlyToSplitter ? storm::utility::one<ValueType>() : toSplitterProbs[state] / escapeValue;
239 }
240 // Every state of a non-divergent block can leave the block; the first state that is non-silent on such a path witnesses this.
241 STORM_LOG_ASSERT(!frontierStates.empty(), "A non-divergent block must contain a non-silent state.");
242
243 // Step 3: Divide the frontier states into equivalence classes based on their conditional values
244 auto& stateClasses = context.cache.temporaryStateClasses;
245 uint64_t constexpr Unclassified = std::numeric_limits<uint64_t>::max(); // Indicates that no class has been assigned to a state
246 uint64_t constexpr MultipleFrontiers =
247 std::numeric_limits<uint64_t>::max() - 1; // Indicates that multiple frontier states with different conditional values can be reached
248
249 std::sort(frontierStates.begin(), frontierStates.end(),
250 [&conditionalValues](uint64_t const state1, uint64_t const state2) { return conditionalValues[state1] < conditionalValues[state2]; });
251 {
252 // true if state1 and state2 should be in different classes. Assumes conditionalValues[state1] <= conditionalValues[state2].
253 auto const haveDifferentClass = [&conditionalValues, &context](uint64_t const state1, uint64_t const state2) {
254 // Whether a state can move to the splitter at all is not subject to the tolerance
255 if (storm::utility::isZero(context.tolerance) || storm::utility::isZero(conditionalValues[state1]) ||
256 storm::utility::isZero(conditionalValues[state2])) {
257 return conditionalValues[state1] != conditionalValues[state2];
258 }
259 return conditionalValues[state1] + context.tolerance < conditionalValues[state2];
260 };
261
262 uint64_t currClassRepresentative = frontierStates.front();
263 stateClasses[currClassRepresentative] = currClassRepresentative;
264 for (uint64_t const state : frontierStates | std::views::drop(1)) {
265 if (haveDifferentClass(currClassRepresentative, state)) {
266 currClassRepresentative = state;
267 }
268 stateClasses[state] = currClassRepresentative;
269 }
270 }
271
272 // Step 4: Catch the trivial case where all states have the same class
273 if (stateClasses[frontierStates.front()] == stateClasses[frontierStates.back()]) {
274 // Just a single class, nothing to split.
275 // Clean-up the touched classes before return.
276 for (uint64_t const state : frontierStates) {
277 stateClasses[state] = Unclassified;
278 }
279 return;
280 }
281
282 // Step 5: Classify the remaining states. Two states state1 and state2 are in the same class iff
283 // - a) all frontier states reachable from state1 or from state2 are in the same class, or
284 // - b) both, state1 and state2 each reach multiple frontier states with distinct classes.
285 // We do a backwards search from the frontier states, propagating their class labels to their predecessors.
286 // A BFS is used (rather than a DFS) to detect case b) more quickly.
287 auto& bfsQueue = context.cache.bfsQueue;
288 bfsQueue.clear();
289 for (auto const state : frontierStates) {
290 bfsQueue.push_back(state);
291 }
292 // We also collect candidates of states that might lose their silence after refinement: the states for which case b) applies that have a case a) successor.
293 std::vector<uint64_t>& nonSilentCandidates = context.cache.nonSilentCandidates;
294 nonSilentCandidates.clear();
295 while (!bfsQueue.empty()) {
296 uint64_t const currentState = bfsQueue.front();
297 bfsQueue.pop_front();
298 uint64_t const currentClass = stateClasses[currentState];
299 STORM_LOG_ASSERT(currentClass != Unclassified, "The current state must have a class assigned.");
300 for (auto const& predecessorEntry : context.backwardTransitions.getRow(currentState)) {
301 uint64_t const predecessor = predecessorEntry.getColumn();
302 uint64_t& predecessorClass = stateClasses[predecessor];
303 if (predecessorClass == Unclassified) {
304 // Ignore predecessors outside of the block.
305 if (context.partition.isBlockOfElement(block, predecessor)) {
306 STORM_LOG_ASSERT(silentStates.get(predecessor), "An unclassified state must be silent.");
307 predecessorClass = currentClass; // propagate the current class
308 bfsQueue.push_back(predecessor);
309 }
310 } else if (predecessorClass == currentClass) {
311 // The class of predecessor does not change. Nothing to do.
312 } else if (predecessorClass == MultipleFrontiers) {
313 // case b) applies for the predecessor, but case a) potentially applies for the current state.
314 nonSilentCandidates.push_back(predecessor);
315 } else if (silentStates.get(predecessor)) {
316 // So far, we assumed that case a) applies for the predecessor, but we have now found a successor in a different class. Hence, Case b) applies.
317 predecessorClass = MultipleFrontiers;
318 bfsQueue.push_back(predecessor);
319 nonSilentCandidates.push_back(predecessor);
320 } else {
321 // The last case only applies for a non-silent (i.e. frontier) predecessor. Hence, the class of the predecessor does not change. Nothing to do.
322 }
323 }
324 }
325
326 // Step 6: Split the block by the computed classes and enqueue the subblocks
327 context.partition.splitBlockByOrder(block,
328 [&stateClasses](uint64_t const state1, uint64_t const state2) { return stateClasses[state1] < stateClasses[state2]; });
329 STORM_LOG_ASSERT(context.partition.isProperSuperBlock(block), "As there are multiple different frontier states, the block must be split.");
330 enqueueSubBlocks(context, block);
331
332 // Step 7: update silent states and clear cached data.
333 recomputeSilentStates(context, nonSilentCandidates);
334 for (uint64_t const state : block) {
335 stateClasses[state] = Unclassified;
336 }
337}
338
339template<typename ValueType, SplitterRefinementMode Mode>
341 auto& predecessorToSplitterProbabilities = context.cache.predecessorToSplitterProbabilities;
342 auto& predecessorBlocks = context.cache.predecessorBlocks;
343
344 for (auto currentState : splitterBlock) {
345 // Compute probability to enter splitter block for each predecessor
346 for (const auto& predecessorEntry : context.backwardTransitions.getRow(currentState)) {
347 auto predecessorState = predecessorEntry.getColumn();
348 auto predecessorBlock = context.partition.getBlockOfElement(predecessorState);
349 if (predecessorBlock.size() == 1) {
350 continue; // No need to try to split singleton blocks
351 }
353 // The states of a divergent block can never leave it, so they all exhibit the same (unobservable) behavior and must not be split.
354 if (context.weakData->divergentStates.get(predecessorState)) {
355 continue;
356 }
357 // For weak bisimulation, the moves of a non-step-sensitive state within its own block are unobservable.
358 // Hence, the splitter must not split itself.
359 if (context.partition.isEqualBlock(predecessorBlock, splitterBlock) && !context.isStepSensitive(predecessorState)) {
360 continue;
361 }
362 }
363 predecessorToSplitterProbabilities.addValue(predecessorState, predecessorEntry.getValue());
364 predecessorBlocks.insert(predecessorBlock);
365 }
366 }
367
368 while (!predecessorBlocks.empty()) {
369 auto const predecessorBlockToSplit = predecessorBlocks.pop();
370 if constexpr (Mode == SplitterRefinementMode::WeakDiscreteTime) {
371 if (!context.isStepSensitive(predecessorBlockToSplit.front())) {
372 refineBlockWeak(context, predecessorBlockToSplit, splitterBlock);
373 continue;
374 }
375 }
376 refineBlockStrong(context, predecessorBlockToSplit);
377 }
378
379 // Reset the predecessorToSplitterProbabilities for the next iteration.
380 predecessorToSplitterProbabilities.clear();
381}
382
383} // namespace detail
384
385template<typename ValueType, SplitterRefinementMode Mode>
387 storm::bisimulation::Partition& partition, ValueType const tolerance, storm::OptionalRef<WeakBisimulationData> weakData) {
388 static_assert(!storm::IsIntervalType<ValueType>, "Interval types are not supported for splitter-based refinement.");
389 // Refinement for interval models requires limiting signatures to feasible intervals. This is rather difficult in a splitter-based setting.
390 STORM_LOG_THROW(!model.isNondeterministicModel(), storm::exceptions::InvalidArgumentException,
391 "Splitter-based refinement is only supported for deterministic models.");
392 STORM_LOG_THROW((storm::utility::isZero(tolerance) || !std::is_same_v<ValueType, storm::RationalFunction>), storm::exceptions::InvalidArgumentException,
393 "Splitter-based refinement with non-zero tolerance does not apply to parametric models.");
394 detail::SplitterRefinementContext<ValueType, Mode> context(model, backwardTransitions, partition, tolerance, weakData);
395 if constexpr (Mode != SplitterRefinementMode::Strong) {
396 STORM_LOG_ASSERT(weakData->checkBlockHomogeneity(partition), "Partition is not homogeneous with respect to the weak bisimulation data.");
397 STORM_LOG_ASSERT(detail::checkSilentStates(context), "The given silent states do not match the given partition.");
398 }
399 // Initially, add all current blocks to the queue.
400 partition.forEachBlock([&context](auto const& block) { context.queue.insert(block); });
401
402 // Perform the splitting until there are no more splitters.
403 while (!context.queue.empty()) {
404 // take the smallest block from the queue
405 auto const splitterBlock = *context.queue.begin();
406 context.queue.erase(context.queue.begin());
407 STORM_LOG_ASSERT(!partition.isProperSuperBlock(splitterBlock), "Broken invariant: the queue should not contain blocks that have been split.");
408 // Split the predecessor blocks and add them to the queue
409 detail::refinePartitionBasedOnSplitter(context, splitterBlock);
410 }
411
412 if constexpr (Mode != SplitterRefinementMode::Strong) {
413 // The refinement itself only keeps the silent states of the blocks refined through refineBlockWeak up to date.
415 detail::recomputeSilentStates(context, context.weakData->stepSensitiveStates);
416 } else {
417 detail::recomputeSilentStates(context, context.weakData->silentStates);
418 }
419 STORM_LOG_ASSERT(detail::checkSilentStates(context), "The silent states do not match the final partition.");
420 }
421}
422
423// double
425 storm::storage::SparseMatrix<double> const& backwardTransitions,
426 storm::bisimulation::Partition& partition, double const tolerance,
429 storm::storage::SparseMatrix<double> const& backwardTransitions,
431 double const tolerance,
434 storm::models::sparse::Model<double> const& model, storm::storage::SparseMatrix<double> const& backwardTransitions,
435 storm::bisimulation::Partition& partition, double const tolerance, storm::OptionalRef<WeakBisimulationData> weakData);
436
437// storm::RationalNumber
440 storm::bisimulation::Partition& partition, storm::RationalNumber const tolerance, storm::OptionalRef<WeakBisimulationData> weakData);
443 storm::bisimulation::Partition& partition, storm::RationalNumber const tolerance, storm::OptionalRef<WeakBisimulationData> weakData);
446 storm::bisimulation::Partition& partition, storm::RationalNumber const tolerance, storm::OptionalRef<WeakBisimulationData> weakData);
447
448// storm::RationalFunction
458
459} // namespace storm::bisimulation
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
Set of blocks that are no proper superblock.
Definition Partition.h:62
Represents a partition of a set of consecutive indices.
Definition Partition.h:20
bool contains(Block const &block, ElementIndex element) const
Definition Partition.cpp:79
void forEachBlock(Func const &f) const
Applies the given function on each block in the partition.
Definition Partition.h:184
void forEachSubBlock(Block const &superBlock, Func const &f) const
Applies the given function on each block in the given super block.
Definition Partition.h:196
bool isSameBlock(ElementIndex const &element1, ElementIndex const &element2) const
Definition Partition.cpp:97
bool isBlockOfElement(Block const &block, ElementIndex const &element) const
bool splitBlockByOrder(Block const &block, SplittingOrder const &less)
Splits the given block according to the given order.
Definition Partition.h:215
std::span< ElementIndex const > Block
A contiguous range of elements that form a (possibly proper super-) block of the partition.
Definition Partition.h:27
Block getBlockOfElement(ElementIndex element) const
Definition Partition.cpp:74
std::pair< Block, Block > splitBlockByRange(Block const &block, SplitRange &&r)
Splits the given block according to the given range of elements.
Definition Partition.h:362
std::set< Block, BlockCompare > OrderedBlockSet
A set of blocks, ordered by their size (smallest first).
Definition Partition.h:48
bool isProperSuperBlock(Block const &block) const
bool isEqualBlock(Block const &lhs, Block const &rhs) const
Definition Partition.cpp:91
std::pair< Block, Block > splitBlockByPredicate(Block const &block, SplittingPredicate const &f)
Splits the given block according to the given predicate.
Definition Partition.h:329
Accumulates a value for each state (numbers are added up, elements are inserted into sets,...
bool isNondeterministicModel() const
Returns true if the model is a nondeterministic model.
Definition ModelBase.cpp:31
Base class for all sparse models.
Definition Model.h:34
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:226
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
void recomputeSilentStates(SplitterRefinementContext< ValueType, Mode > &context, auto const &candidates)
Recomputes whether the given candidate states are silent.
void enqueueSubBlocks(SplitterRefinementContext< ValueType, Mode > &context, storm::bisimulation::Partition::Block const splitBlock)
Replaces the given block, which has just been split, by its sub-blocks in the queue of splitters.
bool checkSilentStates(SplitterRefinementContext< ValueType, Mode > const &context)
Checks that the recorded silent states match the current partition.
void refineBlockWeak(SplitterRefinementContext< ValueType, SplitterRefinementMode::WeakDiscreteTime > &context, storm::bisimulation::Partition::Block const block, storm::bisimulation::Partition::Block const splitterBlock)
Refines the given predecessor block of the current splitter with respect to the probability of moving...
void refineBlockStrong(SplitterRefinementContext< ValueType, Mode > &context, storm::bisimulation::Partition::Block const predecessorBlockToSplit)
Refines the given predecessor block of the current splitter with respect to the probability of moving...
void refinePartitionBasedOnSplitter(SplitterRefinementContext< ValueType, Mode > &context, storm::bisimulation::Partition::Block const splitterBlock)
template void performSplitterBasedRefinement< storm::RationalFunction, SplitterRefinementMode::Strong >(storm::models::sparse::Model< storm::RationalFunction > const &model, storm::storage::SparseMatrix< storm::RationalFunction > const &backwardTransitions, storm::bisimulation::Partition &partition, storm::RationalFunction const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
template void performSplitterBasedRefinement< storm::RationalFunction, SplitterRefinementMode::WeakDiscreteTime >(storm::models::sparse::Model< storm::RationalFunction > const &model, storm::storage::SparseMatrix< storm::RationalFunction > const &backwardTransitions, storm::bisimulation::Partition &partition, storm::RationalFunction const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
bool isSilentState(storm::storage::SparseMatrix< ValueType > const &transitions, Partition const &partition, uint64_t const state)
template void performSplitterBasedRefinement< double, SplitterRefinementMode::WeakDiscreteTime >(storm::models::sparse::Model< double > const &model, storm::storage::SparseMatrix< double > const &backwardTransitions, storm::bisimulation::Partition &partition, double const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
template void performSplitterBasedRefinement< double, SplitterRefinementMode::Strong >(storm::models::sparse::Model< double > const &model, storm::storage::SparseMatrix< double > const &backwardTransitions, storm::bisimulation::Partition &partition, double const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
template void performSplitterBasedRefinement< storm::RationalNumber, SplitterRefinementMode::WeakDiscreteTime >(storm::models::sparse::Model< storm::RationalNumber > const &model, storm::storage::SparseMatrix< storm::RationalNumber > const &backwardTransitions, storm::bisimulation::Partition &partition, storm::RationalNumber const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
template void performSplitterBasedRefinement< storm::RationalFunction, SplitterRefinementMode::WeakContinuousTime >(storm::models::sparse::Model< storm::RationalFunction > const &model, storm::storage::SparseMatrix< storm::RationalFunction > const &backwardTransitions, storm::bisimulation::Partition &partition, storm::RationalFunction const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
template void performSplitterBasedRefinement< double, SplitterRefinementMode::WeakContinuousTime >(storm::models::sparse::Model< double > const &model, storm::storage::SparseMatrix< double > const &backwardTransitions, storm::bisimulation::Partition &partition, double const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
void performSplitterBasedRefinement(storm::models::sparse::Model< ValueType > const &model, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::bisimulation::Partition &partition, ValueType const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
Performs splitter-based partition refinement.
template void performSplitterBasedRefinement< storm::RationalNumber, SplitterRefinementMode::Strong >(storm::models::sparse::Model< storm::RationalNumber > const &model, storm::storage::SparseMatrix< storm::RationalNumber > const &backwardTransitions, storm::bisimulation::Partition &partition, storm::RationalNumber const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
template void performSplitterBasedRefinement< storm::RationalNumber, SplitterRefinementMode::WeakContinuousTime >(storm::models::sparse::Model< storm::RationalNumber > const &model, storm::storage::SparseMatrix< storm::RationalNumber > const &backwardTransitions, storm::bisimulation::Partition &partition, storm::RationalNumber const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
constexpr bool IsIntervalType
Helper to check if a type is an interval.
carl::RationalFunction< Polynomial, true > RationalFunction
storm::models::sparse::Model< ValueType > const & model
storm::bisimulation::Partition::OrderedBlockSet queue
SplitterRefinementContext(storm::models::sparse::Model< ValueType > const &model, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::bisimulation::Partition &partition, ValueType const tolerance, storm::OptionalRef< WeakBisimulationData > weakData)
storm::OptionalRef< WeakBisimulationData > weakData
Only set in the weak modes; its silentStates are kept up to date here, cf. recomputeSilentStates.
std::conditional_t< isWeakDiscreteTime, WeakDiscreteTimeCache, DefaultCache > Cache
Picks the scratch space that this mode actually uses, so that accessing the wrong one is a compile er...
storm::storage::SparseMatrix< ValueType > const & backwardTransitions