4#include <boost/iterator/zip_iterator.hpp>
7#include <unordered_map>
28using namespace bisimulation;
30template<
typename ModelType>
34 probabilitiesToCurrentSplitter(model.getNumberOfStates(),
storm::utility::zero<
ValueType>()) {
38template<
typename ModelType>
43template<
typename ModelType>
45 std::vector<storm::storage::sparse::state_type> stateStack;
46 stateStack.reserve(this->model.getNumberOfStates());
49 uint_fast64_t currentSize = this->partition.size();
50 for (uint_fast64_t blockIndex = 0; blockIndex < currentSize; ++blockIndex) {
51 auto& block = *this->partition.getBlocks()[blockIndex];
52 nondivergentStates.clear();
54 for (
auto stateIt = this->partition.begin(block), stateIte = this->partition.end(block); stateIt != stateIte; ++stateIt) {
55 if (nondivergentStates.get(*stateIt)) {
61 bool isDirectlyNonDivergent =
false;
62 for (
auto const& successor : this->model.getRows(*stateIt)) {
65 if (this->partition.getBlock(successor.getColumn()) != block) {
66 isDirectlyNonDivergent =
true;
71 if (isDirectlyNonDivergent) {
72 stateStack.push_back(*stateIt);
74 while (!stateStack.empty()) {
76 stateStack.pop_back();
77 nondivergentStates.set(currentState);
79 for (
auto const& predecessor : this->backwardTransitions.getRow(currentState)) {
80 if (this->partition.getBlock(predecessor.getColumn()) == block && !nondivergentStates.get(predecessor.getColumn())) {
81 stateStack.push_back(predecessor.getColumn());
88 if (!nondivergentStates.empty() && nondivergentStates.getNumberOfSetBits() != block.getNumberOfStates()) {
90 this->partition.splitStates(block, nondivergentStates);
95 block.data().setAbsorbing(
true);
96 }
else if (nondivergentStates.empty()) {
98 block.data().setAbsorbing(
true);
103template<
typename ModelType>
104void DeterministicModelBisimulationDecomposition<ModelType>::initializeSilentProbabilities() {
105 silentProbabilities.resize(this->model.getNumberOfStates(), storm::utility::zero<ValueType>());
107 Block<BlockDataType>
const* currentBlockPtr = &this->partition.getBlock(state);
108 for (
auto const& successorEntry : this->model.getRows(state)) {
109 if (&this->partition.getBlock(successorEntry.getColumn()) == currentBlockPtr) {
110 silentProbabilities[state] += successorEntry.getValue();
116template<
typename ModelType>
117void DeterministicModelBisimulationDecomposition<ModelType>::initializeWeakDtmcBisimulation() {
120 this->splitOffDivergentStates();
121 this->initializeSilentProbabilities();
124template<
typename ModelType>
125void DeterministicModelBisimulationDecomposition<ModelType>::postProcessInitialPartition() {
127 this->initializeWeakDtmcBisimulation();
130 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->options.getType() ==
BisimulationType::Weak) {
136 std::optional<std::vector<ValueType>>
const& optionalStateRewardVector = this->model.getUniqueRewardModel().getOptionalStateRewardVector();
137 std::optional<std::vector<ValueType>>
const& optionalStateActionRewardVector = this->model.getUniqueRewardModel().getOptionalStateActionRewardVector();
138 for (
auto& block : this->partition.getBlocks()) {
139 auto state = *this->partition.begin(*block);
140 block->data().setHasRewards((optionalStateRewardVector && !
storm::utility::isZero(optionalStateRewardVector.value()[state])) ||
141 (optionalStateActionRewardVector && !
storm::utility::isZero(optionalStateActionRewardVector.value()[state])));
146template<
typename ModelType>
149 postProcessInitialPartition();
152template<
typename ModelType>
155 postProcessInitialPartition();
158template<
typename ModelType>
161 return probabilitiesToCurrentSplitter[state];
164template<
typename ModelType>
166 return this->comparator.isOne(silentProbabilities[state]);
169template<
typename ModelType>
171 return !this->comparator.isZero(silentProbabilities[state]);
174template<
typename ModelType>
177 return silentProbabilities[state];
180template<
typename ModelType>
181void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterStrong(
182 bisimulation::Block<BlockDataType>& block, std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) {
183 STORM_LOG_TRACE(
"Refining predecessor " << block.getId() <<
" of splitter");
186 Block<BlockDataType>* blockToRefineProbabilistically = █
190 if (block.getBeginIndex() != block.data().marker1() && block.getEndIndex() != block.data().marker1()) {
192 this->partition.splitBlock(block, block.data().marker1());
193 blockToRefineProbabilistically = block.getPreviousBlockPointer();
196 blockToRefineProbabilistically->data().setHasRewards(block.data().hasRewards());
199 split |= this->partition.splitBlock(
200 *blockToRefineProbabilistically,
202 return this->comparator.isLess(getProbabilityToSplitter(state1), getProbabilityToSplitter(state2));
204 [&splitterQueue, &block](Block<BlockDataType>& newBlock) {
205 splitterQueue.emplace_back(&newBlock);
206 newBlock.data().setSplitter();
209 newBlock.data().setHasRewards(block.data().hasRewards());
214 if (split && !blockToRefineProbabilistically->data().splitter()) {
215 splitterQueue.emplace_back(blockToRefineProbabilistically);
216 blockToRefineProbabilistically->data().setSplitter();
220template<
typename ModelType>
221void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterStrong(
222 std::list<Block<BlockDataType>*>
const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) {
223 for (
auto block : predecessorBlocks) {
224 refinePredecessorBlockOfSplitterStrong(*block, splitterQueue);
227 block->resetMarkers();
230 block->data().setNeedsRefinement(
false);
234template<
typename ModelType>
235bool DeterministicModelBisimulationDecomposition<ModelType>::possiblyNeedsRefinement(bisimulation::Block<BlockDataType>
const& predecessorBlock)
const {
236 return predecessorBlock.getNumberOfStates() > 1 && !predecessorBlock.data().absorbing();
239template<
typename ModelType>
241 bisimulation::Block<BlockDataType>
const& predecessorBlock,
242 ValueType
const& value) {
243 STORM_LOG_TRACE(
"Increasing probability of " << predecessor <<
" to splitter by " << value <<
".");
247 if (predecessorPosition >= predecessorBlock.data().marker1()) {
249 probabilitiesToCurrentSplitter[predecessor] = value;
252 probabilitiesToCurrentSplitter[predecessor] += value;
256template<
typename ModelType>
258 bisimulation::Block<BlockDataType>& predecessorBlock) {
259 this->partition.swapStates(predecessor, this->partition.getState(predecessorBlock.data().marker1()));
260 predecessorBlock.data().incrementMarker1();
263template<
typename ModelType>
265 bisimulation::Block<BlockDataType>& predecessorBlock) {
266 this->partition.swapStates(predecessor, this->partition.getState(predecessorBlock.data().marker2()));
267 predecessorBlock.data().incrementMarker2();
270template<
typename ModelType>
272 bisimulation::Block<BlockDataType>& predecessorBlock,
274 uint_fast64_t& elementsToSkip) {
278 if (predecessorPosition <= currentPositionInSplitter + elementsToSkip) {
279 this->partition.swapStates(predecessor, this->partition.getState(predecessorBlock.data().marker1()));
280 predecessorBlock.data().incrementMarker1();
285 if (predecessorBlock.data().marker2() == predecessorBlock.data().marker1()) {
286 this->partition.swapStatesAtPositions(predecessorBlock.data().marker2(), predecessorPosition);
287 this->partition.swapStatesAtPositions(predecessorPosition, currentPositionInSplitter + elementsToSkip + 1);
289 this->partition.swapStatesAtPositions(predecessorBlock.data().marker2(), predecessorPosition);
290 this->partition.swapStatesAtPositions(predecessorPosition, predecessorBlock.data().marker1());
291 this->partition.swapStatesAtPositions(predecessorPosition, currentPositionInSplitter + elementsToSkip + 1);
296 predecessorBlock.data().incrementMarker1();
297 predecessorBlock.data().incrementMarker2();
301template<
typename ModelType>
302void DeterministicModelBisimulationDecomposition<ModelType>::exploreRemainingStatesOfSplitter(
303 bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*>& predecessorBlocks) {
304 for (
auto splitterIt = this->partition.begin(splitter),
305 splitterIte = this->partition.begin(splitter) + (splitter.data().marker2() - splitter.getBeginIndex());
306 splitterIt != splitterIte; ++splitterIt) {
309 for (
auto const& predecessorEntry : this->backwardTransitions.getRow(currentState)) {
311 Block<BlockDataType>& predecessorBlock = this->partition.getBlock(predecessor);
314 if (!possiblyNeedsRefinement(predecessorBlock)) {
325 increaseProbabilityToSplitter(predecessor, predecessorBlock, predecessorEntry.getValue());
329 if (predecessorPosition >= predecessorBlock.data().marker1()) {
330 moveStateToMarker1(predecessor, predecessorBlock);
335 insertIntoPredecessorList(predecessorBlock, predecessorBlocks);
341 splitter.data().setMarker2(splitter.getBeginIndex());
344template<
typename ModelType>
345void DeterministicModelBisimulationDecomposition<ModelType>::updateSilentProbabilitiesBasedOnProbabilitiesToSplitter(
346 bisimulation::Block<BlockDataType>& block) {
348 for (
auto stateIt = this->partition.begin(block), stateIte = this->partition.begin() + block.data().marker1(); stateIt != stateIte; ++stateIt) {
349 silentProbabilities[*stateIt] = probabilitiesToCurrentSplitter[*stateIt];
352 for (
auto stateIt = this->partition.begin() + block.data().marker1(), stateIte = this->partition.end(block); stateIt != stateIte; ++stateIt) {
353 silentProbabilities[*stateIt] = storm::utility::zero<ValueType>();
357template<
typename ModelType>
358void DeterministicModelBisimulationDecomposition<ModelType>::updateSilentProbabilitiesBasedOnTransitions(bisimulation::Block<BlockDataType>& block) {
359 for (
auto stateIt = this->partition.begin(block), stateIte = this->partition.end(block); stateIt != stateIte; ++stateIt) {
360 if (hasNonZeroSilentProbability(*stateIt)) {
361 ValueType newSilentProbability = storm::utility::zero<ValueType>();
362 for (
auto const& successorEntry : this->model.getTransitionMatrix().getRow(*stateIt)) {
363 if (this->partition.getBlock(successorEntry.getColumn()) == block) {
364 newSilentProbability += successorEntry.getValue();
367 silentProbabilities[*stateIt] = newSilentProbability;
372template<
typename ModelType>
373void DeterministicModelBisimulationDecomposition<ModelType>::computeConditionalProbabilitiesForNonSilentStates(bisimulation::Block<BlockDataType>& block) {
374 for (
auto stateIt = this->partition.begin() + block.getBeginIndex(), stateIte = this->partition.begin() + block.data().marker1(); stateIt != stateIte;
376 if (!this->comparator.isOne(getSilentProbability(*stateIt))) {
377 probabilitiesToCurrentSplitter[*stateIt] /= storm::utility::one<ValueType>() - getSilentProbability(*stateIt);
382template<
typename ModelType>
383std::vector<uint_fast64_t> DeterministicModelBisimulationDecomposition<ModelType>::computeNonSilentBlocks(bisimulation::Block<BlockDataType>& block) {
385 return probabilitiesToCurrentSplitter[state1] < probabilitiesToCurrentSplitter[state2];
387 this->partition.sortRange(block.getBeginIndex(), block.data().marker1(), less);
388 return this->partition.computeRangesOfEqualValue(block.getBeginIndex(), block.data().marker1(), less);
391template<
typename ModelType>
392std::vector<storm::storage::BitVector> DeterministicModelBisimulationDecomposition<ModelType>::computeWeakStateLabelingBasedOnNonSilentBlocks(
393 bisimulation::Block<BlockDataType>
const& block, std::vector<uint_fast64_t>
const& nonSilentBlockIndices) {
396 std::vector<storm::storage::BitVector> stateLabels(block.getNumberOfStates(),
storm::storage::BitVector(nonSilentBlockIndices.size() - 1));
398 std::vector<storm::storage::sparse::state_type> stateStack;
399 stateStack.reserve(block.getNumberOfStates());
400 for (uint_fast64_t stateClassIndex = 0; stateClassIndex < nonSilentBlockIndices.size() - 1; ++stateClassIndex) {
401 for (
auto stateIt = this->partition.begin() + nonSilentBlockIndices[stateClassIndex],
402 stateIte = this->partition.begin() + nonSilentBlockIndices[stateClassIndex + 1];
403 stateIt != stateIte; ++stateIt) {
404 stateStack.push_back(*stateIt);
405 stateLabels[this->partition.getPosition(*stateIt) - block.getBeginIndex()].set(stateClassIndex);
406 while (!stateStack.empty()) {
408 stateStack.pop_back();
410 for (
auto const& predecessorEntry : this->backwardTransitions.getRow(currentState)) {
413 if (this->comparator.isZero(predecessorEntry.getValue())) {
419 if (this->partition.getBlock(predecessor) == block && isSilent(predecessor) &&
420 !stateLabels[this->partition.getPosition(predecessor) - block.getBeginIndex()].get(stateClassIndex)) {
421 stateStack.push_back(predecessor);
422 stateLabels[this->partition.getPosition(predecessor) - block.getBeginIndex()].set(stateClassIndex);
432template<
typename ModelType>
433void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterWeak(
434 bisimulation::Block<BlockDataType>& block, std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) {
437 computeConditionalProbabilitiesForNonSilentStates(block);
440 std::vector<uint_fast64_t> nonSilentBlockIndices = computeNonSilentBlocks(block);
441 std::vector<storm::storage::BitVector> weakStateLabels = computeWeakStateLabelingBasedOnNonSilentBlocks(block, nonSilentBlockIndices);
447 auto split = this->partition.splitBlock(
450 return weakStateLabels[this->partition.getPosition(state1) - originalBlockIndex] <
451 weakStateLabels[this->partition.getPosition(state2) - originalBlockIndex];
453 [
this, &splitterQueue, &block](bisimulation::Block<BlockDataType>& newBlock) {
454 updateSilentProbabilitiesBasedOnTransitions(newBlock);
457 newBlock.data().setSplitter();
458 splitterQueue.emplace_back(&newBlock);
461 newBlock.data().setHasRewards(block.data().hasRewards());
466 updateSilentProbabilitiesBasedOnTransitions(block);
468 if (!block.data().splitter()) {
470 block.data().setSplitter();
471 splitterQueue.emplace_back(&block);
476template<
typename ModelType>
477void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterWeak(
478 bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*>
const& predecessorBlocks,
479 std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) {
480 for (
auto block : predecessorBlocks) {
481 if (block->data().hasRewards()) {
482 refinePredecessorBlockOfSplitterStrong(*block, splitterQueue);
484 if (*block != splitter) {
485 refinePredecessorBlockOfSplitterWeak(*block, splitterQueue);
491 block->resetMarkers();
492 block->data().setNeedsRefinement(
false);
496template<
typename ModelType>
497void DeterministicModelBisimulationDecomposition<ModelType>::insertIntoPredecessorList(bisimulation::Block<BlockDataType>& predecessorBlock,
498 std::list<bisimulation::Block<BlockDataType>*>& predecessorBlocks) {
500 if (!predecessorBlock.data().needsRefinement()) {
501 predecessorBlocks.emplace_back(&predecessorBlock);
502 predecessorBlock.data().setNeedsRefinement();
506template<
typename ModelType>
526 std::list<Block<BlockDataType>*> predecessorBlocks;
528 bool splitterIsPredecessorBlock =
false;
529 for (
auto splitterIt = this->partition.begin(splitter), splitterIte = this->partition.end(splitter); splitterIt != splitterIte;
530 ++splitterIt, ++currentPosition) {
533 uint_fast64_t elementsToSkip = 0;
534 for (
auto const& predecessorEntry : this->backwardTransitions.getRow(currentState)) {
540 if (!possiblyNeedsRefinement(predecessorBlock)) {
551 increaseProbabilityToSplitter(predecessor, predecessorBlock, predecessorEntry.getValue());
554 if (predecessorPosition >= predecessorBlock.
data().marker1()) {
556 if (predecessorBlock != splitter) {
557 moveStateToMarker1(predecessor, predecessorBlock);
560 splitterIsPredecessorBlock =
true;
561 moveStateInSplitter(predecessor, predecessorBlock, currentPosition, elementsToSkip);
564 insertIntoPredecessorList(predecessorBlock, predecessorBlocks);
569 splitterIt += elementsToSkip;
570 currentPosition += elementsToSkip;
575 if (splitterIsPredecessorBlock) {
576 exploreRemainingStatesOfSplitter(splitter, predecessorBlocks);
583 refinePredecessorBlocksOfSplitterStrong(predecessorBlocks, splitterQueue);
587 if (splitterIsPredecessorBlock) {
588 updateSilentProbabilitiesBasedOnProbabilitiesToSplitter(splitter);
591 refinePredecessorBlocksOfSplitterWeak(splitter, predecessorBlocks, splitterQueue);
595template<
typename ModelType>
607 std::set<std::string> atomicPropositionsSet = this->options.respectedAtomicPropositions.get();
608 atomicPropositionsSet.insert(
"init");
609 std::vector<std::string> atomicPropositions = std::vector<std::string>(atomicPropositionsSet.begin(), atomicPropositionsSet.end());
610 for (
auto const& ap : atomicPropositions) {
615 std::optional<std::vector<ValueType>> stateRewards;
616 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
617 stateRewards = std::vector<ValueType>(this->blocks.size());
621 for (uint_fast64_t blockIndex = 0; blockIndex < this->blocks.size(); ++blockIndex) {
622 auto const& block = this->blocks[blockIndex];
631 for (
auto const& state : block) {
632 if (!isSilent(state)) {
633 representativeState = state;
642 if (oldBlock.
data().absorbing()) {
643 builder.
addNextValue(blockIndex, blockIndex, storm::utility::one<ValueType>());
646 if (oldBlock.
data().hasRepresentativeState()) {
647 representativeState = oldBlock.
data().representativeState();
652 for (
auto const& ap : atomicPropositions) {
653 if (this->model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
659 std::map<storm::storage::sparse::state_type, ValueType> blockProbability;
660 for (
auto const& entry : this->model.getTransitionMatrix().getRow(representativeState)) {
668 auto probIterator = blockProbability.find(targetBlock);
669 if (probIterator != blockProbability.end()) {
670 probIterator->second += entry.getValue();
672 blockProbability[targetBlock] = entry.getValue();
677 for (
auto const& probabilityEntry : blockProbability) {
679 !oldBlock.
data().hasRewards()) {
680 builder.
addNextValue(blockIndex, probabilityEntry.first,
681 probabilityEntry.second / (storm::utility::one<ValueType>() - getSilentProbability(representativeState)));
683 builder.
addNextValue(blockIndex, probabilityEntry.first, probabilityEntry.second);
689 for (
auto const& ap : atomicPropositions) {
690 if (this->model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
698 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
699 auto const& rewardModel = this->model.getUniqueRewardModel();
700 if (rewardModel.hasStateRewards()) {
701 stateRewards.value()[blockIndex] = rewardModel.getStateRewardVector()[representativeState];
703 if (rewardModel.hasStateActionRewards()) {
704 stateRewards.value()[blockIndex] += rewardModel.getStateActionRewardVector()[representativeState];
710 for (
auto initialState : this->model.getInitialStates()) {
716 std::unordered_map<std::string, typename ModelType::RewardModelType> rewardModels;
717 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
718 STORM_LOG_THROW(this->model.hasUniqueRewardModel(), storm::exceptions::IllegalFunctionCallException,
"Cannot preserve more than one reward model.");
719 typename std::unordered_map<std::string, typename ModelType::RewardModelType>::const_iterator nameRewardModelPair =
720 this->model.getRewardModels().begin();
721 rewardModels.insert(std::make_pair(nameRewardModelPair->first,
typename ModelType::RewardModelType(stateRewards)));
725 this->quotient = std::shared_ptr<ModelType>(
new ModelType(builder.
build(), std::move(newLabeling), std::move(rewardModels)));
731#ifdef STORM_HAVE_CARL
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.
This class is the superclass of all decompositions of a sparse model into its bisimulation quotient.
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.
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.
DeterministicModelBisimulationDecomposition(ModelType const &model, typename BisimulationDecomposition< ModelType, BlockDataType >::Options const &options=typename BisimulationDecomposition< ModelType, BlockDataType >::Options())
Computes the bisimulation relation for the given model.
ModelType::ValueType ValueType
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.
A class that can be used to build a sparse matrix by adding value by value.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
storm::storage::sparse::state_type getBeginIndex() const
std::size_t getId() const
#define STORM_LOG_TRACE(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)