16using namespace bisimulation;
18template<
typename ModelType>
20 ModelType
const& model,
24 choiceToStateMapping(model.getNumberOfChoices()),
25 quotientDistributions(model.getNumberOfChoices()),
26 orderedQuotientDistributions(model.getNumberOfChoices()) {
28 "Weak bisimulation is currently not supported for nondeterministic models.");
31template<
typename ModelType>
33 STORM_LOG_THROW(this->options.isOptimizationDirectionSet(), storm::exceptions::IllegalFunctionCallException,
34 "Can only compute states with probability 0/1 with an optimization direction (min/max).");
35 if (this->options.getOptimizationDirection() == OptimizationDirection::Minimize) {
37 this->model.getBackwardTransitions(), this->options.phiStates.get(), this->options.psiStates.get());
40 this->model.getBackwardTransitions(), this->options.phiStates.get(), this->options.psiStates.get());
44template<
typename ModelType>
46 this->createChoiceToStateMapping();
47 this->initializeQuotientDistributions();
50template<
typename ModelType>
52 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
54 for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
55 choiceToStateMapping[choice] = state;
60template<
typename ModelType>
61void NondeterministicModelBisimulationDecomposition<ModelType>::initializeQuotientDistributions() {
62 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
64 for (
auto const& block : this->partition.getBlocks()) {
65 if (block->data().absorbing()) {
67 for (
auto stateIt = this->partition.begin(*block), stateIte = this->partition.end(*block); stateIt != stateIte; ++stateIt) {
68 for (uint_fast64_t choice = nondeterministicChoiceIndices[*stateIt]; choice < nondeterministicChoiceIndices[*stateIt + 1]; ++choice) {
69 this->quotientDistributions[choice].addProbability(block->getId(), storm::utility::one<ValueType>());
70 orderedQuotientDistributions[choice] = &this->quotientDistributions[choice];
75 for (
auto stateIt = this->partition.begin(*block), stateIte = this->partition.end(*block); stateIt != stateIte; ++stateIt) {
76 for (uint_fast64_t choice = nondeterministicChoiceIndices[*stateIt]; choice < nondeterministicChoiceIndices[*stateIt + 1]; ++choice) {
77 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
78 auto const& rewardModel = this->model.getUniqueRewardModel();
79 if (rewardModel.hasStateActionRewards()) {
80 this->quotientDistributions[choice].setReward(rewardModel.getStateActionReward(choice));
83 for (
auto entry : this->model.getTransitionMatrix().getRow(choice)) {
84 if (!this->comparator.isZero(entry.getValue())) {
85 this->quotientDistributions[choice].addProbability(this->partition.getBlock(entry.getColumn()).getId(), entry.getValue());
88 orderedQuotientDistributions[choice] = &this->quotientDistributions[choice];
94 for (
decltype(this->model.getNumberOfStates()) state = 0; state < this->model.getNumberOfStates(); ++state) {
95 updateOrderedQuotientDistributions(state);
99template<
typename ModelType>
101 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
102 std::sort(this->orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state],
103 this->orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state + 1],
105 return dist1->less(*dist2, this->comparator);
109template<
typename ModelType>
121 std::set<std::string> atomicPropositionsSet = this->options.respectedAtomicPropositions.get();
122 atomicPropositionsSet.insert(
"init");
123 std::vector<std::string> atomicPropositions = std::vector<std::string>(atomicPropositionsSet.begin(), atomicPropositionsSet.end());
124 for (
auto const& ap : atomicPropositions) {
129 std::optional<std::vector<ValueType>> stateRewards;
130 std::optional<std::vector<ValueType>> stateActionRewards;
131 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
132 if (this->model.getUniqueRewardModel().hasStateRewards()) {
133 stateRewards = std::vector<ValueType>(this->blocks.size());
135 if (this->model.getUniqueRewardModel().hasStateActionRewards()) {
136 stateActionRewards = std::vector<ValueType>();
141 uint_fast64_t currentRow = 0;
142 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
143 for (uint_fast64_t blockIndex = 0; blockIndex < this->blocks.size(); ++blockIndex) {
144 auto const& block = this->blocks[blockIndex];
155 if (oldBlock.
data().absorbing()) {
156 builder.
addNextValue(currentRow, blockIndex, storm::utility::one<ValueType>());
160 if (oldBlock.
data().hasRepresentativeState()) {
161 representativeState = oldBlock.
data().representativeState();
165 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->model.getUniqueRewardModel().hasStateActionRewards()) {
166 stateActionRewards.value().push_back(storm::utility::zero<ValueType>());
171 for (
auto const& ap : atomicPropositions) {
172 if (this->model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
178 for (uint_fast64_t choice = nondeterministicChoiceIndices[representativeState]; choice < nondeterministicChoiceIndices[representativeState + 1];
181 if (choice > nondeterministicChoiceIndices[representativeState] &&
182 quotientDistributions[choice - 1].equals(quotientDistributions[choice], this->comparator)) {
186 for (
auto entry : quotientDistributions[choice]) {
187 builder.
addNextValue(currentRow, entry.first, entry.second);
189 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->model.getUniqueRewardModel().hasStateActionRewards()) {
190 stateActionRewards.value().push_back(quotientDistributions[choice].getReward());
197 for (
auto const& ap : atomicPropositions) {
198 if (this->model.getStateLabeling().getStateHasLabel(ap, representativeState)) {
206 if (this->options.getKeepRewards() && this->model.hasRewardModel() && this->model.getUniqueRewardModel().hasStateRewards()) {
207 stateRewards.value()[blockIndex] = this->model.getUniqueRewardModel().getStateRewardVector()[representativeState];
212 for (
auto initialState : this->model.getInitialStates()) {
218 std::unordered_map<std::string, typename ModelType::RewardModelType> rewardModels;
219 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
220 STORM_LOG_THROW(this->model.hasUniqueRewardModel(), storm::exceptions::IllegalFunctionCallException,
"Cannot preserve more than one reward model.");
221 typename std::unordered_map<std::string, typename ModelType::RewardModelType>::const_iterator nameRewardModelPair =
222 this->model.getRewardModels().begin();
223 rewardModels.insert(std::make_pair(nameRewardModelPair->first,
typename ModelType::RewardModelType(stateRewards, stateActionRewards)));
227 this->quotient = std::make_shared<ModelType>(builder.
build(0, this->size(), this->size()), std::move(newLabeling), std::move(rewardModels));
230template<
typename ModelType>
235template<
typename ModelType>
236void NondeterministicModelBisimulationDecomposition<ModelType>::updateQuotientDistributionsOfPredecessors(Block<BlockDataType>
const& newBlock,
237 Block<BlockDataType>
const& oldBlock,
238 std::vector<Block<BlockDataType>*>& splitterQueue) {
239 uint_fast64_t lastState = 0;
240 bool lastStateInitialized =
false;
242 for (
auto stateIt = this->partition.begin(newBlock), stateIte = this->partition.end(newBlock); stateIt != stateIte; ++stateIt) {
243 for (
auto predecessorEntry : this->backwardTransitions.getRow(*stateIt)) {
244 if (this->comparator.isZero(predecessorEntry.getValue())) {
250 Block<BlockDataType>& predecessorBlock = this->partition.getBlock(predecessorState);
253 if (predecessorBlock.data().absorbing()) {
258 if (!predecessorBlock.data().splitter()) {
259 predecessorBlock.data().setSplitter();
260 splitterQueue.push_back(&predecessorBlock);
263 if (lastStateInitialized) {
266 if (lastState != predecessorState) {
267 updateOrderedQuotientDistributions(lastState);
268 lastState = predecessorState;
271 lastStateInitialized =
true;
272 lastState = choiceToStateMapping[predecessorChoice];
276 this->quotientDistributions[predecessorChoice].shiftProbability(oldBlock.getId(), newBlock.getId(), predecessorEntry.getValue());
280 if (lastStateInitialized) {
281 updateOrderedQuotientDistributions(lastState);
285template<
typename ModelType>
286bool NondeterministicModelBisimulationDecomposition<ModelType>::checkQuotientDistributions()
const {
287 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
288 for (
decltype(this->model.getNumberOfStates()) state = 0; state < this->model.getNumberOfStates(); ++state) {
289 for (
auto choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
291 if (this->options.getKeepRewards() && this->model.hasRewardModel()) {
292 auto const& rewardModel = this->model.getUniqueRewardModel();
293 if (rewardModel.hasStateActionRewards()) {
294 distribution.
setReward(rewardModel.getStateActionReward(choice));
297 for (
auto const& element : this->model.getTransitionMatrix().getRow(choice)) {
298 distribution.
addProbability(this->partition.getBlock(element.getColumn()).getId(), element.getValue());
301 if (!distribution.
equals(quotientDistributions[choice])) {
302 std::cout <<
"the distributions for choice " << choice <<
" of state " << state <<
" do not match.\n";
303 std::cout <<
"is: " << quotientDistributions[choice] <<
" but should be " << distribution <<
'\n';
307 bool less1 = distribution.
less(quotientDistributions[choice], this->comparator);
308 bool less2 = quotientDistributions[choice].less(distribution, this->comparator);
310 if (distribution.
equals(quotientDistributions[choice]) && (less1 || less2)) {
311 std::cout <<
"mismatch of equality and less for \n";
312 std::cout << quotientDistributions[choice] <<
" vs " << distribution <<
'\n';
317 for (
auto choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1] - 1; ++choice) {
318 if (orderedQuotientDistributions[choice + 1]->less(*orderedQuotientDistributions[choice], this->comparator)) {
319 std::cout <<
"choice " << (choice + 1) <<
" is less than predecessor\n";
320 std::cout << *orderedQuotientDistributions[choice] <<
" should be less than " << *orderedQuotientDistributions[choice + 1] <<
'\n';
328template<
typename ModelType>
329bool NondeterministicModelBisimulationDecomposition<ModelType>::printDistributions(uint_fast64_t state)
const {
330 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
331 for (
auto choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
332 std::cout << quotientDistributions[choice] <<
'\n';
337template<
typename ModelType>
338bool NondeterministicModelBisimulationDecomposition<ModelType>::checkBlockStable(bisimulation::Block<BlockDataType>
const& newBlock)
const {
339 std::cout <<
"checking stability of new block " << newBlock.getId() <<
" of size " << newBlock.getNumberOfStates() <<
'\n';
340 for (
auto stateIt1 = this->partition.begin(newBlock), stateIte1 = this->partition.end(newBlock); stateIt1 != stateIte1; ++stateIt1) {
341 for (
auto stateIt2 = this->partition.begin(newBlock), stateIte2 = this->partition.end(newBlock); stateIt2 != stateIte2; ++stateIt2) {
342 bool less1 = quotientDistributionsLess(*stateIt1, *stateIt2);
343 bool less2 = quotientDistributionsLess(*stateIt2, *stateIt1);
344 if (less1 || less2) {
345 std::cout <<
"the partition is not stable for the states " << *stateIt1 <<
" and " << *stateIt2 <<
'\n';
346 std::cout <<
"less1 " << less1 <<
" and less2 " << less2 <<
'\n';
348 std::cout <<
"distributions of state " << *stateIt1 <<
'\n';
349 this->printDistributions(*stateIt1);
350 std::cout <<
"distributions of state " << *stateIt2 <<
'\n';
351 this->printDistributions(*stateIt2);
359template<
typename ModelType>
360bool NondeterministicModelBisimulationDecomposition<ModelType>::checkDistributionsDifferent(bisimulation::Block<BlockDataType>
const& block,
362 for (
auto stateIt1 = this->partition.begin(block), stateIte1 = this->partition.end(block); stateIt1 != stateIte1; ++stateIt1) {
363 for (
auto stateIt2 = this->partition.begin() + block.getEndIndex(), stateIte2 = this->partition.begin() + end; stateIt2 != stateIte2; ++stateIt2) {
364 if (!quotientDistributionsLess(*stateIt1, *stateIt2)) {
365 std::cout <<
"distributions are not less, even though they should be!\n";
368 std::cout <<
"less:\n";
369 this->printDistributions(*stateIt1);
370 std::cout <<
"and\n";
371 this->printDistributions(*stateIt2);
378template<
typename ModelType>
379bool NondeterministicModelBisimulationDecomposition<ModelType>::splitBlockAccordingToCurrentQuotientDistributions(
380 Block<BlockDataType>& block, std::vector<Block<BlockDataType>*>& splitterQueue) {
381 std::list<Block<BlockDataType>*> newBlocks;
382 bool split = this->partition.splitBlock(
385 bool result = quotientDistributionsLess(state1, state2);
388 [&newBlocks](Block<BlockDataType>& newBlock) { newBlocks.push_back(&newBlock); });
392 for (
auto el : newBlocks) {
393 this->updateQuotientDistributionsOfPredecessors(*el, block, splitterQueue);
399template<
typename ModelType>
402 STORM_LOG_TRACE(
"Comparing the quotient distributions of state " << state1 <<
" and " << state2 <<
".");
403 std::vector<uint_fast64_t> nondeterministicChoiceIndices = this->model.getTransitionMatrix().getRowGroupIndices();
405 auto firstIt = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state1];
406 auto firstIte = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state1 + 1];
407 auto secondIt = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state2];
408 auto secondIte = orderedQuotientDistributions.begin() + nondeterministicChoiceIndices[state2 + 1];
410 for (; firstIt != firstIte && secondIt != secondIte; ++firstIt, ++secondIt) {
412 if ((*firstIt)->less(**secondIt, this->comparator)) {
414 }
else if ((*secondIt)->less(**firstIt, this->comparator)) {
420 while (firstIt != firstIte && std::next(firstIt) != firstIte && !(*firstIt)->less(**std::next(firstIt), this->comparator)) {
423 while (secondIt != secondIte && std::next(secondIt) != secondIte && !(*secondIt)->less(**std::next(secondIt), this->comparator)) {
428 if (firstIt == firstIte && secondIt != secondIte) {
434template<
typename ModelType>
437 if (!possiblyNeedsRefinement(splitter)) {
443 splitBlockAccordingToCurrentQuotientDistributions(splitter, splitterQueue);
448#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.
void addProbability(StateType const &state, ValueType const &probability)
Assigns the given state the given probability under this distribution.
bool equals(DistributionWithReward< ValueType, StateType > const &other, storm::utility::ConstantsComparator< ValueType > const &comparator=storm::utility::ConstantsComparator< ValueType >()) const
Checks whether the two distributions specify the same probabilities to go to the same states.
bool less(DistributionWithReward< ValueType, StateType > const &other, storm::utility::ConstantsComparator< ValueType > const &comparator) const
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)