Browse Source

Merge master into parametricSystems.

Former-commit-id: 9d445c58e2
main
dehnert 10 years ago
parent
commit
19bc5eb995
  1. 15
      src/exceptions/InvalidOptionException.h
  2. 8
      src/logic/BinaryPathFormula.cpp
  3. 2
      src/logic/BinaryPathFormula.h
  4. 8
      src/logic/BinaryStateFormula.cpp
  5. 2
      src/logic/BinaryStateFormula.h
  6. 4
      src/logic/BoundedUntilFormula.cpp
  7. 2
      src/logic/BoundedUntilFormula.h
  8. 8
      src/logic/Formula.cpp
  9. 2
      src/logic/Formula.h
  10. 4
      src/logic/NextFormula.cpp
  11. 2
      src/logic/NextFormula.h
  12. 8
      src/logic/UnaryPathFormula.cpp
  13. 2
      src/logic/UnaryPathFormula.h
  14. 8
      src/logic/UnaryStateFormula.cpp
  15. 2
      src/logic/UnaryStateFormula.h
  16. 5
      src/modelchecker/propositional/SparsePropositionalModelChecker.cpp
  17. 1
      src/modelchecker/propositional/SparsePropositionalModelChecker.h
  18. 14
      src/models/AbstractModel.h
  19. 270
      src/storage/DeterministicModelBisimulationDecomposition.cpp
  20. 118
      src/storage/DeterministicModelBisimulationDecomposition.h
  21. 47
      src/stormParametric.cpp
  22. 9
      src/utility/cli.h
  23. 65
      test/functional/storage/DeterministicModelBisimulationDecompositionTest.cpp

15
src/exceptions/InvalidOptionException.h

@ -0,0 +1,15 @@
#ifndef STORM_EXCEPTIONS_INVALIDOPTIONEXCEPTION_H_
#define STORM_EXCEPTIONS_INVALIDOPTIONEXCEPTION_H_
#include "src/exceptions/BaseException.h"
#include "src/exceptions/ExceptionMacros.h"
namespace storm {
namespace exceptions {
STORM_NEW_EXCEPTION(InvalidOptionException)
} // namespace exceptions
} // namespace storm
#endif /* STORM_EXCEPTIONS_INVALIDOPTIONEXCEPTION_H_ */

8
src/logic/BinaryPathFormula.cpp

@ -18,6 +18,14 @@ namespace storm {
return this->getLeftSubformula().isLtlFormula() && this->getRightSubformula().isLtlFormula(); return this->getLeftSubformula().isLtlFormula() && this->getRightSubformula().isLtlFormula();
} }
bool BinaryPathFormula::containsBoundedUntilFormula() const {
return this->getLeftSubformula().containsBoundedUntilFormula() || this->getRightSubformula().containsBoundedUntilFormula();
}
bool BinaryPathFormula::containsNextFormula() const {
return this->getLeftSubformula().containsNextFormula() || this->getRightSubformula().containsNextFormula();
}
bool BinaryPathFormula::containsProbabilityOperator() const { bool BinaryPathFormula::containsProbabilityOperator() const {
return this->getLeftSubformula().containsProbabilityOperator() || this->getRightSubformula().containsProbabilityOperator(); return this->getLeftSubformula().containsProbabilityOperator() || this->getRightSubformula().containsProbabilityOperator();
} }

2
src/logic/BinaryPathFormula.h

@ -19,6 +19,8 @@ namespace storm {
virtual bool isPctlPathFormula() const override; virtual bool isPctlPathFormula() const override;
virtual bool isLtlFormula() const override; virtual bool isLtlFormula() const override;
virtual bool containsBoundedUntilFormula() const override;
virtual bool containsNextFormula() const override;
virtual bool containsProbabilityOperator() const override; virtual bool containsProbabilityOperator() const override;
virtual bool containsNestedProbabilityOperators() const override; virtual bool containsNestedProbabilityOperators() const override;
virtual bool containsRewardOperator() const override; virtual bool containsRewardOperator() const override;

8
src/logic/BinaryStateFormula.cpp

@ -18,6 +18,14 @@ namespace storm {
return this->getLeftSubformula().isLtlFormula() && this->getRightSubformula().isLtlFormula(); return this->getLeftSubformula().isLtlFormula() && this->getRightSubformula().isLtlFormula();
} }
bool BinaryStateFormula::containsBoundedUntilFormula() const {
return this->getLeftSubformula().containsBoundedUntilFormula() || this->getRightSubformula().containsBoundedUntilFormula();
}
bool BinaryStateFormula::containsNextFormula() const {
return this->getLeftSubformula().containsNextFormula() || this->getRightSubformula().containsNextFormula();
}
bool BinaryStateFormula::containsProbabilityOperator() const { bool BinaryStateFormula::containsProbabilityOperator() const {
return this->getLeftSubformula().containsProbabilityOperator() || this->getRightSubformula().containsProbabilityOperator(); return this->getLeftSubformula().containsProbabilityOperator() || this->getRightSubformula().containsProbabilityOperator();
} }

2
src/logic/BinaryStateFormula.h

@ -17,6 +17,8 @@ namespace storm {
virtual bool isPctlStateFormula() const override; virtual bool isPctlStateFormula() const override;
virtual bool isLtlFormula() const override; virtual bool isLtlFormula() const override;
virtual bool containsBoundedUntilFormula() const override;
virtual bool containsNextFormula() const override;
virtual bool containsProbabilityOperator() const override; virtual bool containsProbabilityOperator() const override;
virtual bool containsNestedProbabilityOperators() const override; virtual bool containsNestedProbabilityOperators() const override;
virtual bool containsRewardOperator() const override; virtual bool containsRewardOperator() const override;

4
src/logic/BoundedUntilFormula.cpp

@ -18,6 +18,10 @@ namespace storm {
return true; return true;
} }
bool BoundedUntilFormula::containsBoundedUntilFormula() const {
return true;
}
bool BoundedUntilFormula::isIntervalBounded() const { bool BoundedUntilFormula::isIntervalBounded() const {
return bounds.which() == 1; return bounds.which() == 1;
} }

2
src/logic/BoundedUntilFormula.h

@ -14,6 +14,8 @@ namespace storm {
virtual bool isBoundedUntilFormula() const override; virtual bool isBoundedUntilFormula() const override;
virtual bool containsBoundedUntilFormula() const override;
bool isIntervalBounded() const; bool isIntervalBounded() const;
bool isIntegerUpperBounded() const; bool isIntegerUpperBounded() const;

8
src/logic/Formula.cpp

@ -138,6 +138,14 @@ namespace storm {
return false; return false;
} }
bool Formula::containsBoundedUntilFormula() const {
return false;
}
bool Formula::containsNextFormula() const {
return false;
}
bool Formula::containsProbabilityOperator() const { bool Formula::containsProbabilityOperator() const {
return false; return false;
} }

2
src/logic/Formula.h

@ -83,6 +83,8 @@ namespace storm {
virtual bool isPltlFormula() const; virtual bool isPltlFormula() const;
virtual bool isLtlFormula() const; virtual bool isLtlFormula() const;
virtual bool isPropositionalFormula() const; virtual bool isPropositionalFormula() const;
virtual bool containsBoundedUntilFormula() const;
virtual bool containsNextFormula() const;
virtual bool containsProbabilityOperator() const; virtual bool containsProbabilityOperator() const;
virtual bool containsNestedProbabilityOperators() const; virtual bool containsNestedProbabilityOperators() const;
virtual bool containsRewardOperator() const; virtual bool containsRewardOperator() const;

4
src/logic/NextFormula.cpp

@ -10,6 +10,10 @@ namespace storm {
return true; return true;
} }
bool NextFormula::containsNextFormula() const {
return true;
}
std::ostream& NextFormula::writeToStream(std::ostream& out) const { std::ostream& NextFormula::writeToStream(std::ostream& out) const {
out << "X "; out << "X ";
this->getSubformula().writeToStream(out); this->getSubformula().writeToStream(out);

2
src/logic/NextFormula.h

@ -15,6 +15,8 @@ namespace storm {
virtual bool isNextFormula() const override; virtual bool isNextFormula() const override;
virtual bool containsNextFormula() const override;
virtual std::ostream& writeToStream(std::ostream& out) const override; virtual std::ostream& writeToStream(std::ostream& out) const override;
}; };
} }

8
src/logic/UnaryPathFormula.cpp

@ -18,6 +18,14 @@ namespace storm {
return this->getSubformula().isLtlFormula(); return this->getSubformula().isLtlFormula();
} }
bool UnaryPathFormula::containsBoundedUntilFormula() const {
return this->getSubformula().containsBoundedUntilFormula();
}
bool UnaryPathFormula::containsNextFormula() const {
return this->getSubformula().containsNextFormula();
}
bool UnaryPathFormula::containsProbabilityOperator() const { bool UnaryPathFormula::containsProbabilityOperator() const {
return this->getSubformula().containsProbabilityOperator(); return this->getSubformula().containsProbabilityOperator();
} }

