5#include <unordered_map>
20template<
typename ModelType>
28template<
typename ModelType>
33template<
typename ModelType>
34void DeterministicModelBisimulationDecomposition<ModelType>::splitOffDivergentStates() {
35 std::vector<storm::storage::sparse::state_type> stateStack;
36 stateStack.reserve(this->model.getNumberOfStates());
39 uint_fast64_t currentSize = this->partition.size();
40 for (uint_fast64_t blockIndex = 0; blockIndex < currentSize; ++blockIndex) {
41 auto& block = *this->partition.getBlocks()[blockIndex];
42 nondivergentStates.clear();
44 for (
auto stateIt = this->partition.begin(block), stateIte = this->partition.end(block); stateIt != stateIte; ++stateIt) {
45 if (nondivergentStates.get(*stateIt)) {
51 bool isDirectlyNonDivergent =
false;
52 for (
auto const& successor : this->model.getTransitionMatrix().getRowGroup(*stateIt)) {
55 if (this->partition.getBlock(successor.getColumn()) != block) {
56 isDirectlyNonDivergent =
true;
61 if (isDirectlyNonDivergent) {
62 stateStack.push_back(*stateIt);
64 while (!stateStack.empty()) {
66 stateStack.pop_back();
67 nondivergentStates.set(currentState);
69 for (
auto const& predecessor : this->backwardTransitions.getRow(currentState)) {
70 if (this->partition.getBlock(predecessor.getColumn()) == block && !nondivergentStates.get(predecessor.getColumn())) {
71 stateStack.push_back(predecessor.getColumn());
78 if (!nondivergentStates.empty() && nondivergentStates.getNumberOfSetBits() != block.getNumberOfStates()) {
80 this->partition.splitStates(block, nondivergentStates);
85 block.data().setAbsorbing(
true);
86 }
else if (nondivergentStates.empty()) {
88 block.data().setAbsorbing(
true);
93template<
typename ModelType>
94void DeterministicModelBisimulationDecomposition<ModelType>::initializeSilentProbabilities() {
98 for (
auto const& successorEntry : this->model.getTransitionMatrix().getRowGroup(state)) {
99 if (&this->partition.getBlock(successorEntry.getColumn()) == currentBlockPtr) {
100 silentProbabilities[state] += getTransitionValue(successorEntry, state);
106template<
typename ModelType>
107void DeterministicModelBisimulationDecomposition<ModelType>::initializeWeakDtmcBisimulation() {
110 this->splitOffDivergentStates();
111 this->initializeSilentProbabilities();
114template<
typename ModelType>
115void DeterministicModelBisimulationDecomposition<ModelType>::postProcessInitialPartition() {
117 this->initializeWeakDtmcBisimulation();
120 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->options.getType() ==
BisimulationType::Weak) {
126 std::optional<std::vector<ValueType>>
const& optionalStateRewardVector = this->model.getUniqueRewardModel().getOptionalStateRewardVector();
127 std::optional<std::vector<ValueType>>
const& optionalStateActionRewardVector = this->model.getUniqueRewardModel().getOptionalStateActionRewardVector();
128 for (
auto& block : this->partition.getBlocks()) {
129 auto state = *this->partition.begin(*block);
130 block->data().setHasRewards((optionalStateRewardVector && !
storm::utility::isZero(optionalStateRewardVector.value()[state])) ||
131 (optionalStateActionRewardVector && !
storm::utility::isZero(optionalStateActionRewardVector.value()[state])));
136template<
typename ModelType>
139 postProcessInitialPartition();
142template<
typename ModelType>
145 postProcessInitialPartition();
148template<
typename ModelType>
151 return probabilitiesToCurrentSplitter[state];
154template<
typename ModelType>
156 return this->comparator.isOne(silentProbabilities[state]);
159template<
typename ModelType>
161 return !this->comparator.isZero(silentProbabilities[state]);
164template<
typename ModelType>
167 return silentProbabilities[state];
170template<
typename ModelType>
171void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterStrong(
173 STORM_LOG_TRACE(
"Refining predecessor " << block.getId() <<
" of splitter");
180 if (block.getBeginIndex() != block.data().marker1() && block.getEndIndex() != block.data().marker1()) {
182 this->partition.splitBlock(block, block.data().marker1());
183 blockToRefineProbabilistically = block.getPreviousBlockPointer();
186 blockToRefineProbabilistically->data().setHasRewards(block.data().hasRewards());
189 split |= this->partition.splitBlock(
190 *blockToRefineProbabilistically,
192 return this->comparator.isLess(getProbabilityToSplitter(state1), getProbabilityToSplitter(state2));
195 splitterQueue.emplace_back(&newBlock);
196 newBlock.data().setSplitter();
199 newBlock.data().setHasRewards(block.data().hasRewards());
204 if (split && !blockToRefineProbabilistically->data().splitter()) {
205 splitterQueue.emplace_back(blockToRefineProbabilistically);
206 blockToRefineProbabilistically->data().setSplitter();
210template<
typename ModelType>
211void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterStrong(
213 for (
auto block : predecessorBlocks) {
214 refinePredecessorBlockOfSplitterStrong(*block, splitterQueue);
217 block->resetMarkers();
220 block->data().setNeedsRefinement(
false);
224template<
typename ModelType>
226 return predecessorBlock.
getNumberOfStates() > 1 && !predecessorBlock.data().absorbing();
229template<
typename ModelType>
232 ValueType
const& value) {
233 STORM_LOG_TRACE(
"Increasing probability of " << predecessor <<
" to splitter by " << value <<
".");
237 if (predecessorPosition >= predecessorBlock.data().marker1()) {
239 probabilitiesToCurrentSplitter[predecessor] = value;
242 probabilitiesToCurrentSplitter[predecessor] += value;
246template<
typename ModelType>
249 this->partition.swapStates(predecessor, this->partition.getState(predecessorBlock.data().marker1()));
250 predecessorBlock.data().incrementMarker1();
253template<
typename ModelType>
256 this->partition.swapStates(predecessor, this->partition.getState(predecessorBlock.data().marker2()));
257 predecessorBlock.data().incrementMarker2();
260template<
typename ModelType>
264 uint_fast64_t& elementsToSkip) {
268 if (predecessorPosition <= currentPositionInSplitter + elementsToSkip) {
269 this->partition.swapStates(predecessor, this->partition.getState(predecessorBlock.data().marker1()));
270 predecessorBlock.data().incrementMarker1();
275 if (predecessorBlock.data().marker2() == predecessorBlock.data().marker1()) {
276 this->partition.swapStatesAtPositions(predecessorBlock.data().marker2(), predecessorPosition);
277 this->partition.swapStatesAtPositions(predecessorPosition, currentPositionInSplitter + elementsToSkip + 1);
279 this->partition.swapStatesAtPositions(predecessorBlock.data().marker2(), predecessorPosition);
280 this->partition.swapStatesAtPositions(predecessorPosition, predecessorBlock.data().marker1());
281 this->partition.swapStatesAtPositions(predecessorPosition, currentPositionInSplitter + elementsToSkip + 1);
286 predecessorBlock.data().incrementMarker1();
287 predecessorBlock.data().incrementMarker2();
291template<
typename ModelType>
292void DeterministicModelBisimulationDecomposition<ModelType>::exploreRemainingStatesOfSplitter(
294 for (
auto splitterIt = this->partition.begin(splitter),
295 splitterIte = this->partition.begin(splitter) + (splitter.data().marker2() - splitter.getBeginIndex());
296 splitterIt != splitterIte; ++splitterIt) {
299 for (
auto const& predecessorEntry : this->backwardTransitions.getRow(currentState)) {
304 if (!possiblyNeedsRefinement(predecessorBlock)) {
315 increaseProbabilityToSplitter(predecessor, predecessorBlock, getTransitionValue(predecessorEntry, predecessor));
319 if (predecessorPosition >= predecessorBlock.data().marker1()) {
320 moveStateToMarker1(predecessor, predecessorBlock);
325 insertIntoPredecessorList(predecessorBlock, predecessorBlocks);
331 splitter.data().setMarker2(splitter.getBeginIndex());
334template<
typename ModelType>
335void DeterministicModelBisimulationDecomposition<ModelType>::updateSilentProbabilitiesBasedOnProbabilitiesToSplitter(
338 for (
auto stateIt = this->partition.begin(block), stateIte = this->partition.begin() + block.data().marker1(); stateIt != stateIte; ++stateIt) {
339 silentProbabilities[*stateIt] = probabilitiesToCurrentSplitter[*stateIt];
342 for (
auto stateIt = this->partition.begin() + block.data().marker1(), stateIte = this->partition.end(block); stateIt != stateIte; ++stateIt) {
347template<
typename ModelType>
349 for (
auto stateIt = this->partition.begin(block), stateIte = this->partition.end(block); stateIt != stateIte; ++stateIt) {
350 if (hasNonZeroSilentProbability(*stateIt)) {
352 for (
auto const& successorEntry : this->model.getTransitionMatrix().getRow(*stateIt)) {
353 if (this->partition.getBlock(successorEntry.getColumn()) == block) {
354 newSilentProbability += getTransitionValue(successorEntry, *stateIt);
357 silentProbabilities[*stateIt] = newSilentProbability;
362template<
typename ModelType>
364 for (
auto stateIt = this->partition.begin() + block.getBeginIndex(), stateIte = this->partition.begin() + block.data().marker1(); stateIt != stateIte;
366 if (!this->comparator.isOne(getSilentProbability(*stateIt))) {
372template<
typename ModelType>
375 return probabilitiesToCurrentSplitter[state1] < probabilitiesToCurrentSplitter[state2];
377 this->partition.sortRange(block.getBeginIndex(), block.data().marker1(), less);
378 return this->partition.computeRangesOfEqualValue(block.getBeginIndex(), block.data().marker1(), less);
381template<
typename ModelType>
382std::vector<storm::storage::BitVector> DeterministicModelBisimulationDecomposition<ModelType>::computeWeakStateLabelingBasedOnNonSilentBlocks(
386 std::vector<storm::storage::BitVector> stateLabels(block.getNumberOfStates(), storm::storage::BitVector(nonSilentBlockIndices.size() - 1));
388 std::vector<storm::storage::sparse::state_type> stateStack;
389 stateStack.reserve(block.getNumberOfStates());
390 for (uint_fast64_t stateClassIndex = 0; stateClassIndex < nonSilentBlockIndices.size() - 1; ++stateClassIndex) {
391 for (
auto stateIt = this->partition.begin() + nonSilentBlockIndices[stateClassIndex],
392 stateIte = this->partition.begin() + nonSilentBlockIndices[stateClassIndex + 1];
393 stateIt != stateIte; ++stateIt) {
394 stateStack.push_back(*stateIt);
395 stateLabels[this->partition.getPosition(*stateIt) - block.getBeginIndex()].set(stateClassIndex);
396 while (!stateStack.empty()) {
398 stateStack.pop_back();
400 for (
auto const& predecessorEntry : this->backwardTransitions.getRow(currentState)) {
409 if (this->partition.getBlock(predecessor) == block && isSilent(predecessor) &&
410 !stateLabels[this->partition.getPosition(predecessor) - block.getBeginIndex()].get(stateClassIndex)) {
411 stateStack.push_back(predecessor);
412 stateLabels[this->partition.getPosition(predecessor) - block.getBeginIndex()].set(stateClassIndex);
422template<
typename ModelType>
423void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterWeak(
427 computeConditionalProbabilitiesForNonSilentStates(block);
430 std::vector<uint_fast64_t> nonSilentBlockIndices = computeNonSilentBlocks(block);
431 std::vector<storm::storage::BitVector> weakStateLabels = computeWeakStateLabelingBasedOnNonSilentBlocks(block, nonSilentBlockIndices);
437 auto split = this->partition.splitBlock(
440 return weakStateLabels[this->partition.getPosition(state1) - originalBlockIndex] <
441 weakStateLabels[this->partition.getPosition(state2) - originalBlockIndex];
444 updateSilentProbabilitiesBasedOnTransitions(newBlock);
447 newBlock.data().setSplitter();
448 splitterQueue.emplace_back(&newBlock);
451 newBlock.data().setHasRewards(block.data().hasRewards());
456 updateSilentProbabilitiesBasedOnTransitions(block);
458 if (!block.data().splitter()) {
460 block.data().setSplitter();
461 splitterQueue.emplace_back(&block);
466template<
typename ModelType>
467void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterWeak(
470 for (
auto block : predecessorBlocks) {
471 if (block->data().hasRewards()) {
472 refinePredecessorBlockOfSplitterStrong(*block, splitterQueue);
474 if (*block != splitter) {
475 refinePredecessorBlockOfSplitterWeak(*block, splitterQueue);
481 block->resetMarkers();
482 block->data().setNeedsRefinement(
false);
486template<
typename ModelType>
490 if (!predecessorBlock.data().needsRefinement()) {
491 predecessorBlocks.emplace_back(&predecessorBlock);
492 predecessorBlock.data().setNeedsRefinement();
496template<
typename ModelType>
516 std::list<Block<BlockDataType>*> predecessorBlocks;
518 bool splitterIsPredecessorBlock =
false;
519 for (
auto splitterIt = this->
partition.begin(splitter), splitterIte = this->partition.end(splitter); splitterIt != splitterIte;
520 ++splitterIt, ++currentPosition) {
523 uint_fast64_t elementsToSkip = 0;
530 if (!possiblyNeedsRefinement(predecessorBlock)) {
541 increaseProbabilityToSplitter(predecessor, predecessorBlock, getTransitionValue(predecessorEntry, predecessor));
544 if (predecessorPosition >= predecessorBlock.
data().marker1()) {
546 if (predecessorBlock != splitter) {
547 moveStateToMarker1(predecessor, predecessorBlock);
550 splitterIsPredecessorBlock =
true;
551 moveStateInSplitter(predecessor, predecessorBlock, currentPosition, elementsToSkip);
554 insertIntoPredecessorList(predecessorBlock, predecessorBlocks);
559 splitterIt += elementsToSkip;
560 currentPosition += elementsToSkip;
565 if (splitterIsPredecessorBlock) {
566 exploreRemainingStatesOfSplitter(splitter, predecessorBlocks);
573 refinePredecessorBlocksOfSplitterStrong(predecessorBlocks, splitterQueue);
577 if (splitterIsPredecessorBlock) {
578 updateSilentProbabilitiesBasedOnProbabilitiesToSplitter(splitter);
581 refinePredecessorBlocksOfSplitterWeak(splitter, predecessorBlocks, splitterQueue);
585template<
typename ModelType>
597 std::set<std::string> atomicPropositionsSet = this->
options.respectedAtomicPropositions.value();
598 atomicPropositionsSet.insert(
"init");
599 std::vector<std::string> atomicPropositions = std::vector<std::string>(atomicPropositionsSet.begin(), atomicPropositionsSet.end());
600 for (
auto const& ap : atomicPropositions) {
605 std::optional<std::vector<ValueType>> stateRewards;
606 if (this->
options.getKeepRewards() && this->model.hasRewardModel()) {
607 stateRewards = std::vector<ValueType>(this->
blocks.size());
611 for (uint_fast64_t blockIndex = 0; blockIndex < this->
blocks.size(); ++blockIndex) {
612 auto const& block = this->
blocks[blockIndex];
621 for (
auto const& state : block) {
622 if (!isSilent(state)) {
623 representativeState = state;
632 if (oldBlock.
data().absorbing()) {
636 if (oldBlock.
data().hasRepresentativeState()) {
637 representativeState = oldBlock.
data().representativeState();
642 for (
auto const& ap : atomicPropositions) {
643 if (this->
model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
649 std::map<storm::storage::sparse::state_type, ValueType> blockProbability;
650 for (
auto const& entry : this->
model.getTransitionMatrix().getRow(representativeState)) {
658 auto probIterator = blockProbability.find(targetBlock);
659 if (probIterator != blockProbability.end()) {
660 probIterator->second += getTransitionValue(entry, representativeState);
662 blockProbability[targetBlock] = getTransitionValue(entry, representativeState);
667 for (
auto const& probabilityEntry : blockProbability) {
669 !oldBlock.
data().hasRewards()) {
670 builder.addNextValue(blockIndex, probabilityEntry.first,
673 builder.addNextValue(blockIndex, probabilityEntry.first, probabilityEntry.second);
679 for (
auto const& ap : atomicPropositions) {
680 if (this->
model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
688 if (this->
options.getKeepRewards() && this->model.hasRewardModel()) {
689 auto const& rewardModel = this->
model.getUniqueRewardModel();
690 if (rewardModel.hasStateRewards()) {
691 stateRewards.value()[blockIndex] = rewardModel.getStateRewardVector()[representativeState];
693 if (rewardModel.hasStateActionRewards()) {
694 stateRewards.value()[blockIndex] += rewardModel.getStateActionRewardVector()[representativeState];
700 for (
auto initialState : this->
model.getInitialStates()) {
706 std::unordered_map<std::string, typename ModelType::RewardModelType> rewardModels;
707 if (this->
options.getKeepRewards() && this->model.hasRewardModel()) {
708 STORM_LOG_THROW(this->
model.hasUniqueRewardModel(), storm::exceptions::IllegalFunctionCallException,
"Cannot preserve more than one reward model.");
709 typename std::unordered_map<std::string, typename ModelType::RewardModelType>::const_iterator nameRewardModelPair =
710 this->
model.getRewardModels().begin();
711 rewardModels.insert(std::make_pair(nameRewardModelPair->first,
typename ModelType::RewardModelType(stateRewards)));
715 this->
quotient = std::make_shared<ModelType>(
builder.build(), std::move(newLabeling), std::move(rewardModels));
718template<
typename ModelType>
721 if constexpr (std::is_same_v<ModelType, storm::models::sparse::Ctmc<typename ModelType::ValueType>>) {
722 auto transitionValue = matrixEntry.
getValue();
725 return transitionValue;
727 STORM_LOG_ASSERT(this->model.isDiscreteTimeModel(),
"Unhandled model type.");
void addLabel(std::string const &label)
Adds a new label to the labelings.
This class manages the labeling of the state space with a number of (atomic) labels.
void addLabelToState(std::string const &label, storm::storage::sparse::state_type state)
Adds a label to a given state.
storm::storage::SparseMatrix< ValueType > backwardTransitions
std::shared_ptr< ModelType > quotient
BisimulationDecomposition(ModelType const &model, Options const &options)
storm::storage::bisimulation::Partition< bisimulation::DeterministicBlockData > partition
virtual void initializeMeasureDrivenPartition()
Creates the measure-driven initial partition for reaching psi states from phi states.
virtual void initializeLabelBasedPartition()
Initializes the initial partition based on all respected labels.
A bit vector that is internally represented as a vector of 64-bit values.
std::vector< block_type > blocks
This class represents the decomposition of a deterministic model into its bisimulation quotient.
virtual void refinePartitionBasedOnSplitter(bisimulation::Block< BlockDataType > &splitter, std::vector< bisimulation::Block< BlockDataType > * > &splitterQueue) override
virtual void initializeLabelBasedPartition() override
Initializes the initial partition based on all respected labels.
virtual void initializeMeasureDrivenPartition() override
Creates the measure-driven initial partition for reaching psi states from phi states.
bisimulation::DeterministicBlockData BlockDataType
ModelType::ValueType ValueType
DeterministicModelBisimulationDecomposition(ModelType const &model, typename BisimulationDecomposition< ModelType, BlockDataType >::Options const &options)
Computes the bisimulation relation for the given model.
virtual void buildQuotient() override
Builds the quotient model based on the previously computed equivalence classes (stored in the blocks ...
virtual std::pair< storm::storage::BitVector, storm::storage::BitVector > getStatesWithProbability01() override
Computes the set of states with probability 0/1 for satisfying phi until psi.
value_type const & getValue() const
Retrieves the value of the matrix entry.
A class that can be used to build a sparse matrix by adding value by value.
std::size_t getNumberOfStates() const
storm::storage::sparse::state_type getBeginIndex() const
std::size_t getId() const
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01(storm::models::sparse::DeterministicModel< T > const &model, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi i...
bool isZero(ValueType const &a)