|  |  | @ -118,22 +118,37 @@ namespace storm { | 
			
		
	
		
			
				
					|  |  |  |             this->initializeSilentProbabilities(); | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::initializeMeasureDrivenPartition() { | 
			
		
	
		
			
				
					|  |  |  |             BisimulationDecomposition<ModelType, BlockDataType>::initializeMeasureDrivenPartition(); | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::postProcessInitialPartition() { | 
			
		
	
		
			
				
					|  |  |  |             if (this->options.getType() == BisimulationType::Weak && this->model.getType() == storm::models::ModelType::Dtmc) { | 
			
		
	
		
			
				
					|  |  |  |                 this->initializeWeakDtmcBisimulation(); | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->options.getType() == BisimulationType::Weak) { | 
			
		
	
		
			
				
					|  |  |  |                 // For a weak bisimulation that is to preserve reward properties, we have to flag all blocks of states
 | 
			
		
	
		
			
				
					|  |  |  |                 // with non-zero reward as reward blocks to they can be refined wrt. strong bisimulation.
 | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 // Here, we assume that the initial partition already respects state rewards. Therefore, it suffices to
 | 
			
		
	
		
			
				
					|  |  |  |                 // check the first state of each block for a non-zero reward.
 | 
			
		
	
		
			
				
					|  |  |  |                 std::vector<ValueType> const& stateRewardVector = this->model.getUniqueRewardModel().getStateRewardVector(); | 
			
		
	
		
			
				
					|  |  |  |                 for (auto& block : this->partition.getBlocks()) { | 
			
		
	
		
			
				
					|  |  |  |                     auto state = *this->partition.begin(*block); | 
			
		
	
		
			
				
					|  |  |  |                     block->data().setHasRewards(!storm::utility::isZero(stateRewardVector[state])); | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::initializeMeasureDrivenPartition() { | 
			
		
	
		
			
				
					|  |  |  |             BisimulationDecomposition<ModelType, BlockDataType>::initializeMeasureDrivenPartition(); | 
			
		
	
		
			
				
					|  |  |  |             postProcessInitialPartition(); | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::initializeLabelBasedPartition() { | 
			
		
	
		
			
				
					|  |  |  |             BisimulationDecomposition<ModelType, BlockDataType>::initializeLabelBasedPartition(); | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             if (this->options.getType() == BisimulationType::Weak && this->model.getType() == storm::models::ModelType::Dtmc) { | 
			
		
	
		
			
				
					|  |  |  |                 this->initializeWeakDtmcBisimulation(); | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |             postProcessInitialPartition(); | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
	
		
			
				
					|  |  | @ -156,37 +171,49 @@ namespace storm { | 
			
		
	
		
			
				
					|  |  |  |             return silentProbabilities[state]; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterStrong(bisimulation::Block<BlockDataType>& block, std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) { | 
			
		
	
		
			
				
					|  |  |  |             STORM_LOG_TRACE("Refining predecessor " << block.getId() << " of splitter"); | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             // Depending on the actions we need to take, the block to refine changes, so we need to keep track of it.
 | 
			
		
	
		
			
				
					|  |  |  |             Block<BlockDataType>* blockToRefineProbabilistically = █ | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             bool split = false; | 
			
		
	
		
			
				
					|  |  |  |             // If the new begin index has shifted to a non-trivial position, we need to split the block.
 | 
			
		
	
		
			
				
					|  |  |  |             if (block.getBeginIndex() != block.data().marker1() && block.getEndIndex() != block.data().marker1()) { | 
			
		
	
		
			
				
					|  |  |  |                 split = true; | 
			
		
	
		
			
				
					|  |  |  |                 this->partition.splitBlock(block, block.data().marker1()); | 
			
		
	
		
			
				
					|  |  |  |                 blockToRefineProbabilistically = block.getPreviousBlockPointer(); | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 // Keep track of whether this is a block with reward states.
 | 
			
		
	
		
			
				
					|  |  |  |                 blockToRefineProbabilistically->data().setHasRewards(block.data().hasRewards()); | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             split |= this->partition.splitBlock(*blockToRefineProbabilistically, | 
			
		
	
		
			
				
					|  |  |  |                                                 [this] (storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) { | 
			
		
	
		
			
				
					|  |  |  |                                                     return this->comparator.isLess(getProbabilityToSplitter(state1), getProbabilityToSplitter(state2)); | 
			
		
	
		
			
				
					|  |  |  |                                                 }, | 
			
		
	
		
			
				
					|  |  |  |                                                 [&splitterQueue,&block] (Block<BlockDataType>& newBlock) { | 
			
		
	
		
			
				
					|  |  |  |                                                     splitterQueue.emplace_back(&newBlock); | 
			
		
	
		
			
				
					|  |  |  |                                                     newBlock.data().setSplitter(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |                                                     // Keep track of whether this is a block with reward states.
 | 
			
		
	
		
			
				
					|  |  |  |                                                     newBlock.data().setHasRewards(block.data().hasRewards()); | 
			
		
	
		
			
				
					|  |  |  |                                                 }); | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             // If the predecessor block was split, we need to insert it into the splitter vector if it is not already
 | 
			
		
	
		
			
				
					|  |  |  |             // marked as a splitter.
 | 
			
		
	
		
			
				
					|  |  |  |             if (split && !blockToRefineProbabilistically->data().splitter()) { | 
			
		
	
		
			
				
					|  |  |  |                 splitterQueue.emplace_back(blockToRefineProbabilistically); | 
			
		
	
		
			
				
					|  |  |  |                 blockToRefineProbabilistically->data().setSplitter(); | 
			
		
	
		
			
				
					|  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterStrong(std::list<Block<BlockDataType>*> const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) { | 
			
		
	
		
			
				
					|  |  |  |             for (auto block : predecessorBlocks) { | 
			
		
	
		
			
				
					|  |  |  |                 STORM_LOG_TRACE("Refining predecessor " << block->getId() << " of splitter"); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |                 // Depending on the actions we need to take, the block to refine changes, so we need to keep track of it.
 | 
			
		
	
		
			
				
					|  |  |  |                 Block<BlockDataType>* blockToRefineProbabilistically = block; | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 bool split = false; | 
			
		
	
		
			
				
					|  |  |  |                 // If the new begin index has shifted to a non-trivial position, we need to split the block.
 | 
			
		
	
		
			
				
					|  |  |  |                 if (block->getBeginIndex() != block->data().marker1() && block->getEndIndex() != block->data().marker1()) { | 
			
		
	
		
			
				
					|  |  |  |                     split = true; | 
			
		
	
		
			
				
					|  |  |  |                     this->partition.splitBlock(*block, block->data().marker1()); | 
			
		
	
		
			
				
					|  |  |  |                     blockToRefineProbabilistically = block->getPreviousBlockPointer(); | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 split |= this->partition.splitBlock(*blockToRefineProbabilistically, | 
			
		
	
		
			
				
					|  |  |  |                                                     [this] (storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) { | 
			
		
	
		
			
				
					|  |  |  |                                                         return this->comparator.isLess(getProbabilityToSplitter(state1), getProbabilityToSplitter(state2)); | 
			
		
	
		
			
				
					|  |  |  |                                                     }, | 
			
		
	
		
			
				
					|  |  |  |                                                     [&splitterQueue] (Block<BlockDataType>& block) { | 
			
		
	
		
			
				
					|  |  |  |                                                         splitterQueue.emplace_back(&block); block.data().setSplitter(); | 
			
		
	
		
			
				
					|  |  |  |                                                     }); | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 // If the predecessor block was split, we need to insert it into the splitter vector if it is not already
 | 
			
		
	
		
			
				
					|  |  |  |                 // marked as a splitter.
 | 
			
		
	
		
			
				
					|  |  |  |                 if (split && !blockToRefineProbabilistically->data().splitter()) { | 
			
		
	
		
			
				
					|  |  |  |                     splitterQueue.emplace_back(blockToRefineProbabilistically); | 
			
		
	
		
			
				
					|  |  |  |                     blockToRefineProbabilistically->data().setSplitter(); | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |                 refinePredecessorBlockOfSplitterStrong(*block, splitterQueue); | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 // If the block was *not* split, we need to reset the markers by notifying the data.
 | 
			
		
	
		
			
				
					|  |  |  |                 block->resetMarkers(); | 
			
		
	
	
		
			
				
					|  |  | @ -395,12 +422,15 @@ namespace storm { | 
			
		
	
		
			
				
					|  |  |  |                                                      [&weakStateLabels,&block,originalBlockIndex,this] (storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) { | 
			
		
	
		
			
				
					|  |  |  |                                                          return weakStateLabels[this->partition.getPosition(state1) - originalBlockIndex] < weakStateLabels[this->partition.getPosition(state2) - originalBlockIndex]; | 
			
		
	
		
			
				
					|  |  |  |                                                      }, | 
			
		
	
		
			
				
					|  |  |  |                                                      [this, &splitterQueue] (bisimulation::Block<BlockDataType>& block) { | 
			
		
	
		
			
				
					|  |  |  |                                                          updateSilentProbabilitiesBasedOnTransitions(block); | 
			
		
	
		
			
				
					|  |  |  |                                                      [this, &splitterQueue, &block] (bisimulation::Block<BlockDataType>& newBlock) { | 
			
		
	
		
			
				
					|  |  |  |                                                          updateSilentProbabilitiesBasedOnTransitions(newBlock); | 
			
		
	
		
			
				
					|  |  |  |                                                           | 
			
		
	
		
			
				
					|  |  |  |                                                          // Insert the new block as a splitter.
 | 
			
		
	
		
			
				
					|  |  |  |                                                          block.data().setSplitter(); | 
			
		
	
		
			
				
					|  |  |  |                                                          splitterQueue.emplace_back(&block); | 
			
		
	
		
			
				
					|  |  |  |                                                          newBlock.data().setSplitter(); | 
			
		
	
		
			
				
					|  |  |  |                                                          splitterQueue.emplace_back(&newBlock); | 
			
		
	
		
			
				
					|  |  |  |                                                           | 
			
		
	
		
			
				
					|  |  |  |                                                          // Keep track of whether this is a block with reward states.
 | 
			
		
	
		
			
				
					|  |  |  |                                                          newBlock.data().setHasRewards(block.data().hasRewards()); | 
			
		
	
		
			
				
					|  |  |  |                                                      }); | 
			
		
	
		
			
				
					|  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |             // If the block was split, we also update the silent probabilities.
 | 
			
		
	
	
		
			
				
					|  |  | @ -418,10 +448,14 @@ namespace storm { | 
			
		
	
		
			
				
					|  |  |  |         template<typename ModelType> | 
			
		
	
		
			
				
					|  |  |  |         void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterWeak(bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterQueue) { | 
			
		
	
		
			
				
					|  |  |  |             for (auto block : predecessorBlocks) { | 
			
		
	
		
			
				
					|  |  |  |                 if (*block != splitter) { | 
			
		
	
		
			
				
					|  |  |  |                     refinePredecessorBlockOfSplitterWeak(*block, splitterQueue); | 
			
		
	
		
			
				
					|  |  |  |                 if (block->data().hasRewards()) { | 
			
		
	
		
			
				
					|  |  |  |                     refinePredecessorBlockOfSplitterStrong(*block, splitterQueue); | 
			
		
	
		
			
				
					|  |  |  |                 } else { | 
			
		
	
		
			
				
					|  |  |  |                     // If the block to split is the splitter itself, we must not do any splitting here.
 | 
			
		
	
		
			
				
					|  |  |  |                     if (*block != splitter) { | 
			
		
	
		
			
				
					|  |  |  |                         refinePredecessorBlockOfSplitterWeak(*block, splitterQueue); | 
			
		
	
		
			
				
					|  |  |  |                     } else { | 
			
		
	
		
			
				
					|  |  |  |                         // If the block to split is the splitter itself, we must not do any splitting here.
 | 
			
		
	
		
			
				
					|  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |                  | 
			
		
	
		
			
				
					|  |  |  |                 block->resetMarkers(); | 
			
		
	
	
		
			
				
					|  |  | @ -594,7 +628,7 @@ namespace storm { | 
			
		
	
		
			
				
					|  |  |  |                         storm::storage::sparse::state_type targetBlock = this->partition.getBlock(entry.getColumn()).getId(); | 
			
		
	
		
			
				
					|  |  |  |                          | 
			
		
	
		
			
				
					|  |  |  |                         // If we are computing a weak bisimulation quotient, there is no need to add self-loops.
 | 
			
		
	
		
			
				
					|  |  |  |                         if ((this->options.getType() == BisimulationType::Weak) && targetBlock == blockIndex) { | 
			
		
	
		
			
				
					|  |  |  |                         if ((this->options.getType() == BisimulationType::Weak) && targetBlock == blockIndex && !oldBlock.data().hasRewards()) { | 
			
		
	
		
			
				
					|  |  |  |                             continue; | 
			
		
	
		
			
				
					|  |  |  |                         } | 
			
		
	
		
			
				
					|  |  |  |                          | 
			
		
	
	
		
			
				
					|  |  | @ -608,7 +642,7 @@ namespace storm { | 
			
		
	
		
			
				
					|  |  |  |                      | 
			
		
	
		
			
				
					|  |  |  |                     // Now add them to the actual matrix.
 | 
			
		
	
		
			
				
					|  |  |  |                     for (auto const& probabilityEntry : blockProbability) { | 
			
		
	
		
			
				
					|  |  |  |                         if (this->options.getType() == BisimulationType::Weak && this->model.getType() == storm::models::ModelType::Dtmc) { | 
			
		
	
		
			
				
					|  |  |  |                         if (this->options.getType() == BisimulationType::Weak && this->model.getType() == storm::models::ModelType::Dtmc && !oldBlock.data().hasRewards()) { | 
			
		
	
		
			
				
					|  |  |  |                             builder.addNextValue(blockIndex, probabilityEntry.first, probabilityEntry.second / (storm::utility::one<ValueType>() - getSilentProbability(representativeState))); | 
			
		
	
		
			
				
					|  |  |  |                         } else { | 
			
		
	
		
			
				
					|  |  |  |                             builder.addNextValue(blockIndex, probabilityEntry.first, probabilityEntry.second); | 
			
		
	
	
		
			
				
					|  |  | 
 |