2
src/logic/UnaryPathFormula.h

@ -19,6 +19,8 @@ namespace storm {
virtual bool isPctlPathFormula() const override; virtual bool isPctlPathFormula() const override;
virtual bool isLtlFormula() const override; virtual bool isLtlFormula() const override;
virtual bool containsBoundedUntilFormula() const override;
virtual bool containsNextFormula() const override;
virtual bool containsProbabilityOperator() const override; virtual bool containsProbabilityOperator() const override;
virtual bool containsNestedProbabilityOperators() const override; virtual bool containsNestedProbabilityOperators() const override;
virtual bool containsRewardOperator() const override; virtual bool containsRewardOperator() const override;

8
src/logic/UnaryStateFormula.cpp

@ -22,6 +22,14 @@ namespace storm {
return this->getSubformula().isLtlFormula(); return this->getSubformula().isLtlFormula();
} }
bool UnaryStateFormula::containsBoundedUntilFormula() const {
return this->getSubformula().containsBoundedUntilFormula();
}
bool UnaryStateFormula::containsNextFormula() const {
return this->getSubformula().containsNextFormula();
}
bool UnaryStateFormula::containsProbabilityOperator() const { bool UnaryStateFormula::containsProbabilityOperator() const {
return getSubformula().containsProbabilityOperator(); return getSubformula().containsProbabilityOperator();
} }

2
src/logic/UnaryStateFormula.h

