26template<
typename ValueType, SplitterRefinementMode Mode>
44 return weakData->stepSensitiveStates.get(state);
82 using Cache = std::conditional_t<isWeakDiscreteTime, WeakDiscreteTimeCache, DefaultCache>;
89template<
typename ValueType, SplitterRefinementMode Mode>
94 for (uint64_t
const state : candidates) {
95 context.weakData->silentStates.set(state,
isSilentState(context.model.getTransitionMatrix(), context.partition, state));
102template<
typename ValueType, SplitterRefinementMode Mode>
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)) {
123template<
typename ValueType, SplitterRefinementMode Mode>
125 bool const wasInQueue = context.
queue.erase(splitBlock) > 0;
128 context.
queue.insert(subBlock);
129 if (subBlock.size() > largestSubBlock.size()) {
130 largestSubBlock = subBlock;
134 context.
queue.erase(largestSubBlock);
143template<
typename ValueType, SplitterRefinementMode Mode>
145 auto& predecessorToSplitterProbabilities = context.
cache.predecessorToSplitterProbabilities;
149 auto [noPredecessors, predecessors] =
150 predecessorToSplitterProbabilities.getNonDefaultStates().size() < predecessorBlockToSplit.size()
153 predecessorBlockToSplit, [&predecessorToSplitterProbabilities](
154 auto const& state) {
return !
storm::utility::isZero(predecessorToSplitterProbabilities.getValues()[state]); });
156 STORM_LOG_ASSERT(!predecessors.empty(),
"The predecessor block should contain at least one predecessor state.");
157 bool wasSplit = noPredecessors.size() > 0;
162 auto const& toSplitterProbs = predecessorToSplitterProbabilities.getValues();
163 auto const less = [&toSplitterProbs](uint64_t
const state1, uint64_t
const state2) {
return toSplitterProbs[state1] < toSplitterProbs[state2]; };
168 auto const lessTolerance = [&toSplitterProbs, &context](uint64_t
const state1, uint64_t
const state2) {
169 return toSplitterProbs[state1] + context.
tolerance < toSplitterProbs[state2];
187template<
typename ValueType>
190 STORM_LOG_ASSERT(!context.
weakData->isDivergent(block),
"Assumed a non-divergent block as a predecessor block of the splitter.");
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)) {
200 frontierStates.push_back(nonSilentState);
204 for (uint64_t
const state : block) {
205 if (!silentStates.get(state)) {
206 frontierStates.push_back(state);
212 auto& conditionalValues = context.
cache.conditionalValues;
213 auto const& toSplitterProbs = context.
cache.predecessorToSplitterProbabilities.getValues();
214 for (uint64_t
const state : frontierStates) {
221 [&context, &splitterBlock](
auto const& entry) {
222 return !storm::utility::isZero(entry.getValue()) && context.partition.contains(splitterBlock, entry.getColumn());
224 "Expected a transition into a splitter, but none was found.");
226 bool leavesOnlyToSplitter =
true;
227 for (
auto const& entry : row) {
231 escapeValue += entry.getValue();
232 leavesOnlyToSplitter = leavesOnlyToSplitter && context.
partition.
contains(splitterBlock, entry.getColumn());
241 STORM_LOG_ASSERT(!frontierStates.empty(),
"A non-divergent block must contain a non-silent state.");
244 auto& stateClasses = context.
cache.temporaryStateClasses;
245 uint64_t
constexpr Unclassified = std::numeric_limits<uint64_t>::max();
246 uint64_t
constexpr MultipleFrontiers =
247 std::numeric_limits<uint64_t>::max() - 1;
249 std::sort(frontierStates.begin(), frontierStates.end(),
250 [&conditionalValues](uint64_t
const state1, uint64_t
const state2) { return conditionalValues[state1] < conditionalValues[state2]; });
253 auto const haveDifferentClass = [&conditionalValues, &context](uint64_t
const state1, uint64_t
const state2) {
257 return conditionalValues[state1] != conditionalValues[state2];
259 return conditionalValues[state1] + context.
tolerance < conditionalValues[state2];
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;
268 stateClasses[state] = currClassRepresentative;
273 if (stateClasses[frontierStates.front()] == stateClasses[frontierStates.back()]) {
276 for (uint64_t
const state : frontierStates) {
277 stateClasses[state] = Unclassified;
287 auto& bfsQueue = context.
cache.bfsQueue;
289 for (
auto const state : frontierStates) {
290 bfsQueue.push_back(state);
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.");
301 uint64_t
const predecessor = predecessorEntry.getColumn();
302 uint64_t& predecessorClass = stateClasses[predecessor];
303 if (predecessorClass == Unclassified) {
306 STORM_LOG_ASSERT(silentStates.get(predecessor),
"An unclassified state must be silent.");
307 predecessorClass = currentClass;
308 bfsQueue.push_back(predecessor);
310 }
else if (predecessorClass == currentClass) {
312 }
else if (predecessorClass == MultipleFrontiers) {
314 nonSilentCandidates.push_back(predecessor);
315 }
else if (silentStates.get(predecessor)) {
317 predecessorClass = MultipleFrontiers;
318 bfsQueue.push_back(predecessor);
319 nonSilentCandidates.push_back(predecessor);
328 [&stateClasses](uint64_t
const state1, uint64_t
const state2) {
return stateClasses[state1] < stateClasses[state2]; });
334 for (uint64_t
const state : block) {
335 stateClasses[state] = Unclassified;
339template<
typename ValueType, SplitterRefinementMode Mode>
341 auto& predecessorToSplitterProbabilities = context.
cache.predecessorToSplitterProbabilities;
342 auto& predecessorBlocks = context.
cache.predecessorBlocks;
344 for (
auto currentState : splitterBlock) {
347 auto predecessorState = predecessorEntry.getColumn();
349 if (predecessorBlock.size() == 1) {
354 if (context.
weakData->divergentStates.get(predecessorState)) {
363 predecessorToSplitterProbabilities.addValue(predecessorState, predecessorEntry.getValue());
364 predecessorBlocks.insert(predecessorBlock);
368 while (!predecessorBlocks.empty()) {
369 auto const predecessorBlockToSplit = predecessorBlocks.pop();
380 predecessorToSplitterProbabilities.clear();
385template<
typename ValueType, SplitterRefinementMode Mode>
391 "Splitter-based refinement is only supported for deterministic models.");
393 "Splitter-based refinement with non-zero tolerance does not apply to parametric models.");
396 STORM_LOG_ASSERT(weakData->checkBlockHomogeneity(partition),
"Partition is not homogeneous with respect to the weak bisimulation data.");
400 partition.
forEachBlock([&context](
auto const& block) { context.
queue.insert(block); });
403 while (!context.
queue.empty()) {
405 auto const splitterBlock = *context.
queue.begin();
431 double const tolerance,
Helper class that optionally holds a reference to an object of type T.
Set of blocks that are no proper superblock.
Represents a partition of a set of consecutive indices.
bool contains(Block const &block, ElementIndex element) const
void forEachBlock(Func const &f) const
Applies the given function on each block in the partition.
void forEachSubBlock(Block const &superBlock, Func const &f) const
Applies the given function on each block in the given super block.
bool isSameBlock(ElementIndex const &element1, ElementIndex const &element2) const
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.
std::span< ElementIndex const > Block
A contiguous range of elements that form a (possibly proper super-) block of the partition.
Block getBlockOfElement(ElementIndex element) const
std::pair< Block, Block > splitBlockByRange(Block const &block, SplitRange &&r)
Splits the given block according to the given range of elements.
std::set< Block, BlockCompare > OrderedBlockSet
A set of blocks, ordered by their size (smallest first).
bool isProperSuperBlock(Block const &block) const
bool isEqualBlock(Block const &lhs, Block const &rhs) const
std::pair< Block, Block > splitBlockByPredicate(Block const &block, SplittingPredicate const &f)
Splits the given block according to the given predicate.
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.
Base class for all sparse models.
storm::storage::SparseMatrix< ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
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)
constexpr bool IsIntervalType
Helper to check if a type is an interval.
carl::RationalFunction< Polynomial, true > RationalFunction
Partition::NonSuperBlockSet predecessorBlocks
SparseAccumulator< ValueType > predecessorToSplitterProbabilities
DefaultCache(Partition const &partition)
WeakDiscreteTimeCache(Partition const &partition)
std::vector< uint64_t > nonSilentCandidates
std::vector< uint64_t > temporaryStateClasses
std::vector< uint64_t > frontierStates
std::vector< ValueType > conditionalValues
std::deque< uint64_t > bfsQueue
storm::models::sparse::Model< ValueType > const & model
static constexpr bool isWeak
bool isStepSensitive(uint64_t const state) const
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.
storm::bisimulation::Partition & partition
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
ValueType const tolerance
static constexpr bool isWeakDiscreteTime