12using namespace bisimulation;
14template<
typename ModelType>
16 ModelType
const& model,
20 choiceToStateMapping(model.getNumberOfChoices()),
21 quotientDistributions(model.getNumberOfChoices()),
22 orderedQuotientDistributions(model.getNumberOfChoices()) {
24 "Weak bisimulation is currently not supported for nondeterministic models.");
27template<
typename ModelType>
29 STORM_LOG_THROW(this->options.isOptimizationDirectionSet(), storm::exceptions::IllegalFunctionCallException,
30 "Can only compute states with probability 0/1 with an optimization direction (min/max).");
31 if (this->options.getOptimizationDirection() == OptimizationDirection::Minimize) {
33 this->model.getBackwardTransitions(), this->options.phiStates.get(), this->options.psiStates.get());
36 this->model.getBackwardTransitions(), this->options.phiStates.get(), this->options.psiStates.get());
40template<
typename ModelType>
42 this->createChoiceToStateMapping();
43 this->initializeQuotientDistributions();
46template<
typename ModelType>
48 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
50 for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
51 choiceToStateMapping[choice] = state;
56template<
typename ModelType>
57void NondeterministicModelBisimulationDecomposition<ModelType>::initializeQuotientDistributions() {
58 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
60 for (
auto const& block : this->partition.getBlocks()) {
61 if (block->data().absorbing()) {
63 for (
auto stateIt = this->partition.begin(*block), stateIte = this->partition.end(*block); stateIt != stateIte; ++stateIt) {
64 for (uint_fast64_t choice = nondeterministicChoiceIndices[*stateIt]; choice < nondeterministicChoiceIndices[*stateIt + 1]; ++choice) {
65 this->quotientDistributions[choice].addProbability(block->getId(), storm::utility::one<ValueType>());
66 orderedQuotientDistributions[choice] = &this->quotientDistributions[choice];
71 for (
auto stateIt = this->partition.begin(*block), stateIte = this->partition.end(*block); stateIt != stateIte; ++stateIt) {
72 for (uint_fast64_t choice = nondeterministicChoiceIndices[*stateIt]; choice < nondeterministicChoiceIndices[*stateIt + 1]; ++choice) {
73 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
74 auto const& rewardModel = this->model.getUniqueRewardModel();
75 if (rewardModel.hasStateActionRewards()) {
76 this->quotientDistributions[choice].setReward(rewardModel.getStateActionReward(choice));
79 for (
auto entry : this->model.getTransitionMatrix().getRow(choice)) {
80 if (!this->comparator.isZero(entry.getValue())) {
81 this->quotientDistributions[choice].addProbability(this->partition.getBlock(entry.getColumn()).getId(), entry.getValue());
84 orderedQuotientDistributions[choice] = &this->quotientDistributions[choice];
90 for (
decltype(this->model.getNumberOfStates()) state = 0; state < this->model.getNumberOfStates(); ++state) {
91 updateOrderedQuotientDistributions(state);
95template<
typename ModelType>
97 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
98 std::sort(this->orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state],
99 this->orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state + 1],
101 return dist1->less(*dist2, this->comparator);
105template<
typename ModelType>
117 std::set<std::string> atomicPropositionsSet = this->options.respectedAtomicPropositions.get();
118 atomicPropositionsSet.insert(
"init");
119 std::vector<std::string> atomicPropositions = std::vector<std::string>(atomicPropositionsSet.begin(), atomicPropositionsSet.end());
120 for (
auto const& ap : atomicPropositions) {
125 std::optional<std::vector<ValueType>> stateRewards;
126 std::optional<std::vector<ValueType>> stateActionRewards;
127 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
128 if (this->model.getUniqueRewardModel().hasStateRewards()) {
129 stateRewards = std::vector<ValueType>(this->blocks.size());
131 if (this->model.getUniqueRewardModel().hasStateActionRewards()) {
132 stateActionRewards = std::vector<ValueType>();
137 uint_fast64_t currentRow = 0;
138 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
139 for (uint_fast64_t blockIndex = 0; blockIndex < this->blocks.size(); ++blockIndex) {
140 auto const& block = this->blocks[blockIndex];
151 if (oldBlock.
data().absorbing()) {
152 builder.
addNextValue(currentRow, blockIndex, storm::utility::one<ValueType>());
156 if (oldBlock.
data().hasRepresentativeState()) {
157 representativeState = oldBlock.
data().representativeState();
161 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->model.getUniqueRewardModel().hasStateActionRewards()) {
162 stateActionRewards.value().push_back(storm::utility::zero<ValueType>());
167 for (
auto const& ap : atomicPropositions) {
168 if (this->model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
174 for (uint_fast64_t choice = nondeterministicChoiceIndices[representativeState]; choice < nondeterministicChoiceIndices[representativeState + 1];
177 if (choice > nondeterministicChoiceIndices[representativeState] &&
178 quotientDistributions[choice - 1].equals(quotientDistributions[choice], this->comparator)) {
182 for (
auto entry : quotientDistributions[choice]) {
183 builder.
addNextValue(currentRow, entry.first, entry.second);
185 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->model.getUniqueRewardModel().hasStateActionRewards()) {
186 stateActionRewards.value().push_back(quotientDistributions[choice].getReward());
193 for (
auto const& ap : atomicPropositions) {
194 if (this->model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
202 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->model.getUniqueRewardModel().hasStateRewards()) {
203 stateRewards.value()[blockIndex] = this->model.getUniqueRewardModel().getStateRewardVector()[representativeState];
208 for (
auto initialState : this->model.getInitialStates()) {
214 std::unordered_map<std::string, typename ModelType::RewardModelType> rewardModels;
215 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
216 STORM_LOG_THROW(this->model.hasUniqueRewardModel(), storm::exceptions::IllegalFunctionCallException,
"Cannot preserve more than one reward model.");
217 typename std::unordered_map<std::string, typename ModelType::RewardModelType>::const_iterator nameRewardModelPair =
218 this->model.getRewardModels().begin();
219 rewardModels.insert(std::make_pair(nameRewardModelPair->first,
typename ModelType::RewardModelType(stateRewards, stateActionRewards)));
223 this->quotient = std::make_shared<ModelType>(builder.
build(0, this->size(), this->size()), std::move(newLabeling), std::move(rewardModels));
226template<
typename ModelType>
231template<
typename ModelType>
232void NondeterministicModelBisimulationDecomposition<ModelType>::updateQuotientDistributionsOfPredecessors(Block<BlockDataType>
const& newBlock,
233 Block<BlockDataType>
const& oldBlock,
234 std::vector<Block<BlockDataType>*>& splitterQueue) {
235 uint_fast64_t lastState = 0;
236 bool lastStateInitialized =
false;
238 for (
auto stateIt = this->partition.begin(newBlock), stateIte = this->partition.end(newBlock); stateIt != stateIte; ++stateIt) {
239 for (
auto predecessorEntry : this->backwardTransitions.getRow(*stateIt)) {
240 if (this->comparator.isZero(predecessorEntry.getValue())) {
246 Block<BlockDataType>& predecessorBlock = this->partition.getBlock(predecessorState);
249 if (predecessorBlock.data().absorbing()) {
254 if (!predecessorBlock.data().splitter()) {
255 predecessorBlock.data().setSplitter();
256 splitterQueue.push_back(&predecessorBlock);
259 if (lastStateInitialized) {
262 if (lastState != predecessorState) {
263 updateOrderedQuotientDistributions(lastState);
264 lastState = predecessorState;
267 lastStateInitialized =
true;
268 lastState = choiceToStateMapping[predecessorChoice];
272 this->quotientDistributions[predecessorChoice].shiftProbability(oldBlock.getId(), newBlock.getId(), predecessorEntry.getValue(), this->comparator);
276 if (lastStateInitialized) {
277 updateOrderedQuotientDistributions(lastState);
281template<
typename ModelType>
282bool NondeterministicModelBisimulationDecomposition<ModelType>::checkQuotientDistributions()
const {
283 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
284 for (
decltype(this->model.getNumberOfStates()) state = 0; state < this->model.getNumberOfStates(); ++state) {
285 for (
auto choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
287 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
288 auto const& rewardModel = this->model.getUniqueRewardModel();
289 if (rewardModel.hasStateActionRewards()) {
290 distribution.
setReward(rewardModel.getStateActionReward(choice));
293 for (
auto const& element : this->model.getTransitionMatrix().getRow(choice)) {
294 distribution.
addProbability(this->partition.getBlock(element.getColumn()).getId(), element.getValue());
297 if (!distribution.
equals(quotientDistributions[choice], this->comparator)) {
298 std::cout <<
"the distributions for choice " << choice <<
" of state " << state <<
" do not match.\n";
299 std::cout <<
"is: " << quotientDistributions[choice] <<
" but should be " << distribution <<
'\n';
303 bool less1 = distribution.
less(quotientDistributions[choice], this->comparator);
304 bool less2 = quotientDistributions[choice].less(distribution, this->comparator);
306 if (distribution.
equals(quotientDistributions[choice], this->comparator) && (less1 || less2)) {
307 std::cout <<
"mismatch of equality and less for \n";
308 std::cout << quotientDistributions[choice] <<
" vs " << distribution <<
'\n';
313 for (
auto choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1] - 1; ++choice) {
314 if (orderedQuotientDistributions[choice + 1]->less(*orderedQuotientDistributions[choice], this->comparator)) {
315 std::cout <<
"choice " << (choice + 1) <<
" is less than predecessor\n";
316 std::cout << *orderedQuotientDistributions[choice] <<
" should be less than " << *orderedQuotientDistributions[choice + 1] <<
'\n';
324template<
typename ModelType>
325bool NondeterministicModelBisimulationDecomposition<ModelType>::printDistributions(uint_fast64_t state)
const {
326 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
327 for (
auto choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
328 std::cout << quotientDistributions[choice] <<
'\n';
333template<
typename ModelType>
334bool NondeterministicModelBisimulationDecomposition<ModelType>::checkBlockStable(bisimulation::Block<BlockDataType>
const& newBlock)
const {
335 std::cout <<
"checking stability of new block " << newBlock.getId() <<
" of size " << newBlock.getNumberOfStates() <<
'\n';
336 for (
auto stateIt1 = this->partition.begin(newBlock), stateIte1 = this->partition.end(newBlock); stateIt1 != stateIte1; ++stateIt1) {
337 for (
auto stateIt2 = this->partition.begin(newBlock), stateIte2 = this->partition.end(newBlock); stateIt2 != stateIte2; ++stateIt2) {
338 bool less1 = quotientDistributionsLess(*stateIt1, *stateIt2);
339 bool less2 = quotientDistributionsLess(*stateIt2, *stateIt1);
340 if (less1 || less2) {
341 std::cout <<
"the partition is not stable for the states " << *stateIt1 <<
" and " << *stateIt2 <<
'\n';
342 std::cout <<
"less1 " << less1 <<
" and less2 " << less2 <<
'\n';
344 std::cout <<
"distributions of state " << *stateIt1 <<
'\n';
345 this->printDistributions(*stateIt1);
346 std::cout <<
"distributions of state " << *stateIt2 <<
'\n';
347 this->printDistributions(*stateIt2);
355template<
typename ModelType>
356bool NondeterministicModelBisimulationDecomposition<ModelType>::checkDistributionsDifferent(bisimulation::Block<BlockDataType>
const& block,
358 for (
auto stateIt1 = this->partition.begin(block), stateIte1 = this->partition.end(block); stateIt1 != stateIte1; ++stateIt1) {
359 for (
auto stateIt2 = this->partition.begin() + block.getEndIndex(), stateIte2 = this->partition.begin() + end; stateIt2 != stateIte2; ++stateIt2) {
360 if (!quotientDistributionsLess(*stateIt1, *stateIt2)) {
361 std::cout <<
"distributions are not less, even though they should be!\n";
364 std::cout <<
"less:\n";
365 this->printDistributions(*stateIt1);
366 std::cout <<
"and\n";
367 this->printDistributions(*stateIt2);
374template<
typename ModelType>
375bool NondeterministicModelBisimulationDecomposition<ModelType>::splitBlockAccordingToCurrentQuotientDistributions(
376 Block<BlockDataType>& block, std::vector<Block<BlockDataType>*>& splitterQueue) {
377 std::list<Block<BlockDataType>*> newBlocks;
378 bool split = this->partition.splitBlock(
381 bool result = quotientDistributionsLess(state1, state2);
384 [&newBlocks](Block<BlockDataType>& newBlock) { newBlocks.push_back(&newBlock); });
388 for (
auto el : newBlocks) {
389 this->updateQuotientDistributionsOfPredecessors(*el, block, splitterQueue);
395template<
typename ModelType>
398 STORM_LOG_TRACE(
"Comparing the quotient distributions of state " << state1 <<
" and " << state2 <<
".");
399 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
401 auto firstIt = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state1];
402 auto firstIte = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state1 + 1];
403 auto secondIt = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state2];
404 auto secondIte = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state2 + 1];
406 for (; firstIt != firstIte && secondIt != secondIte; ++firstIt, ++secondIt) {
408 if ((*firstIt)->less(**secondIt, this->comparator)) {
410 }
else if ((*secondIt)->less(**firstIt, this->comparator)) {
416 while (firstIt != firstIte && std::next(firstIt) != firstIte && !(*firstIt)->less(**std::next(firstIt), this->comparator)) {
419 while (secondIt != secondIte && std::next(secondIt) != secondIte && !(*secondIt)->less(**std::next(secondIt), this->comparator)) {
424 if (firstIt == firstIte && secondIt != secondIte) {
430template<
typename ModelType>
433 if (!possiblyNeedsRefinement(splitter)) {
439 splitBlockAccordingToCurrentQuotientDistributions(splitter, splitterQueue);
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.
void addProbability(StateType const &state, ValueType const &probability)
Assigns the given state the given probability under this distribution.
bool less(DistributionWithReward< ValueType, StateType > const &other, storm::utility::ConstantsComparator< ValueType > const &comparator) const
bool equals(DistributionWithReward< ValueType, StateType > const &other, storm::utility::ConstantsComparator< ValueType > const &comparator) const
Checks whether the two distributions specify the same probabilities to go to the same states.
void setReward(ValueType const &reward)
Sets the reward of this distribution.
This class represents the decomposition of a nondeterministic model into its bisimulation quotient.
virtual void refinePartitionBasedOnSplitter(bisimulation::Block< BlockDataType > &splitter, std::vector< bisimulation::Block< BlockDataType > * > &splitterQueue) override
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.
NondeterministicModelBisimulationDecomposition(ModelType const &model, typename BisimulationDecomposition< ModelType, BlockDataType >::Options const &options=typename BisimulationDecomposition< ModelType, BlockDataType >::Options())
Computes the bisimulation relation for the given model.
virtual void initialize() override
A function that can initialize auxiliary data structures.
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.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
std::size_t getNumberOfStates() const
std::size_t getId() const
#define STORM_LOG_TRACE(message)
#define STORM_LOG_THROW(cond, exception, message)
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Max(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Min(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)