@ -18,6 +18,8 @@ namespace storm {
virtual bool isPropositionalFormula() const override; virtual bool isPropositionalFormula() const override;
virtual bool isPctlStateFormula() const override; virtual bool isPctlStateFormula() const override;
virtual bool isLtlFormula() const override; virtual bool isLtlFormula() const override;
virtual bool containsBoundedUntilFormula() const override;
virtual bool containsNextFormula() const override;
virtual bool containsProbabilityOperator() const override; virtual bool containsProbabilityOperator() const override;
virtual bool containsNestedProbabilityOperators() const override; virtual bool containsNestedProbabilityOperators() const override;
virtual bool containsRewardOperator() const override; virtual bool containsRewardOperator() const override;

5
src/modelchecker/propositional/SparsePropositionalModelChecker.cpp

@ -18,6 +18,11 @@ namespace storm {
// Intentionally left empty. // Intentionally left empty.
} }
template<typename ValueType>
bool SparsePropositionalModelChecker<ValueType>::canHandle(storm::logic::Formula const& formula) const {
return formula.isPropositionalFormula();
}
template<typename ValueType> template<typename ValueType>
std::unique_ptr<CheckResult> SparsePropositionalModelChecker<ValueType>::checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) { std::unique_ptr<CheckResult> SparsePropositionalModelChecker<ValueType>::checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) {
if (stateFormula.isTrueFormula()) { if (stateFormula.isTrueFormula()) {

1
src/modelchecker/propositional/SparsePropositionalModelChecker.h

@ -14,6 +14,7 @@ namespace storm {
explicit SparsePropositionalModelChecker(storm::models::AbstractModel<ValueType> const& model); explicit SparsePropositionalModelChecker(storm::models::AbstractModel<ValueType> const& model);
// The implemented methods of the AbstractModelChecker interface. // The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(storm::logic::Formula const& formula) const override;
virtual std::unique_ptr<CheckResult> checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) override; virtual std::unique_ptr<CheckResult> checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) override;
virtual std::unique_ptr<CheckResult> checkAtomicLabelFormula(storm::logic::AtomicLabelFormula const& stateFormula) override; virtual std::unique_ptr<CheckResult> checkAtomicLabelFormula(storm::logic::AtomicLabelFormula const& stateFormula) override;

14
src/models/AbstractModel.h

@ -12,7 +12,11 @@
#include "src/storage/Scheduler.h" #include "src/storage/Scheduler.h"
#include "src/storage/StronglyConnectedComponentDecomposition.h" #include "src/storage/StronglyConnectedComponentDecomposition.h"
#include "src/utility/ConstantsComparator.h" #include "src/utility/ConstantsComparator.h"
#include "src/utility/macros.h"
#include "src/utility/Hash.h" #include "src/utility/Hash.h"
#include "src/utility/vector.h"
#include "src/exceptions/InvalidOperationException.h"
namespace storm { namespace storm {
namespace models { namespace models {
@ -345,6 +349,16 @@ class AbstractModel: public std::enable_shared_from_this<AbstractModel<T>> {
return static_cast<bool>(choiceLabeling); return static_cast<bool>(choiceLabeling);
} }
void convertTransitionRewardsToStateRewards() {
STORM_LOG_THROW(this->hasTransitionRewards(), storm::exceptions::InvalidOperationException, "Cannot reduce non-existant transition rewards to state rewards.");
if (this->hasStateRewards()) {
storm::utility::vector::addVectorsInPlace(stateRewardVector.get(), transitionMatrix.getPointwiseProductRowSumVector(transitionRewardMatrix.get()));
} else {
this->stateRewardVector = transitionMatrix.getPointwiseProductRowSumVector(transitionRewardMatrix.get());
}
this->transitionRewardMatrix = boost::optional<storm::storage::SparseMatrix<T>>();
}
/*! /*!
* Retrieves the size of the internal representation of the model in memory. * Retrieves the size of the internal representation of the model in memory.
* @return the size of the internal representation of the model in memory * @return the size of the internal representation of the model in memory

270
src/storage/DeterministicModelBisimulationDecomposition.cpp

@ -7,14 +7,18 @@
#include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/transform_iterator.hpp>
#include "src/storage/parameters.h" #include "src/storage/parameters.h"
#include "src/modelchecker/propositional/SparsePropositionalModelChecker.h"
#include "src/modelchecker/results/ExplicitQualitativeCheckResult.h"
#include "src/utility/graph.h" #include "src/utility/graph.h"
#include "src/exceptions/IllegalFunctionCallException.h" #include "src/exceptions/IllegalFunctionCallException.h"
#include "src/exceptions/InvalidOptionException.h"
namespace storm { namespace storm {
namespace storage { namespace storage {
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::Block::Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next, std::size_t id, std::shared_ptr<std::string> const& label) : next(next), prev(prev), begin(begin), end(end), markedAsSplitter(false), markedAsPredecessorBlock(false), markedPosition(begin), absorbing(false), id(id), label(label) {
DeterministicModelBisimulationDecomposition<ValueType>::Block::Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next, std::size_t id) : next(next), prev(prev), begin(begin), end(end), markedAsSplitter(false), markedAsPredecessorBlock(false), markedPosition(begin), absorbing(false), id(id) {
if (next != nullptr) { if (next != nullptr) {
next->prev = this; next->prev = this;
} }
@ -131,12 +135,12 @@ namespace storm {
typename DeterministicModelBisimulationDecomposition<ValueType>::Block* DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlockPointer() { typename DeterministicModelBisimulationDecomposition<ValueType>::Block* DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlockPointer() {
return this->prev; return this->prev;
} }
template<typename ValueType> template<typename ValueType>
typename DeterministicModelBisimulationDecomposition<ValueType>::Block* DeterministicModelBisimulationDecomposition<ValueType>::Block::getNextBlockPointer() { typename DeterministicModelBisimulationDecomposition<ValueType>::Block* DeterministicModelBisimulationDecomposition<ValueType>::Block::getNextBlockPointer() {
return this->next; return this->next;
} }
template<typename ValueType> template<typename ValueType>
typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlock() const { typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlock() const {
return *this->prev; return *this->prev;
@ -170,12 +174,12 @@ namespace storm {
void DeterministicModelBisimulationDecomposition<ValueType>::Block::markAsSplitter() { void DeterministicModelBisimulationDecomposition<ValueType>::Block::markAsSplitter() {
this->markedAsSplitter = true; this->markedAsSplitter = true;
} }
template<typename ValueType> template<typename ValueType>
void DeterministicModelBisimulationDecomposition<ValueType>::Block::unmarkAsSplitter() { void DeterministicModelBisimulationDecomposition<ValueType>::Block::unmarkAsSplitter() {
this->markedAsSplitter = false; this->markedAsSplitter = false;
} }
template<typename ValueType> template<typename ValueType>
std::size_t DeterministicModelBisimulationDecomposition<ValueType>::Block::getId() const { std::size_t DeterministicModelBisimulationDecomposition<ValueType>::Block::getId() const {
return this->id; return this->id;
@ -191,6 +195,22 @@ namespace storm {
return this->absorbing; return this->absorbing;
} }
template<typename ValueType>
void DeterministicModelBisimulationDecomposition<ValueType>::Block::setRepresentativeState(storm::storage::sparse::state_type representativeState) {
this->representativeState = representativeState;
}
template<typename ValueType>
bool DeterministicModelBisimulationDecomposition<ValueType>::Block::hasRepresentativeState() const {
return static_cast<bool>(representativeState);
}
template<typename ValueType>
storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getRepresentativeState() const {
STORM_LOG_THROW(representativeState, storm::exceptions::InvalidOperationException, "Unable to retrieve representative state for block.");
return representativeState.get();
}
template<typename ValueType> template<typename ValueType>
storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getMarkedPosition() const { storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getMarkedPosition() const {
return this->markedPosition; return this->markedPosition;
@ -226,22 +246,6 @@ namespace storm {
return markedAsPredecessorBlock; return markedAsPredecessorBlock;
} }
template<typename ValueType>
bool DeterministicModelBisimulationDecomposition<ValueType>::Block::hasLabel() const {
return this->label != nullptr;
}
template<typename ValueType>
std::string const& DeterministicModelBisimulationDecomposition<ValueType>::Block::getLabel() const {
STORM_LOG_THROW(this->label != nullptr, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve label of block that has none.");
return *this->label;
}
template<typename ValueType>
std::shared_ptr<std::string> const& DeterministicModelBisimulationDecomposition<ValueType>::Block::getLabelPtr() const {
return this->label;
}
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates, bool keepSilentProbabilities) : numberOfBlocks(0), stateToBlockMapping(numberOfStates), statesAndValues(numberOfStates), positions(numberOfStates), keepSilentProbabilities(keepSilentProbabilities), silentProbabilities() { DeterministicModelBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates, bool keepSilentProbabilities) : numberOfBlocks(0), stateToBlockMapping(numberOfStates), statesAndValues(numberOfStates), positions(numberOfStates), keepSilentProbabilities(keepSilentProbabilities), silentProbabilities() {
// Create the block and give it an iterator to itself. // Create the block and give it an iterator to itself.
@ -262,7 +266,7 @@ namespace storm {
} }
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates, storm::storage::BitVector const& prob0States, storm::storage::BitVector const& prob1States, std::string const& otherLabel, std::string const& prob1Label, bool keepSilentProbabilities) : numberOfBlocks(0), stateToBlockMapping(numberOfStates), statesAndValues(numberOfStates), positions(numberOfStates), keepSilentProbabilities(keepSilentProbabilities), silentProbabilities() {
DeterministicModelBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates, storm::storage::BitVector const& prob0States, storm::storage::BitVector const& prob1States, boost::optional<storm::storage::sparse::state_type> representativeProb1State, bool keepSilentProbabilities) : numberOfBlocks(0), stateToBlockMapping(numberOfStates), statesAndValues(numberOfStates), positions(numberOfStates), keepSilentProbabilities(keepSilentProbabilities), silentProbabilities() {
storm::storage::sparse::state_type position = 0; storm::storage::sparse::state_type position = 0;
Block* firstBlock = nullptr; Block* firstBlock = nullptr;
Block* secondBlock = nullptr; Block* secondBlock = nullptr;
@ -271,7 +275,7 @@ namespace storm {
typename std::list<Block>::iterator firstIt = blocks.emplace(this->blocks.end(), 0, prob0States.getNumberOfSetBits(), nullptr, nullptr, numberOfBlocks++); typename std::list<Block>::iterator firstIt = blocks.emplace(this->blocks.end(), 0, prob0States.getNumberOfSetBits(), nullptr, nullptr, numberOfBlocks++);
firstBlock = &(*firstIt); firstBlock = &(*firstIt);
firstBlock->setIterator(firstIt); firstBlock->setIterator(firstIt);
for (auto state : prob0States) { for (auto state : prob0States) {
statesAndValues[position] = std::make_pair(state, storm::utility::zero<ValueType>()); statesAndValues[position] = std::make_pair(state, storm::utility::zero<ValueType>());
positions[state] = position; positions[state] = position;
@ -280,9 +284,9 @@ namespace storm {
} }
firstBlock->setAbsorbing(true); firstBlock->setAbsorbing(true);
} }
if (!prob1States.empty()) { if (!prob1States.empty()) {
typename std::list<Block>::iterator secondIt = blocks.emplace(this->blocks.end(), position, position + prob1States.getNumberOfSetBits(), firstBlock, nullptr, numberOfBlocks++, std::shared_ptr<std::string>(new std::string(prob1Label)));
typename std::list<Block>::iterator secondIt = blocks.emplace(this->blocks.end(), position, position + prob1States.getNumberOfSetBits(), firstBlock, nullptr, numberOfBlocks++);
secondBlock = &(*secondIt); secondBlock = &(*secondIt);
secondBlock->setIterator(secondIt); secondBlock->setIterator(secondIt);
@ -293,11 +297,12 @@ namespace storm {
++position; ++position;
} }
secondBlock->setAbsorbing(true); secondBlock->setAbsorbing(true);
secondBlock->setRepresentativeState(representativeProb1State.get());
} }
storm::storage::BitVector otherStates = ~(prob0States | prob1States); storm::storage::BitVector otherStates = ~(prob0States | prob1States);
if (!otherStates.empty()) { if (!otherStates.empty()) {
typename std::list<Block>::iterator thirdIt = blocks.emplace(this->blocks.end(), position, numberOfStates, secondBlock, nullptr, numberOfBlocks++, otherLabel == "true" ? std::shared_ptr<std::string>(nullptr) : std::shared_ptr<std::string>(new std::string(otherLabel)));
typename std::list<Block>::iterator thirdIt = blocks.emplace(this->blocks.end(), position, numberOfStates, secondBlock, nullptr, numberOfBlocks++);
thirdBlock = &(*thirdIt); thirdBlock = &(*thirdIt);
thirdBlock->setIterator(thirdIt); thirdBlock->setIterator(thirdIt);
@ -320,7 +325,7 @@ namespace storm {
std::swap(this->statesAndValues[this->positions[state1]], this->statesAndValues[this->positions[state2]]); std::swap(this->statesAndValues[this->positions[state1]], this->statesAndValues[this->positions[state2]]);
std::swap(this->positions[state1], this->positions[state2]); std::swap(this->positions[state1], this->positions[state2]);
} }
template<typename ValueType> template<typename ValueType>
void DeterministicModelBisimulationDecomposition<ValueType>::Partition::swapStatesAtPositions(storm::storage::sparse::state_type position1, storm::storage::sparse::state_type position2) { void DeterministicModelBisimulationDecomposition<ValueType>::Partition::swapStatesAtPositions(storm::storage::sparse::state_type position1, storm::storage::sparse::state_type position2) {
storm::storage::sparse::state_type state1 = this->statesAndValues[position1].first; storm::storage::sparse::state_type state1 = this->statesAndValues[position1].first;
@ -366,7 +371,7 @@ namespace storm {
void DeterministicModelBisimulationDecomposition<ValueType>::Partition::increaseValue(storm::storage::sparse::state_type state, ValueType value) { void DeterministicModelBisimulationDecomposition<ValueType>::Partition::increaseValue(storm::storage::sparse::state_type state, ValueType value) {
this->statesAndValues[this->positions[state]].second += value; this->statesAndValues[this->positions[state]].second += value;
} }
template<typename ValueType> template<typename ValueType>
void DeterministicModelBisimulationDecomposition<ValueType>::Partition::updateBlockMapping(Block& block, typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator first, typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator last) { void DeterministicModelBisimulationDecomposition<ValueType>::Partition::updateBlockMapping(Block& block, typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator first, typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator last) {
for (; first != last; ++first) { for (; first != last; ++first) {
@ -383,12 +388,12 @@ namespace storm {
typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) { typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) {
return *this->stateToBlockMapping[state]; return *this->stateToBlockMapping[state];
} }
template<typename ValueType> template<typename ValueType>
typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) const { typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) const {
return *this->stateToBlockMapping[state]; return *this->stateToBlockMapping[state];
} }
template<typename ValueType> template<typename ValueType>
typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBegin(Block const& block) { typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBegin(Block const& block) {
return this->statesAndValues.begin() + block.getBegin(); return this->statesAndValues.begin() + block.getBegin();
@ -398,7 +403,7 @@ namespace storm {
typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getEnd(Block const& block) { typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getEnd(Block const& block) {
return this->statesAndValues.begin() + block.getEnd(); return this->statesAndValues.begin() + block.getEnd();
} }
template<typename ValueType> template<typename ValueType>
typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBegin(Block const& block) const { typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBegin(Block const& block) const {
return this->statesAndValues.begin() + block.getBegin(); return this->statesAndValues.begin() + block.getBegin();
@ -418,7 +423,7 @@ namespace storm {
} }
// Actually create the new block and insert it at the correct position. // Actually create the new block and insert it at the correct position.
typename std::list<Block>::iterator selfIt = this->blocks.emplace(block.getIterator(), block.getBegin(), position, block.getPreviousBlockPointer(), &block, numberOfBlocks++, block.getLabelPtr());
typename std::list<Block>::iterator selfIt = this->blocks.emplace(block.getIterator(), block.getBegin(), position, block.getPreviousBlockPointer(), &block, numberOfBlocks++);
selfIt->setIterator(selfIt); selfIt->setIterator(selfIt);
Block& newBlock = *selfIt; Block& newBlock = *selfIt;
@ -590,48 +595,124 @@ namespace storm {
} }
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, boost::optional<std::set<std::string>> const& atomicPropositions, bool keepRewards, bool weak, bool buildQuotient) : comparator() {
STORM_LOG_THROW(!model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without transition rewards.");
storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
BisimulationType bisimulationType = weak ? BisimulationType::WeakDtmc : BisimulationType::Strong;
Partition initialPartition = getLabelBasedInitialPartition(model, backwardTransitions, bisimulationType, atomicPropositions, keepRewards);
partitionRefinement(model, atomicPropositions, backwardTransitions, initialPartition, bisimulationType, keepRewards, buildQuotient);
DeterministicModelBisimulationDecomposition<ValueType>::Options::Options(storm::models::AbstractModel<ValueType> const& model, storm::logic::Formula const& formula) : Options() {
if (!formula.containsRewardOperator()) {
this->keepRewards = false;
}
// As a tradeoff, we compute a strong bisimulation, because that is typically much faster. If the number of
// states is to be minimized, this should be set to weak by the calling site.
weak = false;
// We need to preserve bounded properties iff the formula contains a bounded until or a next subformula.
bounded = formula.containsBoundedUntilFormula() || formula.containsNextFormula();
std::vector<std::shared_ptr<storm::logic::AtomicExpressionFormula const>> atomicExpressionFormulas = formula.getAtomicExpressionFormulas();
std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>> atomicLabelFormulas = formula.getAtomicLabelFormulas();
std::set<std::string> labelsToRespect;
for (auto const& labelFormula : atomicLabelFormulas) {
labelsToRespect.insert(labelFormula->getLabel());
}
for (auto const& expressionFormula : atomicExpressionFormulas) {
std::stringstream stream;
stream << expressionFormula;
labelsToRespect.insert(stream.str());
}
respectedAtomicPropositions = labelsToRespect;
std::shared_ptr<storm::logic::Formula const> newFormula = formula.asSharedPointer();
if (formula.isProbabilityOperatorFormula()) {
newFormula = formula.asProbabilityOperatorFormula().getSubformula().asSharedPointer();
} else if (formula.isRewardOperatorFormula()) {
newFormula = formula.asRewardOperatorFormula().getSubformula().asSharedPointer();
}
std::shared_ptr<storm::logic::Formula const> leftSubformula = std::make_shared<storm::logic::BooleanLiteralFormula>(true);
std::shared_ptr<storm::logic::Formula const> rightSubformula;
if (newFormula->isUntilFormula()) {
leftSubformula = newFormula->asUntilFormula().getLeftSubformula().asSharedPointer();
rightSubformula = newFormula->asUntilFormula().getRightSubformula().asSharedPointer();
if (leftSubformula->isPropositionalFormula() && rightSubformula->isPropositionalFormula()) {
measureDrivenInitialPartition = true;
}
} else if (newFormula->isEventuallyFormula()) {
rightSubformula = newFormula->asEventuallyFormula().getSubformula().asSharedPointer();
if (rightSubformula->isPropositionalFormula()) {
measureDrivenInitialPartition = true;
}
} else if (newFormula->isReachabilityRewardFormula()) {
rightSubformula = newFormula->asEventuallyFormula().getSubformula().asSharedPointer();
if (rightSubformula->isPropositionalFormula()) {
measureDrivenInitialPartition = true;
}
}
if (measureDrivenInitialPartition) {
storm::modelchecker::SparsePropositionalModelChecker<ValueType> checker(model);
std::unique_ptr<storm::modelchecker::CheckResult> phiStatesCheckResult = checker.check(*leftSubformula);
std::unique_ptr<storm::modelchecker::CheckResult> psiStatesCheckResult = checker.check(*rightSubformula);
phiStates = phiStatesCheckResult->asExplicitQualitativeCheckResult().getTruthValuesVector();
psiStates = psiStatesCheckResult->asExplicitQualitativeCheckResult().getTruthValuesVector();
}
} }
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, boost::optional<std::set<std::string>> const& atomicPropositions, bool keepRewards, bool weak, bool buildQuotient) {
STORM_LOG_THROW(!keepRewards || !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without transition rewards.");
storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
BisimulationType bisimulationType = weak ? BisimulationType::WeakCtmc : BisimulationType::Strong;
Partition initialPartition = getLabelBasedInitialPartition(model, backwardTransitions, bisimulationType, atomicPropositions, keepRewards);
partitionRefinement(model, atomicPropositions, backwardTransitions, initialPartition, bisimulationType, keepRewards,buildQuotient);
DeterministicModelBisimulationDecomposition<ValueType>::Options::Options() : measureDrivenInitialPartition(false), phiStates(), psiStates(), respectedAtomicPropositions(), keepRewards(true), weak(false), bounded(true), buildQuotient(true) {
// Intentionally left empty.
} }
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool keepRewards, bool weak, bool bounded, bool buildQuotient) {
STORM_LOG_THROW(!keepRewards || !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without transition rewards.");
STORM_LOG_THROW(!weak || !bounded, storm::exceptions::IllegalFunctionCallException, "Weak bisimulation does not preserve bounded properties.");
DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, Options const& options) {
STORM_LOG_THROW(!model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without transition rewards. Consider converting the transition rewards to state rewards (via suitable function calls).");
STORM_LOG_THROW(!options.weak || !options.bounded, storm::exceptions::IllegalFunctionCallException, "Weak bisimulation cannot preserve bounded properties.");
storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions(); storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
BisimulationType bisimulationType = weak ? BisimulationType::WeakDtmc : BisimulationType::Strong;
Partition initialPartition = getMeasureDrivenInitialPartition(model, backwardTransitions, phiLabel, psiLabel, bisimulationType, keepRewards, bounded);
BisimulationType bisimulationType = options.weak ? BisimulationType::WeakDtmc : BisimulationType::Strong;
std::set<std::string> atomicPropositions; std::set<std::string> atomicPropositions;
if (phiLabel != "true" && phiLabel != "false") {
atomicPropositions.insert(phiLabel);
if (options.respectedAtomicPropositions) {
atomicPropositions = options.respectedAtomicPropositions.get();
} else {
atomicPropositions = model.getStateLabeling().getAtomicPropositions();
} }
if (psiLabel != "true" && phiLabel != "false") {
atomicPropositions.insert(psiLabel);
Partition initialPartition;
if (options.measureDrivenInitialPartition) {
STORM_LOG_THROW(options.phiStates, storm::exceptions::InvalidOptionException, "Unable to compute measure-driven initial partition without phi and psi states.");
STORM_LOG_THROW(options.psiStates, storm::exceptions::InvalidOptionException, "Unable to compute measure-driven initial partition without phi and psi states.");
initialPartition = getMeasureDrivenInitialPartition(model, backwardTransitions, options.phiStates.get(), options.psiStates.get(), bisimulationType, options.keepRewards, options.bounded);
} else {
initialPartition = getLabelBasedInitialPartition(model, backwardTransitions, bisimulationType, atomicPropositions, options.keepRewards);
} }
partitionRefinement(model, std::set<std::string>({phiLabel, psiLabel}), model.getBackwardTransitions(), initialPartition, bisimulationType, keepRewards, buildQuotient);
partitionRefinement(model, atomicPropositions, backwardTransitions, initialPartition, bisimulationType, options.keepRewards, options.buildQuotient);
} }
template<typename ValueType> template<typename ValueType>
DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool keepRewards, bool weak, bool bounded, bool buildQuotient) {
STORM_LOG_THROW(!keepRewards || !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without transition rewards.");
STORM_LOG_THROW(!weak || !bounded, storm::exceptions::IllegalFunctionCallException, "Weak bisimulation does not preserve bounded properties.");
DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, Options const& options) {
STORM_LOG_THROW(!model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without transition rewards. Consider converting the transition rewards to state rewards (via suitable function calls).");
STORM_LOG_THROW(!options.weak || !options.bounded, storm::exceptions::IllegalFunctionCallException, "Weak bisimulation cannot preserve bounded properties.");
storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions(); storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
BisimulationType bisimulationType = weak ? BisimulationType::WeakCtmc : BisimulationType::Strong;
Partition initialPartition = getMeasureDrivenInitialPartition(model, backwardTransitions, phiLabel, psiLabel, bisimulationType, keepRewards, bounded);
partitionRefinement(model, std::set<std::string>({phiLabel, psiLabel}), model.getBackwardTransitions(), initialPartition, bisimulationType, keepRewards, buildQuotient);
BisimulationType bisimulationType = options.weak ? BisimulationType::WeakCtmc : BisimulationType::Strong;
std::set<std::string> atomicPropositions;
if (options.respectedAtomicPropositions) {
atomicPropositions = options.respectedAtomicPropositions.get();
} else {
atomicPropositions = model.getStateLabeling().getAtomicPropositions();
}
Partition initialPartition;
if (options.measureDrivenInitialPartition) {
STORM_LOG_THROW(options.phiStates, storm::exceptions::InvalidOptionException, "Unable to compute measure-driven initial partition without phi and psi states.");
STORM_LOG_THROW(options.psiStates, storm::exceptions::InvalidOptionException, "Unable to compute measure-driven initial partition without phi and psi states.");
initialPartition = getMeasureDrivenInitialPartition(model, backwardTransitions, options.phiStates.get(), options.psiStates.get(), bisimulationType, options.keepRewards, options.bounded);
} else {
initialPartition = getLabelBasedInitialPartition(model, backwardTransitions, bisimulationType, atomicPropositions, options.keepRewards);
}
partitionRefinement(model, atomicPropositions, backwardTransitions, initialPartition, bisimulationType, options.keepRewards, options.buildQuotient);
} }
template<typename ValueType> template<typename ValueType>
@ -691,15 +772,16 @@ namespace storm {
if (oldBlock.isAbsorbing()) { if (oldBlock.isAbsorbing()) {
builder.addNextValue(blockIndex, blockIndex, storm::utility::constantOne<ValueType>()); builder.addNextValue(blockIndex, blockIndex, storm::utility::constantOne<ValueType>());
if (oldBlock.hasLabel()) {
newLabeling.addAtomicPropositionToState(oldBlock.getLabel(), blockIndex);
} else {
// Otherwise add all atomic propositions to the equivalence class that the representative state
// satisfies.
for (auto const& ap : atomicPropositions) {
if (model.getStateLabeling().getStateHasAtomicProposition(ap, representativeState)) {
newLabeling.addAtomicPropositionToState(ap, blockIndex);
}
// If the block has a special representative state, we retrieve it now.
if (oldBlock.hasRepresentativeState()) {
representativeState = oldBlock.getRepresentativeState();
}
// Add all of the selected atomic propositions that hold in the representative state to the state
// representing the block.
for (auto const& ap : atomicPropositions) {
if (model.getStateLabeling().getStateHasAtomicProposition(ap, representativeState)) {
newLabeling.addAtomicPropositionToState(ap, blockIndex);
} }
} }
} else { } else {
@ -730,16 +812,11 @@ namespace storm {
} }
} }
// If the block has a special label, we only add that to the block.
if (oldBlock.hasLabel()) {
newLabeling.addAtomicPropositionToState(oldBlock.getLabel(), blockIndex);
} else {
// Otherwise add all atomic propositions to the equivalence class that the representative state
// satisfies.
for (auto const& ap : atomicPropositions) {
if (model.getStateLabeling().getStateHasAtomicProposition(ap, representativeState)) {
newLabeling.addAtomicPropositionToState(ap, blockIndex);
}
// Otherwise add all atomic propositions to the equivalence class that the representative state
// satisfies.
for (auto const& ap : atomicPropositions) {
if (model.getStateLabeling().getStateHasAtomicProposition(ap, representativeState)) {
newLabeling.addAtomicPropositionToState(ap, blockIndex);
} }
} }
} }
@ -765,7 +842,7 @@ namespace storm {
template<typename ModelType> template<typename ModelType>
void DeterministicModelBisimulationDecomposition<ValueType>::partitionRefinement(ModelType const& model, boost::optional<std::set<std::string>> const& atomicPropositions, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Partition& partition, BisimulationType bisimulationType, bool keepRewards, bool buildQuotient) { void DeterministicModelBisimulationDecomposition<ValueType>::partitionRefinement(ModelType const& model, boost::optional<std::set<std::string>> const& atomicPropositions, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Partition& partition, BisimulationType bisimulationType, bool keepRewards, bool buildQuotient) {
std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now();
// Initially, all blocks are potential splitter, so we insert them in the splitterQueue. // Initially, all blocks are potential splitter, so we insert them in the splitterQueue.
std::chrono::high_resolution_clock::time_point refinementStart = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point refinementStart = std::chrono::high_resolution_clock::now();
std::deque<Block*> splitterQueue; std::deque<Block*> splitterQueue;
@ -775,6 +852,7 @@ namespace storm {
while (!splitterQueue.empty()) { while (!splitterQueue.empty()) {
// Optionally: sort the splitter queue according to some criterion (here: prefer small splitters). // Optionally: sort the splitter queue according to some criterion (here: prefer small splitters).
std::sort(splitterQueue.begin(), splitterQueue.end(), [] (Block const* b1, Block const* b2) { return b1->getNumberOfStates() < b2->getNumberOfStates() || (b1->getNumberOfStates() == b2->getNumberOfStates() && b1->getId() < b2->getId()); } ); std::sort(splitterQueue.begin(), splitterQueue.end(), [] (Block const* b1, Block const* b2) { return b1->getNumberOfStates() < b2->getNumberOfStates() || (b1->getNumberOfStates() == b2->getNumberOfStates() && b1->getId() < b2->getId()); } );
std::sort(splitterQueue.begin(), splitterQueue.end(), [] (Block const* b1, Block const* b2) { return b1->getNumberOfStates() < b2->getNumberOfStates(); } );
// Get and prepare the next splitter. // Get and prepare the next splitter.
Block* splitter = splitterQueue.front(); Block* splitter = splitterQueue.front();
@ -785,7 +863,7 @@ namespace storm {
refinePartition(model.getTransitionMatrix(), backwardTransitions, *splitter, partition, bisimulationType, splitterQueue); refinePartition(model.getTransitionMatrix(), backwardTransitions, *splitter, partition, bisimulationType, splitterQueue);
} }
std::chrono::high_resolution_clock::duration refinementTime = std::chrono::high_resolution_clock::now() - refinementStart; std::chrono::high_resolution_clock::duration refinementTime = std::chrono::high_resolution_clock::now() - refinementStart;
// Now move the states from the internal partition into their final place in the decomposition. We do so in // Now move the states from the internal partition into their final place in the decomposition. We do so in
// a way that maintains the block IDs as indices. // a way that maintains the block IDs as indices.
std::chrono::high_resolution_clock::time_point extractionStart = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point extractionStart = std::chrono::high_resolution_clock::now();
@ -798,12 +876,12 @@ namespace storm {
std::function<storm::storage::sparse::state_type (std::pair<storm::storage::sparse::state_type, ValueType> const&)> projection = [] (std::pair<storm::storage::sparse::state_type, ValueType> const& a) -> storm::storage::sparse::state_type { return a.first; }; std::function<storm::storage::sparse::state_type (std::pair<storm::storage::sparse::state_type, ValueType> const&)> projection = [] (std::pair<storm::storage::sparse::state_type, ValueType> const& a) -> storm::storage::sparse::state_type { return a.first; };
this->blocks[block.getId()] = block_type(boost::make_transform_iterator(partition.getBegin(block), projection), boost::make_transform_iterator(partition.getEnd(block), projection), true); this->blocks[block.getId()] = block_type(boost::make_transform_iterator(partition.getBegin(block), projection), boost::make_transform_iterator(partition.getEnd(block), projection), true);
} }
// If we are required to build the quotient model, do so now. // If we are required to build the quotient model, do so now.
if (buildQuotient) { if (buildQuotient) {
this->buildQuotient(model, atomicPropositions, partition, bisimulationType, keepRewards); this->buildQuotient(model, atomicPropositions, partition, bisimulationType, keepRewards);
} }
std::chrono::high_resolution_clock::duration extractionTime = std::chrono::high_resolution_clock::now() - extractionStart; std::chrono::high_resolution_clock::duration extractionTime = std::chrono::high_resolution_clock::now() - extractionStart;
std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart; std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart;
@ -1018,7 +1096,7 @@ namespace storm {
typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::const_iterator end = partition.getStatesAndValues().begin() + block.getBegin() - 1; typename std::vector<std::pair<storm::storage::sparse::state_type, ValueType>>::const_iterator end = partition.getStatesAndValues().begin() + block.getBegin() - 1;
storm::storage::sparse::state_type currentIndex = block.getOriginalBegin(); storm::storage::sparse::state_type currentIndex = block.getOriginalBegin();
result.push_back(currentIndex); result.push_back(currentIndex);
// Now we can check whether the block needs to be split, which is the case iff the probabilities for the // Now we can check whether the block needs to be split, which is the case iff the probabilities for the
// first and the last state are different. // first and the last state are different.
while (!comparator.isEqual(begin->second, end->second)) { while (!comparator.isEqual(begin->second, end->second)) {
@ -1033,7 +1111,7 @@ namespace storm {
++begin; ++begin;
++currentIndex; ++currentIndex;
} }
// Remember the index at which the probabilities were different. // Remember the index at which the probabilities were different.
result.push_back(currentIndex); result.push_back(currentIndex);
} }
@ -1126,7 +1204,7 @@ namespace storm {
storm::storage::sparse::state_type predecessor = predecessorEntry.getColumn(); storm::storage::sparse::state_type predecessor = predecessorEntry.getColumn();
Block& predecessorBlock = partition.getBlock(predecessor); Block& predecessorBlock = partition.getBlock(predecessor);
storm::storage::sparse::state_type predecessorPosition = partition.getPosition(predecessor); storm::storage::sparse::state_type predecessorPosition = partition.getPosition(predecessor);
if (predecessorPosition >= predecessorBlock.getBegin()) { if (predecessorPosition >= predecessorBlock.getBegin()) {
partition.swapStatesAtPositions(predecessorPosition, predecessorBlock.getBegin()); partition.swapStatesAtPositions(predecessorPosition, predecessorBlock.getBegin());
predecessorBlock.incrementBegin(); predecessorBlock.incrementBegin();
@ -1141,7 +1219,7 @@ namespace storm {
} }
} }
} }
if (bisimulationType == BisimulationType::Strong || bisimulationType == BisimulationType::WeakCtmc) { if (bisimulationType == BisimulationType::Strong || bisimulationType == BisimulationType::WeakCtmc) {
std::vector<Block*> blocksToSplit; std::vector<Block*> blocksToSplit;
@ -1192,7 +1270,7 @@ namespace storm {
partition.setSilentProbabilities(partition.getStatesAndValues().begin() + splitter.getOriginalBegin(), partition.getStatesAndValues().begin() + splitter.getBegin()); partition.setSilentProbabilities(partition.getStatesAndValues().begin() + splitter.getOriginalBegin(), partition.getStatesAndValues().begin() + splitter.getBegin());
partition.setSilentProbabilitiesToZero(partition.getStatesAndValues().begin() + splitter.getBegin(), partition.getStatesAndValues().begin() + splitter.getEnd()); partition.setSilentProbabilitiesToZero(partition.getStatesAndValues().begin() + splitter.getBegin(), partition.getStatesAndValues().begin() + splitter.getEnd());
} }
// Now refine all predecessor blocks in a weak manner. That is, we split according to the criterion of // Now refine all predecessor blocks in a weak manner. That is, we split according to the criterion of
// weak bisimulation. // weak bisimulation.
for (auto blockPtr : predecessorBlocks) { for (auto blockPtr : predecessorBlocks) {
@ -1205,7 +1283,7 @@ namespace storm {
// Restore the begin of the block. // Restore the begin of the block.
block.setBegin(block.getOriginalBegin()); block.setBegin(block.getOriginalBegin());
} }
block.unmarkAsPredecessorBlock(); block.unmarkAsPredecessorBlock();
block.resetMarkedPosition(); block.resetMarkedPosition();
} }
@ -1216,9 +1294,15 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
template<typename ModelType> template<typename ModelType>
typename DeterministicModelBisimulationDecomposition<ValueType>::Partition DeterministicModelBisimulationDecomposition<ValueType>::getMeasureDrivenInitialPartition(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, std::string const& phiLabel, std::string const& psiLabel, BisimulationType bisimulationType, bool keepRewards, bool bounded) {
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(backwardTransitions, phiLabel == "true" ? storm::storage::BitVector(model.getNumberOfStates(), true) : model.getLabeledStates(phiLabel), model.getLabeledStates(psiLabel));
Partition partition(model.getNumberOfStates(), statesWithProbability01.first, bounded || keepRewards ? model.getLabeledStates(psiLabel) : statesWithProbability01.second, phiLabel, psiLabel, bisimulationType == BisimulationType::WeakDtmc);
typename DeterministicModelBisimulationDecomposition<ValueType>::Partition DeterministicModelBisimulationDecomposition<ValueType>::getMeasureDrivenInitialPartition(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, BisimulationType bisimulationType, bool keepRewards, bool bounded) {
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(backwardTransitions, phiStates, psiStates);
boost::optional<storm::storage::sparse::state_type> representativePsiState;
if (!psiStates.empty()) {
representativePsiState = *psiStates.begin();
}
Partition partition(model.getNumberOfStates(), statesWithProbability01.first, bounded || keepRewards ? psiStates : statesWithProbability01.second, representativePsiState, bisimulationType == BisimulationType::WeakDtmc);
// If the model has state rewards, we need to consider them, because otherwise reward properties are not // If the model has state rewards, we need to consider them, because otherwise reward properties are not
// preserved. // preserved.

118
src/storage/DeterministicModelBisimulationDecomposition.h

@ -6,6 +6,7 @@
#include "src/storage/sparse/StateType.h" #include "src/storage/sparse/StateType.h"
#include "src/storage/Decomposition.h" #include "src/storage/Decomposition.h"
#include "src/logic/Formulas.h"
#include "src/models/Dtmc.h" #include "src/models/Dtmc.h"
#include "src/models/Ctmc.h" #include "src/models/Ctmc.h"
#include "src/storage/Distribution.h" #include "src/storage/Distribution.h"
@ -21,53 +22,64 @@ namespace storm {
template <typename ValueType> template <typename ValueType>
class DeterministicModelBisimulationDecomposition : public Decomposition<StateBlock> { class DeterministicModelBisimulationDecomposition : public Decomposition<StateBlock> {
public: public:
/*!
* Decomposes the given DTMC into equivalence classes under weak or strong bisimulation.
*
* @param model The model to decompose.
* @param A set of atomic propositions that is to be respected. If not given, all atomic propositions of the
* model will be respected. If built, the quotient model will only contain the respected atomic propositions.
* @param weak A flag indication whether a weak bisimulation is to be computed.
* @param buildQuotient Sets whether or not the quotient model is to be built.
*/
DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, boost::optional<std::set<std::string>> const& atomicPropositions = boost::optional<std::set<std::string>>(), bool keepRewards = true, bool weak = false, bool buildQuotient = false);
// A class that offers the possibility to customize the bisimulation.
struct Options {
// Creates an object representing the default values for all options.
Options();
/*!
* Decomposes the given CTMC into equivalence classes under weak or strong bisimulation.
*
* @param model The model to decompose.
* @param A set of atomic propositions that is to be respected. If not given, all atomic propositions of the
* model will be respected. If built, the quotient model will only contain the respected atomic propositions.
* @param weak A flag indication whether a weak bisimulation is to be computed.
* @param buildQuotient Sets whether or not the quotient model is to be built.
*/
DeterministicModelBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, boost::optional<std::set<std::string>> const& atomicPropositions = boost::optional<std::set<std::string>>(), bool keepRewards = true, bool weak = false, bool buildQuotient = false);
/*!
* Creates an object representing the options necessary to obtain the smallest quotient while still
* preserving the given formula.
*
* @param The model for which the quotient model shall be computed. This needs to be given in order to
* derive a suitable initial partition.
* @param formula The formula that is to be preserved.
*/
Options(storm::models::AbstractModel<ValueType> const& model, storm::logic::Formula const& formula);
// A flag that indicates whether a measure driven initial partition is to be used. If this flag is set
// to true, the two optional pairs phiStatesAndLabel and psiStatesAndLabel must be set. Then, the
// measure driven initial partition wrt. to the states phi and psi is taken.
bool measureDrivenInitialPartition;
boost::optional<storm::storage::BitVector> phiStates;
boost::optional<storm::storage::BitVector> psiStates;
// An optional set of strings that indicate which of the atomic propositions of the model are to be
// respected and which may be ignored. If not given, all atomic propositions of the model are respected.
boost::optional<std::set<std::string>> respectedAtomicPropositions;
// A flag that indicates whether or not the state-rewards of the model are to be respected (and should
// be kept in the quotient model, if one is built).
bool keepRewards;
// A flag that indicates whether a strong or a weak bisimulation is to be computed.
bool weak;
// A flag that indicates whether step-bounded properties are to be preserved. This may only be set to tru
// when computing strong bisimulation equivalence.
bool bounded;
// A flag that governs whether the quotient model is actually built or only the decomposition is computed.
bool buildQuotient;
};
/*! /*!
* Decomposes the given DTMC into equivalence classes under strong bisimulation in a way that onle safely
* preserves formulas of the form phi until psi.
* Decomposes the given DTMC into equivalance classes of a bisimulation. Which kind of bisimulation is
* computed, is customizable via the given options.
* *
* @param model The model to decompose. * @param model The model to decompose.
* @param phiLabel The label that all phi states carry in the model.
* @param psiLabel The label that all psi states carry in the model.
* @param weak A flag indication whether a weak bisimulation is to be computed.
* @param bounded If set to true, also bounded until formulas are preserved.
* @param buildQuotient Sets whether or not the quotient model is to be built.
* @param options The options that customize the computed bisimulation.
*/ */
DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool keepRewards, bool weak, bool bounded, bool buildQuotient = false);
DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, Options const& options = Options());
/*! /*!
* Decomposes the given CTMC into equivalence classes under strong bisimulation in a way that onle safely
* preserves formulas of the form phi until psi.
* Decomposes the given CTMC into equivalance classes of a bisimulation. Which kind of bisimulation is
* computed, is customizable via the given options.
* *
* @param model The model to decompose. * @param model The model to decompose.
* @param phiLabel The label that all phi states carry in the model.
* @param psiLabel The label that all psi states carry in the model.
* @param weak A flag indication whether a weak bisimulation is to be computed.
* @param bounded If set to true, also bounded until formulas are preserved.
* @param buildQuotient Sets whether or not the quotient model is to be built.
* @param options The options that customize the computed bisimulation.
*/ */
DeterministicModelBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool keepRewards, bool weak, bool bounded, bool buildQuotient = false);
DeterministicModelBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, Options const& options = Options());
/*! /*!
* Retrieves the quotient of the model under the previously computed bisimulation. * Retrieves the quotient of the model under the previously computed bisimulation.
@ -87,7 +99,7 @@ namespace storm {
typedef typename std::list<Block>::const_iterator const_iterator; typedef typename std::list<Block>::const_iterator const_iterator;
// Creates a new block with the given begin and end. // Creates a new block with the given begin and end.
Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next, std::size_t id, std::shared_ptr<std::string> const& label = nullptr);
Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next, std::size_t id);
// Prints the block. // Prints the block.
void print(Partition const& partition) const; void print(Partition const& partition) const;
@ -191,14 +203,14 @@ namespace storm {
// Retrieves whether the block is to be interpreted as absorbing. // Retrieves whether the block is to be interpreted as absorbing.
bool isAbsorbing() const; bool isAbsorbing() const;
// Retrieves whether the block has a special label.
bool hasLabel() const;
// Sets the representative state of this block
void setRepresentativeState(storm::storage::sparse::state_type representativeState);
// Retrieves the special label of the block if it has one.
std::string const& getLabel() const;
// Retrieves a pointer to the label of the block (which is the nullptr if there is none).
std::shared_ptr<std::string> const& getLabelPtr() const;
// Retrieves the representative state for this block.
bool hasRepresentativeState() const;
// Retrieves the representative state for this block.
storm::storage::sparse::state_type getRepresentativeState() const;
private: private:
// An iterator to itself. This is needed to conveniently insert elements in the overall list of blocks // An iterator to itself. This is needed to conveniently insert elements in the overall list of blocks
@ -228,8 +240,9 @@ namespace storm {
// The ID of the block. This is only used for debugging purposes. // The ID of the block. This is only used for debugging purposes.
std::size_t id; std::size_t id;
// The label of the block or nullptr if it has none.
std::shared_ptr<std::string> label;
// An optional representative state for the block. If this is set, this state is used to derive the
// atomic propositions of the meta state in the quotient model.
boost::optional<storm::storage::sparse::state_type> representativeState;
}; };
class Partition { class Partition {
@ -252,11 +265,12 @@ namespace storm {
* @param numberOfStates The number of states the partition holds. * @param numberOfStates The number of states the partition holds.
* @param prob0States The states which have probability 0 of satisfying phi until psi. * @param prob0States The states which have probability 0 of satisfying phi until psi.
* @param prob1States The states which have probability 1 of satisfying phi until psi. * @param prob1States The states which have probability 1 of satisfying phi until psi.
* @param otherLabel The label that is to be attached to all other states.
* @param prob1Label The label that is to be attached to all states with probability 1.
* @param representativeProb1State If the set of prob1States is non-empty, this needs to be a state
* that is representative for this block in the sense that the state representing this block in the quotient
* model will receive exactly the atomic propositions of the representative state.
* @param keepSilentProbabilities A flag indicating whether or not silent probabilities are to be tracked. * @param keepSilentProbabilities A flag indicating whether or not silent probabilities are to be tracked.
*/ */
Partition(std::size_t numberOfStates, storm::storage::BitVector const& prob0States, storm::storage::BitVector const& prob1States, std::string const& otherLabel, std::string const& prob1Label, bool keepSilentProbabilities = false);
Partition(std::size_t numberOfStates, storm::storage::BitVector const& prob0States, storm::storage::BitVector const& prob1States, boost::optional<storm::storage::sparse::state_type> representativeProb1State, bool keepSilentProbabilities = false);
Partition() = default; Partition() = default;
Partition(Partition const& other) = default; Partition(Partition const& other) = default;
@ -468,15 +482,15 @@ namespace storm {
* @param model The model whose state space is partitioned based on reachability of psi states from phi * @param model The model whose state space is partitioned based on reachability of psi states from phi
* states. * states.
* @param backwardTransitions The backward transitions of the model. * @param backwardTransitions The backward transitions of the model.
* @param phiLabel The label that all phi states carry in the model.
* @param psiLabel The label that all psi states carry in the model.
* @param phiStates The phi states in the model.
* @param psiStates The psi states in the model.
* @param bisimulationType The kind of bisimulation that is to be computed. * @param bisimulationType The kind of bisimulation that is to be computed.
* @param bounded If set to true, the initial partition will be chosen in such a way that preserves bounded * @param bounded If set to true, the initial partition will be chosen in such a way that preserves bounded
* reachability queries. * reachability queries.
* @return The resulting partition. * @return The resulting partition.
*/ */
template<typename ModelType> template<typename ModelType>
Partition getMeasureDrivenInitialPartition(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, std::string const& phiLabel, std::string const& psiLabel, BisimulationType bisimulationType, bool keepRewards = true, bool bounded = false);
Partition getMeasureDrivenInitialPartition(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, BisimulationType bisimulationType, bool keepRewards = true, bool bounded = false);
/*! /*!
* Creates the initial partition based on all the labels in the given model. * Creates the initial partition based on all the labels in the given model.

47
src/stormParametric.cpp

@ -169,51 +169,14 @@ void check() {
bool checkRewards = formula->isRewardOperatorFormula(); bool checkRewards = formula->isRewardOperatorFormula();
std::string phiLabel;
std::string psiLabel;
bool measureDrivenBisimulation = false;
if (formula->isProbabilityOperatorFormula()) {
storm::logic::Formula const& subformula = formula->asProbabilityOperatorFormula().getSubformula();
if (subformula.isEventuallyFormula()) {
phiLabel = "true";
std::stringstream stream;
stream << subformula.asEventuallyFormula().getSubformula();
psiLabel = stream.str();
measureDrivenBisimulation = true;
} else if (subformula.isUntilFormula()) {
std::stringstream stream;
stream << subformula.asUntilFormula().getLeftSubformula();
phiLabel = stream.str();
std::stringstream stream2;
stream2 << subformula.asUntilFormula().getRightSubformula();
psiLabel = stream2.str();
measureDrivenBisimulation = true;
}
} else if (formula->isRewardOperatorFormula()) {
storm::logic::Formula const& subformula = formula->asRewardOperatorFormula().getSubformula();
if (subformula.isReachabilityRewardFormula()) {
phiLabel = "true";
std::stringstream stream;
stream << subformula.asReachabilityRewardFormula().getSubformula();
psiLabel = stream.str();
measureDrivenBisimulation = true;
}
}
// Perform bisimulation minimization if requested. // Perform bisimulation minimization if requested.
if (storm::settings::generalSettings().isBisimulationSet()) { if (storm::settings::generalSettings().isBisimulationSet()) {
if (measureDrivenBisimulation) {
storm::storage::DeterministicModelBisimulationDecomposition<ValueType> bisimulationDecomposition(*dtmc, phiLabel, psiLabel, checkRewards, storm::settings::bisimulationSettings().isWeakBisimulationSet(), false, true);
*dtmc = std::move(*bisimulationDecomposition.getQuotient()->template as<storm::models::Dtmc<ValueType>>());
} else {
storm::storage::DeterministicModelBisimulationDecomposition<ValueType> bisimulationDecomposition(*dtmc, boost::optional<std::set<std::string>>(), checkRewards, storm::settings::bisimulationSettings().isWeakBisimulationSet(), true);
*dtmc = std::move(*bisimulationDecomposition.getQuotient()->template as<storm::models::Dtmc<ValueType>>());
}
typename storm::storage::DeterministicModelBisimulationDecomposition<ValueType>::Options options(*dtmc, *formula);
options.weak = storm::settings::bisimulationSettings().isWeakBisimulationSet();
storm::storage::DeterministicModelBisimulationDecomposition<ValueType> bisimulationDecomposition(*dtmc, options);
*dtmc = std::move(*bisimulationDecomposition.getQuotient()->template as<storm::models::Dtmc<ValueType>>());
dtmc->printModelInformationToStream(std::cout); dtmc->printModelInformationToStream(std::cout);
} }

9
src/utility/cli.h

@ -292,8 +292,15 @@ namespace storm {
STORM_LOG_THROW(model->getType() == storm::models::DTMC, storm::exceptions::InvalidSettingsException, "Bisimulation minimization is currently only available for DTMCs."); STORM_LOG_THROW(model->getType() == storm::models::DTMC, storm::exceptions::InvalidSettingsException, "Bisimulation minimization is currently only available for DTMCs.");
std::shared_ptr<storm::models::Dtmc<double>> dtmc = model->template as<storm::models::Dtmc<double>>(); std::shared_ptr<storm::models::Dtmc<double>> dtmc = model->template as<storm::models::Dtmc<double>>();
if (dtmc->hasTransitionRewards()) {
dtmc->convertTransitionRewardsToStateRewards();
}
std::cout << "Performing bisimulation minimization... "; std::cout << "Performing bisimulation minimization... ";
storm::storage::DeterministicModelBisimulationDecomposition<double> bisimulationDecomposition(*dtmc, boost::optional<std::set<std::string>>(), true, storm::settings::bisimulationSettings().isWeakBisimulationSet(), true);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options;
options.weak = storm::settings::bisimulationSettings().isWeakBisimulationSet();
storm::storage::DeterministicModelBisimulationDecomposition<double> bisimulationDecomposition(*dtmc, options);
model = bisimulationDecomposition.getQuotient(); model = bisimulationDecomposition.getQuotient();
std::cout << "done." << std::endl << std::endl; std::cout << "done." << std::endl << std::endl;
} }

65
test/functional/storage/DeterministicModelBisimulationDecompositionTest.cpp

@ -9,7 +9,7 @@ TEST(DeterministicModelBisimulationDecomposition, Die) {
ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); ASSERT_EQ(abstractModel->getType(), storm::models::DTMC);
std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim(*dtmc, boost::optional<std::set<std::string>>(), true, false, true);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim(*dtmc);
std::shared_ptr<storm::models::AbstractModel<double>> result; std::shared_ptr<storm::models::AbstractModel<double>> result;
ASSERT_NO_THROW(result = bisim.getQuotient()); ASSERT_NO_THROW(result = bisim.getQuotient());
@ -17,19 +17,35 @@ TEST(DeterministicModelBisimulationDecomposition, Die) {
EXPECT_EQ(13, result->getNumberOfStates()); EXPECT_EQ(13, result->getNumberOfStates());
EXPECT_EQ(20, result->getNumberOfTransitions()); EXPECT_EQ(20, result->getNumberOfTransitions());
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim2(*dtmc, std::set<std::string>({"one"}), true, false, true);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options;
options.respectedAtomicPropositions = std::set<std::string>({"one"});
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim2(*dtmc, options);
ASSERT_NO_THROW(result = bisim2.getQuotient()); ASSERT_NO_THROW(result = bisim2.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType()); EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(5, result->getNumberOfStates()); EXPECT_EQ(5, result->getNumberOfStates());
EXPECT_EQ(8, result->getNumberOfTransitions()); EXPECT_EQ(8, result->getNumberOfTransitions());
options.bounded = false;
options.weak = true;
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim3(*dtmc, std::set<std::string>({"one"}), true, true, true);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim3(*dtmc, options);
ASSERT_NO_THROW(result = bisim3.getQuotient()); ASSERT_NO_THROW(result = bisim3.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType()); EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(5, result->getNumberOfStates()); EXPECT_EQ(5, result->getNumberOfStates());
EXPECT_EQ(8, result->getNumberOfTransitions()); EXPECT_EQ(8, result->getNumberOfTransitions());
auto labelFormula = std::make_shared<storm::logic::AtomicLabelFormula>("one");
auto eventuallyFormula = std::make_shared<storm::logic::EventuallyFormula>(labelFormula);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options2(*dtmc, *eventuallyFormula);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim4(*dtmc, options2);
ASSERT_NO_THROW(result = bisim4.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(5, result->getNumberOfStates());
EXPECT_EQ(8, result->getNumberOfTransitions());
} }
TEST(DeterministicModelBisimulationDecomposition, Crowds) { TEST(DeterministicModelBisimulationDecomposition, Crowds) {
@ -38,7 +54,7 @@ TEST(DeterministicModelBisimulationDecomposition, Crowds) {
ASSERT_EQ(abstractModel->getType(), storm::models::DTMC); ASSERT_EQ(abstractModel->getType(), storm::models::DTMC);
std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim(*dtmc, boost::optional<std::set<std::string>>(), true, false, true);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim(*dtmc);
std::shared_ptr<storm::models::AbstractModel<double>> result; std::shared_ptr<storm::models::AbstractModel<double>> result;
ASSERT_NO_THROW(result = bisim.getQuotient()); ASSERT_NO_THROW(result = bisim.getQuotient());
@ -46,17 +62,54 @@ TEST(DeterministicModelBisimulationDecomposition, Crowds) {
EXPECT_EQ(334, result->getNumberOfStates()); EXPECT_EQ(334, result->getNumberOfStates());
EXPECT_EQ(546, result->getNumberOfTransitions()); EXPECT_EQ(546, result->getNumberOfTransitions());
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim2(*dtmc, std::set<std::string>({"observe0Greater1"}), true, false, true);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options;
options.respectedAtomicPropositions = std::set<std::string>({"observe0Greater1"});
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim2(*dtmc, options);
ASSERT_NO_THROW(result = bisim2.getQuotient()); ASSERT_NO_THROW(result = bisim2.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType()); EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(65, result->getNumberOfStates()); EXPECT_EQ(65, result->getNumberOfStates());
EXPECT_EQ(105, result->getNumberOfTransitions()); EXPECT_EQ(105, result->getNumberOfTransitions());
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim3(*dtmc, std::set<std::string>({"observe0Greater1"}), true, true, true);
options.bounded = false;
options.weak = true;
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim3(*dtmc, options);
ASSERT_NO_THROW(result = bisim3.getQuotient()); ASSERT_NO_THROW(result = bisim3.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType()); EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(43, result->getNumberOfStates()); EXPECT_EQ(43, result->getNumberOfStates());
EXPECT_EQ(83, result->getNumberOfTransitions()); EXPECT_EQ(83, result->getNumberOfTransitions());
auto labelFormula = std::make_shared<storm::logic::AtomicLabelFormula>("observe0Greater1");
auto eventuallyFormula = std::make_shared<storm::logic::EventuallyFormula>(labelFormula);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options2(*dtmc, *eventuallyFormula);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim4(*dtmc, options2);
ASSERT_NO_THROW(result = bisim4.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(64, result->getNumberOfStates());
EXPECT_EQ(104, result->getNumberOfTransitions());
auto probabilityOperatorFormula = std::make_shared<storm::logic::ProbabilityOperatorFormula>(eventuallyFormula);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options3(*dtmc, *probabilityOperatorFormula);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim5(*dtmc, options3);
ASSERT_NO_THROW(result = bisim5.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(64, result->getNumberOfStates());
EXPECT_EQ(104, result->getNumberOfTransitions());
auto boundedUntilFormula = std::make_shared<storm::logic::BoundedUntilFormula>(std::make_shared<storm::logic::BooleanLiteralFormula>(true), labelFormula, 50);
typename storm::storage::DeterministicModelBisimulationDecomposition<double>::Options options4(*dtmc, *boundedUntilFormula);
storm::storage::DeterministicModelBisimulationDecomposition<double> bisim6(*dtmc, options4);
ASSERT_NO_THROW(result = bisim6.getQuotient());
EXPECT_EQ(storm::models::DTMC, result->getType());
EXPECT_EQ(65, result->getNumberOfStates());
EXPECT_EQ(105, result->getNumberOfTransitions());
} }
Loading…
Cancel
Save