From 7ce969b312688e8f852ac568351990f6b22afd74 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 23 Feb 2016 17:59:00 +0100 Subject: [PATCH 01/17] started working on more flexible model generation using next-state-generators Former-commit-id: 805940f1790292223f9216cc9dd146af12d3d6c8 --- src/CMakeLists.txt | 4 + src/builder/ExplicitPrismModelBuilder.cpp | 39 +---- src/builder/ExplicitPrismModelBuilder.h | 54 ------- src/generator/Choice.cpp | 111 ++++++++++++++ src/generator/Choice.h | 158 ++++++++++++++++++++ src/generator/NextStateGenerator.h | 27 ++++ src/generator/PrismNextStateGenerator.cpp | 22 +++ src/generator/PrismNextStateGenerator.h | 32 ++++ src/generator/prism/VariableInformation.cpp | 44 ++++++ src/generator/prism/VariableInformation.h | 73 +++++++++ src/storage/Distribution.cpp | 52 +++---- src/storage/Distribution.h | 12 +- 12 files changed, 504 insertions(+), 124 deletions(-) create mode 100644 src/generator/Choice.cpp create mode 100644 src/generator/Choice.h create mode 100644 src/generator/NextStateGenerator.h create mode 100644 src/generator/PrismNextStateGenerator.cpp create mode 100644 src/generator/PrismNextStateGenerator.h create mode 100644 src/generator/prism/VariableInformation.cpp create mode 100644 src/generator/prism/VariableInformation.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b06a498d..0a5832d87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,8 @@ file(GLOB_RECURSE STORM_SOURCES_CLI ${PROJECT_SOURCE_DIR}/src/cli/*.cpp) file(GLOB_RECURSE STORM_MAIN_FILE ${PROJECT_SOURCE_DIR}/src/storm.cpp) file(GLOB_RECURSE STORM_ADAPTERS_FILES ${PROJECT_SOURCE_DIR}/src/adapters/*.h ${PROJECT_SOURCE_DIR}/src/adapters/*.cpp) file(GLOB_RECURSE STORM_BUILDER_FILES ${PROJECT_SOURCE_DIR}/src/builder/*.h ${PROJECT_SOURCE_DIR}/src/builder/*.cpp) +file(GLOB STORM_GENERATOR_FILES ${PROJECT_SOURCE_DIR}/src/generator/*.h ${PROJECT_SOURCE_DIR}/src/generator/*.cpp) +file(GLOB_RECURSE STORM_GENERATOR_PRISM_FILES ${PROJECT_SOURCE_DIR}/src/generator/prism/*.h ${PROJECT_SOURCE_DIR}/src/generator/prism/*.cpp) file(GLOB_RECURSE STORM_CLI_FILES ${PROJECT_SOURCE_DIR}/src/cli/*.h ${PROJECT_SOURCE_DIR}/src/cli/*.cpp) file(GLOB_RECURSE STORM_EXCEPTIONS_FILES ${PROJECT_SOURCE_DIR}/src/exceptions/*.h ${PROJECT_SOURCE_DIR}/src/exceptions/*.cpp) file(GLOB_RECURSE STORM_LOGIC_FILES ${PROJECT_SOURCE_DIR}/src/logic/*.h ${PROJECT_SOURCE_DIR}/src/logic/*.cpp) @@ -57,6 +59,8 @@ set(STORM_MAIN_HEADERS ${STORM_HEADERS_CLI}) source_group(main FILES ${STORM_MAIN_FILE}) source_group(adapters FILES ${STORM_ADAPTERS_FILES}) source_group(builder FILES ${STORM_BUILDER_FILES}) +source_group(generator FILES ${STORM_GENERATOR_FILES}) +source_group(generator\\prism FILES ${STORM_GENERATOR_PRISM_FILES}) source_group(cli FILES ${STORM_CLI_FILES}) source_group(exceptions FILES ${STORM_EXCEPTIONS_FILES}) source_group(logic FILES ${STORM_LOGIC_FILES}) diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index d7c2f6293..19b115c6e 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -75,44 +75,7 @@ namespace storm { ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), bitsPerState(bitsPerState), reachableStates() { // Intentionally left empty. } - - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::VariableInformation::BooleanVariableInformation::BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset) : variable(variable), initialValue(initialValue), bitOffset(bitOffset) { - // Intentionally left empty. - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::VariableInformation::IntegerVariableInformation::IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth) : variable(variable), initialValue(initialValue), lowerBound(lowerBound), upperBound(upperBound), bitOffset(bitOffset), bitWidth(bitWidth) { - // Intentionally left empty. - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::VariableInformation::VariableInformation(storm::expressions::ExpressionManager const& manager) : manager(manager) { - // Intentionally left empty. - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - uint_fast64_t ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const { - auto const& booleanIndex = booleanVariableToIndexMap.find(variable); - if (booleanIndex != booleanVariableToIndexMap.end()) { - return booleanVariables[booleanIndex->second].bitOffset; - } - auto const& integerIndex = integerVariableToIndexMap.find(variable); - if (integerIndex != integerVariableToIndexMap.end()) { - return integerVariables[integerIndex->second].bitOffset; - } - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit index of unknown variable."); - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - uint_fast64_t ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::VariableInformation::getBitWidth(storm::expressions::Variable const& variable) const { - auto const& integerIndex = integerVariableToIndexMap.find(variable); - if (integerIndex != integerVariableToIndexMap.end()) { - return integerVariables[integerIndex->second].bitWidth; - } - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit width of unknown variable."); - } - + template <typename ValueType, typename RewardModelType, typename IndexType> ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), rewardModels(), choiceLabeling() { // Intentionally left empty. diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index ccb0622e7..9bd62ad2b 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -74,60 +74,6 @@ namespace storm { } }; - // A structure storing information about the used variables of the program. - struct VariableInformation { - VariableInformation(storm::expressions::ExpressionManager const& manager); - - struct BooleanVariableInformation { - BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset); - - // The boolean variable. - storm::expressions::Variable variable; - - // Its initial value. - bool initialValue; - - // Its bit offset in the compressed state. - uint_fast64_t bitOffset; - }; - - struct IntegerVariableInformation { - IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth); - - // The integer variable. - storm::expressions::Variable variable; - - // Its initial value. - int_fast64_t initialValue; - - // The lower bound of its range. - int_fast64_t lowerBound; - - // The upper bound of its range. - int_fast64_t upperBound; - - // Its bit offset in the compressed state. - uint_fast64_t bitOffset; - - // Its bit width in the compressed state. - uint_fast64_t bitWidth; - }; - - // Provide methods to access the bit offset and width of variables in the compressed state. - uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const; - uint_fast64_t getBitWidth(storm::expressions::Variable const& variable) const; - - // The known boolean variables. - boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> booleanVariableToIndexMap; - std::vector<BooleanVariableInformation> booleanVariables; - - // The known integer variables. - boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> integerVariableToIndexMap; - std::vector<IntegerVariableInformation> integerVariables; - - storm::expressions::ExpressionManager const& manager; - }; - // A structure holding the individual components of a model. struct ModelComponents { ModelComponents(); diff --git a/src/generator/Choice.cpp b/src/generator/Choice.cpp new file mode 100644 index 000000000..3b6609ebd --- /dev/null +++ b/src/generator/Choice.cpp @@ -0,0 +1,111 @@ +#include "src/generator/Choice.h" + +#include "src/utility/constants.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType> + Choice<ValueType, StateType>::Choice(uint_fast64_t actionIndex, bool markovian) : markovian(markovian), actionIndex(actionIndex), distribution(), totalMass(storm::utility::zero<ValueType>()), choiceReward(storm::utility::zero<ValueType>()) { + // Intentionally left empty. + } + + template<typename ValueType, typename StateType> + typename storm::storage::Distribution<ValueType, StateType>::iterator Choice<ValueType, StateType>::begin() { + return distribution.begin(); + } + + template<typename ValueType, typename StateType> + typename storm::storage::Distribution<ValueType, StateType>::const_iterator Choice<ValueType, StateType>::begin() const { + return distribution.cbegin(); + } + + template<typename ValueType, typename StateType> + typename storm::storage::Distribution<ValueType, StateType>::iterator Choice<ValueType, StateType>::end() { + return distribution.end(); + } + + template<typename ValueType, typename StateType> + typename storm::storage::Distribution<ValueType, StateType>::const_iterator Choice<ValueType, StateType>::end() const { + return distribution.cend(); + } + + template<typename ValueType, typename StateType> + void Choice<ValueType, StateType>::addChoiceLabel(uint_fast64_t label) { + if (!choiceLabels) { + choiceLabels = LabelSet(); + } + choiceLabels->insert(label); + } + + template<typename ValueType, typename StateType> + void Choice<ValueType, StateType>::addChoiceLabels(LabelSet const& labelSet) { + if (!choiceLabels) { + choiceLabels = LabelSet(); + } + choiceLabels->insert(labelSet.begin(), labelSet.end()); + } + + template<typename ValueType, typename StateType> + boost::container::flat_set<uint_fast64_t> const& Choice<ValueType, StateType>::getChoiceLabels() const { + return *choiceLabels; + } + + template<typename ValueType, typename StateType> + uint_fast64_t Choice<ValueType, StateType>::getActionIndex() const { + return actionIndex; + } + + template<typename ValueType, typename StateType> + ValueType Choice<ValueType, StateType>::getTotalMass() const { + return totalMass; + } + + template<typename ValueType, typename StateType> + ValueType& Choice<ValueType, StateType>::getOrAddEntry(StateType const& state) { + auto stateProbabilityPair = distribution.find(state); + + if (stateProbabilityPair == distribution.end()) { + distribution[state] = ValueType(); + } + return distribution.at(state); + } + + template<typename ValueType, typename StateType> + ValueType const& Choice<ValueType, StateType>::getOrAddEntry(StateType const& state) const { + auto stateProbabilityPair = distribution.find(state); + + if (stateProbabilityPair == distribution.end()) { + distribution[state] = ValueType(); + } + return distribution.at(state); + } + + template<typename ValueType, typename StateType> + void Choice<ValueType, StateType>::addProbability(StateType const& state, ValueType const& value) { + totalMass += value; + distribution[state] += value; + } + + template<typename ValueType, typename StateType> + void Choice<ValueType, StateType>::addChoiceReward(ValueType const& value) { + choiceReward += value; + } + + template<typename ValueType, typename StateType> + std::size_t Choice<ValueType, StateType>::size() const { + return distribution.size(); + } + + template<typename ValueType, typename StateType> + std::ostream& operator<<(std::ostream& out, Choice<ValueType, StateType> const& choice) { + out << "<"; + for (auto const& stateProbabilityPair : choice) { + out << stateProbabilityPair.first << " : " << stateProbabilityPair.second << ", "; + } + out << ">"; + return out; + } + + } +} \ No newline at end of file diff --git a/src/generator/Choice.h b/src/generator/Choice.h new file mode 100644 index 000000000..07eb6efbe --- /dev/null +++ b/src/generator/Choice.h @@ -0,0 +1,158 @@ +#ifndef STORM_GENERATOR_CHOICE_H_ +#define STORM_GENERATOR_CHOICE_H_ + +#include <cstdint> +#include <functional> + +#include <boost/optional.hpp> +#include <boost/container/flat_set.hpp> + +#include "src/storage/Distribution.h" + +namespace storm { + namespace generator { + + // A structure holding information about a particular choice. + template<typename ValueType, typename StateType=uint32_t> + struct Choice { + public: + typedef boost::container::flat_set<uint_fast64_t> LabelSet; + + Choice(uint_fast64_t actionIndex = 0, bool markovian = false); + + Choice(Choice const& other) = default; + Choice& operator=(Choice const& other) = default; + Choice(Choice&& other) = default; + Choice& operator=(Choice&& other) = default; + + /*! + * Returns an iterator to the distribution associated with this choice. + * + * @return An iterator to the first element of the distribution. + */ + typename storm::storage::Distribution<ValueType, StateType>::iterator begin(); + + /*! + * Returns an iterator to the distribution associated with this choice. + * + * @return An iterator to the first element of the distribution. + */ + typename storm::storage::Distribution<ValueType, StateType>::const_iterator begin() const; + + /*! + * Returns an iterator past the end of the distribution associated with this choice. + * + * @return An iterator past the end of the distribution. + */ + typename storm::storage::Distribution<ValueType, StateType>::iterator end(); + + /*! + * Returns an iterator past the end of the distribution associated with this choice. + * + * @return An iterator past the end of the distribution. + */ + typename storm::storage::Distribution<ValueType, StateType>::const_iterator end() const; + + /*! + * Inserts the contents of this object to the given output stream. + * + * @param out The stream in which to insert the contents. + */ + template<typename ValueTypePrime, typename StateTypePrime> + friend std::ostream& operator<<(std::ostream& out, Choice<ValueTypePrime, StateTypePrime> const& choice); + + /*! + * Adds the given label to the labels associated with this choice. + * + * @param label The label to associate with this choice. + */ + void addChoiceLabel(uint_fast64_t label); + + /*! + * Adds the given label set to the labels associated with this choice. + * + * @param labelSet The label set to associate with this choice. + */ + void addChoiceLabels(LabelSet const& labelSet); + + /*! + * Retrieves the set of labels associated with this choice. + * + * @return The set of labels associated with this choice. + */ + LabelSet const& getChoiceLabels() const; + + /*! + * Retrieves the index of the action of this choice. + * + * @return The index of the action of this choice. + */ + uint_fast64_t getActionIndex() const; + + /*! + * Retrieves the total mass of this choice. + * + * @return The total mass. + */ + ValueType getTotalMass() const; + + /*! + * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, + * yet. + * + * @param state The state for which to add the entry. + * @return A reference to the entry that is associated with the given state. + */ + ValueType& getOrAddEntry(StateType const& state); + + /*! + * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, + * yet. + * + * @param state The state for which to add the entry. + * @return A reference to the entry that is associated with the given state. + */ + ValueType const& getOrAddEntry(StateType const& state) const; + + /*! + * Adds the given probability value to the given state in the underlying distribution. + */ + void addProbability(StateType const& state, ValueType const& value); + + /*! + * Adds the given value to the reward associated with this choice. + */ + void addChoiceReward(ValueType const& value); + + /*! + * Retrieves the size of the distribution associated with this choice. + */ + std::size_t size() const; + + private: + // A flag indicating whether this choice is Markovian or not. + bool markovian; + + // The action index associated with this choice. + uint_fast64_t actionIndex; + + // The distribution that is associated with the choice. + storm::storage::Distribution<ValueType, StateType> distribution; + + // The total probability mass (or rates) of this choice. + ValueType totalMass; + + // The reward value associated with this choice. + ValueType choiceReward; + + // The labels that are associated with this choice. + boost::optional<LabelSet> choiceLabels; + }; + + template<typename ValueType, typename StateType> + std::ostream& operator<<(std::ostream& out, Choice<ValueType, StateType> const& choice); + + } +} + +#endif /* STORM_GENERATOR_CHOICE_H_ */ \ No newline at end of file diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h new file mode 100644 index 000000000..1fdc38cfb --- /dev/null +++ b/src/generator/NextStateGenerator.h @@ -0,0 +1,27 @@ +#ifndef STORM_GENERATOR_NEXTSTATEGENERATOR_H_ +#define STORM_GENERATOR_NEXTSTATEGENERATOR_H_ + +#include <vector> +#include <cstdint> + +#include "src/storage/sparse/StateType.h" +#include "src/storage/BitVector.h" + +#include "src/generator/Choice.h" + +namespace storm { + namespace generator { + template<typename ValueType, typename StateType = uint32_t> + class NextStateGenerator { + public: + typedef storm::storage::BitVector InternalStateType; + typedef StateType (*StateToIdCallback)(InternalStateType const&); + + virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; + virtual std::vector<Choice<ValueType>> expand(StateType const& state, StateToIdCallback stateToIdCallback) = 0; + virtual ValueType getStateReward(StateType const& state) = 0; + }; + } +} + +#endif \ No newline at end of file diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp new file mode 100644 index 000000000..0acf4d8dd --- /dev/null +++ b/src/generator/PrismNextStateGenerator.cpp @@ -0,0 +1,22 @@ +#include "src/generator/PrismNextStateGenerator.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType> + PrismNextStateGenerator<ValueType, StateType>::PrismNextStateGenerator(storm::prism::Program const& program) : program(program) { + // Intentionally left empty. + } + + template<typename ValueType, typename StateType> + std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::expand(StateType const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) { + // TODO + } + + template<typename ValueType, typename StateType> + ValueType PrismNextStateGenerator<ValueType, StateType>::getStateReward(StateType const& state) { + // TODO + } + + } +} \ No newline at end of file diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h new file mode 100644 index 000000000..1330d4420 --- /dev/null +++ b/src/generator/PrismNextStateGenerator.h @@ -0,0 +1,32 @@ +#ifndef STORM_GENERATOR_PRISMNEXTSTATEGENERATOR_H_ +#define STORM_GENERATOR_PRISMNEXTSTATEGENERATOR_H_ + +#include "src/generator/NextStateGenerator.h" + +#include "src/storage/prism/Program.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType = uint32_t> + class PrismNextStateGenerator : public NextStateGenerator<ValueType, StateType> { + public: + typedef typename NextStateGenerator<ValueType, StateType>::StateToIdCallback StateToIdCallback; + + PrismNextStateGenerator(storm::prism::Program const& program); + + virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; + virtual std::vector<Choice<ValueType>> expand(StateType const& state, StateToIdCallback stateToIdCallback) override; + virtual ValueType getStateReward(StateType const& state) override; + + private: + // The program used for the generation of next states. + storm::prism::Program program; + + + }; + + } +} + +#endif /* STORM_GENERATOR_PRISMNEXTSTATEGENERATOR_H_ */ \ No newline at end of file diff --git a/src/generator/prism/VariableInformation.cpp b/src/generator/prism/VariableInformation.cpp new file mode 100644 index 000000000..f648eda1b --- /dev/null +++ b/src/generator/prism/VariableInformation.cpp @@ -0,0 +1,44 @@ +#include "src/generator/prism/VariableInformation.h" + +#include "src/utility/macros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace generator { + namespace prism { + + BooleanVariableInformation::BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset) : variable(variable), initialValue(initialValue), bitOffset(bitOffset) { + // Intentionally left empty. + } + + IntegerVariableInformation::IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth) : variable(variable), initialValue(initialValue), lowerBound(lowerBound), upperBound(upperBound), bitOffset(bitOffset), bitWidth(bitWidth) { + // Intentionally left empty. + } + + VariableInformation::VariableInformation(storm::expressions::ExpressionManager const& manager) : manager(manager) { + // Intentionally left empty. + } + + uint_fast64_t VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const { + auto const& booleanIndex = booleanVariableToIndexMap.find(variable); + if (booleanIndex != booleanVariableToIndexMap.end()) { + return booleanVariables[booleanIndex->second].bitOffset; + } + auto const& integerIndex = integerVariableToIndexMap.find(variable); + if (integerIndex != integerVariableToIndexMap.end()) { + return integerVariables[integerIndex->second].bitOffset; + } + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit index of unknown variable."); + } + + uint_fast64_t VariableInformation::getBitWidth(storm::expressions::Variable const& variable) const { + auto const& integerIndex = integerVariableToIndexMap.find(variable); + if (integerIndex != integerVariableToIndexMap.end()) { + return integerVariables[integerIndex->second].bitWidth; + } + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit width of unknown variable."); + } + + } + } +} \ No newline at end of file diff --git a/src/generator/prism/VariableInformation.h b/src/generator/prism/VariableInformation.h new file mode 100644 index 000000000..56ac7bda2 --- /dev/null +++ b/src/generator/prism/VariableInformation.h @@ -0,0 +1,73 @@ +#ifndef STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ +#define STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ + +#include <vector> +#include <boost/container/flat_map.hpp> + +#include "src/storage/expressions/Variable.h" + +namespace storm { + namespace generator { + namespace prism { + + // A structure storing information about the boolean variables of the program. + struct BooleanVariableInformation { + BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset); + + // The boolean variable. + storm::expressions::Variable variable; + + // Its initial value. + bool initialValue; + + // Its bit offset in the compressed state. + uint_fast64_t bitOffset; + }; + + // A structure storing information about the integer variables of the program. + struct IntegerVariableInformation { + IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth); + + // The integer variable. + storm::expressions::Variable variable; + + // Its initial value. + int_fast64_t initialValue; + + // The lower bound of its range. + int_fast64_t lowerBound; + + // The upper bound of its range. + int_fast64_t upperBound; + + // Its bit offset in the compressed state. + uint_fast64_t bitOffset; + + // Its bit width in the compressed state. + uint_fast64_t bitWidth; + }; + + // A structure storing information about the used variables of the program. + struct VariableInformation { + VariableInformation(storm::expressions::ExpressionManager const& manager); + + // Provide methods to access the bit offset and width of variables in the compressed state. + uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const; + uint_fast64_t getBitWidth(storm::expressions::Variable const& variable) const; + + // The known boolean variables. + boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> booleanVariableToIndexMap; + std::vector<BooleanVariableInformation> booleanVariables; + + // The known integer variables. + boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> integerVariableToIndexMap; + std::vector<IntegerVariableInformation> integerVariables; + + storm::expressions::ExpressionManager const& manager; + }; + + } + } +} + +#endif /* STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ */ \ No newline at end of file diff --git a/src/storage/Distribution.cpp b/src/storage/Distribution.cpp index 50d794d01..516227277 100644 --- a/src/storage/Distribution.cpp +++ b/src/storage/Distribution.cpp @@ -14,13 +14,13 @@ namespace storm { namespace storage { - template<typename ValueType> - Distribution<ValueType>::Distribution() { + template<typename ValueType, typename StateType> + Distribution<ValueType, StateType>::Distribution() { // Intentionally left empty. } - template<typename ValueType> - bool Distribution<ValueType>::equals(Distribution<ValueType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator) const { + template<typename ValueType, typename StateType> + bool Distribution<ValueType, StateType>::equals(Distribution<ValueType, StateType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator) const { // We need to check equality by ourselves, because we need to account for epsilon differences. if (this->distribution.size() != other.distribution.size()) { return false; @@ -42,8 +42,8 @@ namespace storm { return true; } - template<typename ValueType> - void Distribution<ValueType>::addProbability(storm::storage::sparse::state_type const& state, ValueType const& probability) { + template<typename ValueType, typename StateType> + void Distribution<ValueType, StateType>::addProbability(StateType const& state, ValueType const& probability) { auto it = this->distribution.find(state); if (it == this->distribution.end()) { this->distribution.emplace_hint(it, state, probability); @@ -52,8 +52,8 @@ namespace storm { } } - template<typename ValueType> - void Distribution<ValueType>::removeProbability(storm::storage::sparse::state_type const& state, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator) { + template<typename ValueType, typename StateType> + void Distribution<ValueType, StateType>::removeProbability(StateType const& state, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator) { auto it = this->distribution.find(state); STORM_LOG_ASSERT(it != this->distribution.end(), "Cannot remove probability, because the state is not in the support of the distribution."); it->second -= probability; @@ -62,34 +62,34 @@ namespace storm { } } - template<typename ValueType> - void Distribution<ValueType>::shiftProbability(storm::storage::sparse::state_type const& fromState, storm::storage::sparse::state_type const& toState, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator) { + template<typename ValueType, typename StateType> + void Distribution<ValueType, StateType>::shiftProbability(StateType const& fromState, StateType const& toState, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator) { removeProbability(fromState, probability, comparator); addProbability(toState, probability); } - template<typename ValueType> - typename Distribution<ValueType>::iterator Distribution<ValueType>::begin() { + template<typename ValueType, typename StateType> + typename Distribution<ValueType, StateType>::iterator Distribution<ValueType, StateType>::begin() { return this->distribution.begin(); } - template<typename ValueType> - typename Distribution<ValueType>::const_iterator Distribution<ValueType>::begin() const { + template<typename ValueType, typename StateType> + typename Distribution<ValueType, StateType>::const_iterator Distribution<ValueType, StateType>::begin() const { return this->distribution.begin(); } - template<typename ValueType> - typename Distribution<ValueType>::iterator Distribution<ValueType>::end() { + template<typename ValueType, typename StateType> + typename Distribution<ValueType, StateType>::iterator Distribution<ValueType, StateType>::end() { return this->distribution.end(); } - template<typename ValueType> - typename Distribution<ValueType>::const_iterator Distribution<ValueType>::end() const { + template<typename ValueType, typename StateType> + typename Distribution<ValueType, StateType>::const_iterator Distribution<ValueType, StateType>::end() const { return this->distribution.end(); } - template<typename ValueType> - void Distribution<ValueType>::scale(storm::storage::sparse::state_type const& state) { + template<typename ValueType, typename StateType> + void Distribution<ValueType, StateType>::scale(StateType const& state) { auto probabilityIterator = this->distribution.find(state); if (probabilityIterator != this->distribution.end()) { ValueType scaleValue = storm::utility::one<ValueType>() / probabilityIterator->second; @@ -101,13 +101,13 @@ namespace storm { } } - template<typename ValueType> - std::size_t Distribution<ValueType>::size() const { + template<typename ValueType, typename StateType> + std::size_t Distribution<ValueType, StateType>::size() const { return this->distribution.size(); } - template<typename ValueType> - std::ostream& operator<<(std::ostream& out, Distribution<ValueType> const& distribution) { + template<typename ValueType, typename StateType> + std::ostream& operator<<(std::ostream& out, Distribution<ValueType, StateType> const& distribution) { out << "{"; for (auto const& entry : distribution) { out << "[" << entry.second << ": " << entry.first << "], "; @@ -117,8 +117,8 @@ namespace storm { return out; } - template<typename ValueType> - bool Distribution<ValueType>::less(Distribution<ValueType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator) const { + template<typename ValueType, typename StateType> + bool Distribution<ValueType, StateType>::less(Distribution<ValueType, StateType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator) const { if (this->size() != other.size()) { return this->size() < other.size(); } diff --git a/src/storage/Distribution.h b/src/storage/Distribution.h index 9d3276941..39e6eb6f9 100644 --- a/src/storage/Distribution.h +++ b/src/storage/Distribution.h @@ -17,10 +17,10 @@ namespace storm { namespace storage { - template<typename ValueType> + template<typename ValueType, typename StateType=storm::storage::sparse::state_type> class Distribution { public: - typedef boost::container::flat_map<storm::storage::sparse::state_type, ValueType> container_type; + typedef boost::container::flat_map<StateType, ValueType> container_type; typedef typename container_type::iterator iterator; typedef typename container_type::const_iterator const_iterator; @@ -51,7 +51,7 @@ namespace storm { * @param state The state to which to assign the probability. * @param probability The probability to assign. */ - void addProbability(storm::storage::sparse::state_type const& state, ValueType const& probability); + void addProbability(StateType const& state, ValueType const& probability); /*! * Removes the given probability mass of going to the given state. @@ -61,7 +61,7 @@ namespace storm { * @param comparator A comparator that is used to determine if the remaining probability is zero. If so, the * entry is removed. */ - void removeProbability(storm::storage::sparse::state_type const& state, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()); + void removeProbability(StateType const& state, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()); /*! * Removes the probability mass from one state and adds it to another. @@ -72,7 +72,7 @@ namespace storm { * @param comparator A comparator that is used to determine if the remaining probability is zero. If so, the * entry is removed. */ - void shiftProbability(storm::storage::sparse::state_type const& fromState, storm::storage::sparse::state_type const& toState, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()); + void shiftProbability(StateType const& fromState, StateType const& toState, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()); /*! * Retrieves an iterator to the elements in this distribution. @@ -109,7 +109,7 @@ namespace storm { * * @param state The state whose associated probability is used to scale the distribution. */ - void scale(storm::storage::sparse::state_type const& state); + void scale(StateType const& state); /*! * Retrieves the size of the distribution, i.e. the size of the support set. From 8a0bd32b553864a2220e05c1ff089f664ff0f54b Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 23 Feb 2016 22:31:01 +0100 Subject: [PATCH 02/17] tearing apart explicit model builder and 'next-state generation' Former-commit-id: 2ccda8d931c279bb7b9359f901ea1980e08575b1 --- src/CMakeLists.txt | 4 +- src/builder/ExplicitPrismModelBuilder.cpp | 437 +++++--------------- src/builder/ExplicitPrismModelBuilder.h | 98 ++--- src/generator/Choice.cpp | 4 +- src/generator/Choice.h | 4 +- src/generator/NextStateGenerator.h | 10 +- src/generator/PrismNextStateGenerator.cpp | 249 ++++++++++- src/generator/PrismNextStateGenerator.h | 92 ++++- src/generator/VariableInformation.cpp | 42 ++ src/generator/VariableInformation.h | 71 ++++ src/generator/prism/VariableInformation.cpp | 44 -- src/generator/prism/VariableInformation.h | 73 ---- src/storage/prism/Program.cpp | 4 + src/storage/prism/Program.h | 12 + 14 files changed, 607 insertions(+), 537 deletions(-) create mode 100644 src/generator/VariableInformation.cpp create mode 100644 src/generator/VariableInformation.h delete mode 100644 src/generator/prism/VariableInformation.cpp delete mode 100644 src/generator/prism/VariableInformation.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a5832d87..7587a3f22 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,8 +11,7 @@ file(GLOB_RECURSE STORM_SOURCES_CLI ${PROJECT_SOURCE_DIR}/src/cli/*.cpp) file(GLOB_RECURSE STORM_MAIN_FILE ${PROJECT_SOURCE_DIR}/src/storm.cpp) file(GLOB_RECURSE STORM_ADAPTERS_FILES ${PROJECT_SOURCE_DIR}/src/adapters/*.h ${PROJECT_SOURCE_DIR}/src/adapters/*.cpp) file(GLOB_RECURSE STORM_BUILDER_FILES ${PROJECT_SOURCE_DIR}/src/builder/*.h ${PROJECT_SOURCE_DIR}/src/builder/*.cpp) -file(GLOB STORM_GENERATOR_FILES ${PROJECT_SOURCE_DIR}/src/generator/*.h ${PROJECT_SOURCE_DIR}/src/generator/*.cpp) -file(GLOB_RECURSE STORM_GENERATOR_PRISM_FILES ${PROJECT_SOURCE_DIR}/src/generator/prism/*.h ${PROJECT_SOURCE_DIR}/src/generator/prism/*.cpp) +file(GLOB_RECURSE STORM_GENERATOR_FILES ${PROJECT_SOURCE_DIR}/src/generator/*.h ${PROJECT_SOURCE_DIR}/src/generator/*.cpp) file(GLOB_RECURSE STORM_CLI_FILES ${PROJECT_SOURCE_DIR}/src/cli/*.h ${PROJECT_SOURCE_DIR}/src/cli/*.cpp) file(GLOB_RECURSE STORM_EXCEPTIONS_FILES ${PROJECT_SOURCE_DIR}/src/exceptions/*.h ${PROJECT_SOURCE_DIR}/src/exceptions/*.cpp) file(GLOB_RECURSE STORM_LOGIC_FILES ${PROJECT_SOURCE_DIR}/src/logic/*.h ${PROJECT_SOURCE_DIR}/src/logic/*.cpp) @@ -60,7 +59,6 @@ source_group(main FILES ${STORM_MAIN_FILE}) source_group(adapters FILES ${STORM_ADAPTERS_FILES}) source_group(builder FILES ${STORM_BUILDER_FILES}) source_group(generator FILES ${STORM_GENERATOR_FILES}) -source_group(generator\\prism FILES ${STORM_GENERATOR_PRISM_FILES}) source_group(cli FILES ${STORM_CLI_FILES}) source_group(exceptions FILES ${STORM_EXCEPTIONS_FILES}) source_group(logic FILES ${STORM_LOGIC_FILES}) diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 19b115c6e..039048111 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -66,33 +66,33 @@ namespace storm { storm::storage::SparseMatrixBuilder<ValueType> transitionRewardMatrixBuilder; }; - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::StateInformation::StateInformation(uint_fast64_t numberOfStates) : valuations(numberOfStates) { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::StateInformation::StateInformation(uint_fast64_t numberOfStates) : valuations(numberOfStates) { // Intentionally left empty. } - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), bitsPerState(bitsPerState), reachableStates() { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), bitsPerState(bitsPerState), numberOfStates() { // Intentionally left empty. } - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), rewardModels(), choiceLabeling() { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), rewardModels(), choiceLabeling() { // Intentionally left empty. } - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::Options::Options() : buildCommandLabels(false), buildAllRewardModels(true), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options() : buildCommandLabels(false), buildAllRewardModels(true), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { // Intentionally left empty. } - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::Options::Options(storm::logic::Formula const& formula) : buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(std::set<std::string>()), expressionLabels(std::vector<storm::expressions::Expression>()), terminalStates(), negatedTerminalStates() { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options(storm::logic::Formula const& formula) : buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(std::set<std::string>()), expressionLabels(std::vector<storm::expressions::Expression>()), terminalStates(), negatedTerminalStates() { this->preserveFormula(formula); } - template <typename ValueType, typename RewardModelType, typename IndexType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::Options::Options(std::vector<std::shared_ptr<const storm::logic::Formula>> const& formulas) : buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options(std::vector<std::shared_ptr<const storm::logic::Formula>> const& formulas) : buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { if (formulas.empty()) { this->buildAllRewardModels = true; this->buildAllLabels = true; @@ -106,8 +106,8 @@ namespace storm { } } - template <typename ValueType, typename RewardModelType, typename IndexType> - void ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) { + template <typename ValueType, typename RewardModelType, typename StateType> + void ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) { if (formula.isAtomicExpressionFormula()) { terminalStates = formula.asAtomicExpressionFormula().getExpression(); } else if (formula.isAtomicLabelFormula()) { @@ -136,8 +136,8 @@ namespace storm { } } - template <typename ValueType, typename RewardModelType, typename IndexType> - void ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::Options::preserveFormula(storm::logic::Formula const& formula) { + template <typename ValueType, typename RewardModelType, typename StateType> + void ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::preserveFormula(storm::logic::Formula const& formula) { // If we already had terminal states, we need to erase them. if (terminalStates) { terminalStates.reset(); @@ -174,105 +174,108 @@ namespace storm { } } - template <typename ValueType, typename RewardModelType, typename IndexType> - void ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::Options::addConstantDefinitionsFromString(storm::prism::Program const& program, std::string const& constantDefinitionString) { + template <typename ValueType, typename RewardModelType, typename StateType> + void ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::addConstantDefinitionsFromString(storm::prism::Program const& program, std::string const& constantDefinitionString) { constantDefinitions = storm::utility::prism::parseConstantDefinitionString(program, constantDefinitionString); } - template <typename ValueType, typename RewardModelType, typename IndexType> - typename ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::StateInformation const& ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::getStateInformation() const { - STORM_LOG_THROW(static_cast<bool>(stateInformation), storm::exceptions::InvalidOperationException, "The state information was not properly build."); - return stateInformation.get(); - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - storm::prism::Program const& ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::getTranslatedProgram() const { - return preparedProgram.get(); - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::translateProgram(storm::prism::Program program, Options const& options) { + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : options(options), program(program) { // Start by defining the undefined constants in the model. if (options.constantDefinitions) { - preparedProgram = program.defineUndefinedConstants(options.constantDefinitions.get()); + this->program = program.defineUndefinedConstants(options.constantDefinitions.get()); } else { - preparedProgram = program; + this->program = program; } - + // If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message. #ifdef STORM_HAVE_CARL // If the program either has undefined constants or we are building a parametric model, but the parameters // not only appear in the probabilities, we re - if (!std::is_same<ValueType, storm::RationalFunction>::value && preparedProgram->hasUndefinedConstants()) { + if (!std::is_same<ValueType, storm::RationalFunction>::value && this->program.hasUndefinedConstants()) { #else - if (preparedProgram->hasUndefinedConstants()) { + if (this->program->hasUndefinedConstants()) { #endif - std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = preparedProgram->getUndefinedConstants(); - std::stringstream stream; - bool printComma = false; - for (auto const& constant : undefinedConstants) { - if (printComma) { - stream << ", "; - } else { - printComma = true; + std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = this->program.getUndefinedConstants(); + std::stringstream stream; + bool printComma = false; + for (auto const& constant : undefinedConstants) { + if (printComma) { + stream << ", "; + } else { + printComma = true; + } + stream << constant.get().getName() << " (" << constant.get().getType() << ")"; } - stream << constant.get().getName() << " (" << constant.get().getType() << ")"; - } - stream << "."; - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); + stream << "."; + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); #ifdef STORM_HAVE_CARL - } else if (std::is_same<ValueType, storm::RationalFunction>::value && !preparedProgram->hasUndefinedConstantsOnlyInUpdateProbabilitiesAndRewards()) { - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, which is not admitted."); + } else if (std::is_same<ValueType, storm::RationalFunction>::value && !this->program.hasUndefinedConstantsOnlyInUpdateProbabilitiesAndRewards()) { + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, which is not admitted."); #endif - } - - // If the set of labels we are supposed to built is restricted, we need to remove the other labels from the program. - if (options.labelsToBuild) { - if (!options.buildAllLabels) { - preparedProgram->filterLabels(options.labelsToBuild.get()); } - } - - // If we need to build labels for expressions that may appear in some formula, we need to add appropriate - // labels to the program. - if (options.expressionLabels) { - std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = preparedProgram->getConstantsSubstitution(); - - for (auto const& expression : options.expressionLabels.get()) { - std::stringstream stream; - stream << expression.substitute(constantsSubstitution); - std::string name = stream.str(); - if (!preparedProgram->hasLabel(name)) { - preparedProgram->addLabel(name, expression); + + // If the set of labels we are supposed to built is restricted, we need to remove the other labels from the program. + if (options.labelsToBuild) { + if (!options.buildAllLabels) { + this->program.filterLabels(options.labelsToBuild.get()); + } + } + + // If we need to build labels for expressions that may appear in some formula, we need to add appropriate + // labels to the program. + if (options.expressionLabels) { + std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = this->program.getConstantsSubstitution(); + + for (auto const& expression : options.expressionLabels.get()) { + std::stringstream stream; + stream << expression.substitute(constantsSubstitution); + std::string name = stream.str(); + if (!this->program.hasLabel(name)) { + this->program.addLabel(name, expression); + } } } - } - - // Now that the program is fixed, we we need to substitute all constants with their concrete value. - preparedProgram = preparedProgram->substituteConstants(); - - STORM_LOG_DEBUG("Building representation of program:" << std::endl << *preparedProgram << std::endl); + + // Now that the program is fixed, we we need to substitute all constants with their concrete value. + this->program = program.substituteConstants(); + } + + template <typename ValueType, typename RewardModelType, typename StateType> + typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::StateInformation const& ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getStateInformation() const { + STORM_LOG_THROW(static_cast<bool>(stateInformation), storm::exceptions::InvalidOperationException, "The state information was not properly build."); + return stateInformation.get(); + } + + template <typename ValueType, typename RewardModelType, typename StateType> + storm::prism::Program const& ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getTranslatedProgram() const { + return program; + } + + template <typename ValueType, typename RewardModelType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::translate() { + STORM_LOG_DEBUG("Building representation of program:" << std::endl << *program << std::endl); // Select the appropriate reward models (after the constants have been substituted). std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels; // First, we make sure that all selected reward models actually exist. for (auto const& rewardModelName : options.rewardModelsToBuild) { - STORM_LOG_THROW(rewardModelName.empty() || preparedProgram->hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'."); + STORM_LOG_THROW(rewardModelName.empty() || program.hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'."); } - for (auto const& rewardModel : preparedProgram->getRewardModels()) { + for (auto const& rewardModel : program.getRewardModels()) { if (options.buildAllRewardModels || options.rewardModelsToBuild.find(rewardModel.getName()) != options.rewardModelsToBuild.end()) { selectedRewardModels.push_back(rewardModel); } } // If no reward model was selected until now and a referenced reward model appears to be unique, we build // the only existing reward model (given that no explicit name was given for the referenced reward model). - if (selectedRewardModels.empty() && preparedProgram->getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") { - selectedRewardModels.push_back(preparedProgram->getRewardModel(0)); + if (selectedRewardModels.empty() && program.getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") { + selectedRewardModels.push_back(program.getRewardModel(0)); } - ModelComponents modelComponents = buildModelComponents(*preparedProgram, selectedRewardModels, options); + ModelComponents modelComponents = buildModelComponents(*program, selectedRewardModels, options); std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> result; switch (program.getModelType()) { @@ -292,19 +295,9 @@ namespace storm { return result; } - - template <typename ValueType, typename RewardModelType, typename IndexType> - void ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::unpackStateIntoEvaluator(storm::storage::BitVector const& currentState, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator) { - for (auto const& booleanVariable : variableInformation.booleanVariables) { - evaluator.setBooleanValue(booleanVariable.variable, currentState.get(booleanVariable.bitOffset)); - } - for (auto const& integerVariable : variableInformation.integerVariables) { - evaluator.setIntegerValue(integerVariable.variable, currentState.getAsInt(integerVariable.bitOffset, integerVariable.bitWidth) + integerVariable.lowerBound); - } - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - storm::expressions::SimpleValuation ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::unpackStateIntoValuation(storm::storage::BitVector const& currentState, VariableInformation const& variableInformation) { + + template <typename ValueType, typename RewardModelType, typename StateType> + storm::expressions::SimpleValuation ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::unpackStateIntoValuation(storm::storage::BitVector const& currentState) { storm::expressions::SimpleValuation result(variableInformation.manager.getSharedPointer()); for (auto const& booleanVariable : variableInformation.booleanVariables) { result.setBooleanValue(booleanVariable.variable, currentState.get(booleanVariable.bitOffset)); @@ -315,254 +308,26 @@ namespace storm { return result; } - template <typename ValueType, typename RewardModelType, typename IndexType> - typename ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::CompressedState ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::applyUpdate(VariableInformation const& variableInformation, CompressedState const& state, storm::prism::Update const& update, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator) { - return applyUpdate(variableInformation, state, state, update, evaluator); - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - typename ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::CompressedState ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::applyUpdate(VariableInformation const& variableInformation, CompressedState const& state, CompressedState const& baseState, storm::prism::Update const& update, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator) { - CompressedState newState(state); - - auto assignmentIt = update.getAssignments().begin(); - auto assignmentIte = update.getAssignments().end(); - - // Iterate over all boolean assignments and carry them out. - auto boolIt = variableInformation.booleanVariables.begin(); - for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasBooleanType(); ++assignmentIt) { - while (assignmentIt->getVariable() != boolIt->variable) { - ++boolIt; - } - newState.set(boolIt->bitOffset, evaluator.asBool(assignmentIt->getExpression())); - } - - // Iterate over all integer assignments and carry them out. - auto integerIt = variableInformation.integerVariables.begin(); - for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasIntegerType(); ++assignmentIt) { - while (assignmentIt->getVariable() != integerIt->variable) { - ++integerIt; - } - int_fast64_t assignedValue = evaluator.asInt(assignmentIt->getExpression()); - STORM_LOG_THROW(assignedValue <= integerIt->upperBound, storm::exceptions::WrongFormatException, "The update " << update << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '" << assignmentIt->getVariableName() << "'."); - newState.setFromInt(integerIt->bitOffset, integerIt->bitWidth, assignedValue - integerIt->lowerBound); - STORM_LOG_ASSERT(static_cast<int_fast64_t>(newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth)) + integerIt->lowerBound == assignedValue, "Writing to the bit vector bucket failed (read " << newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth) << " but wrote " << assignedValue << ")."); - } - - // Check that we processed all assignments. - STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed."); - - return newState; - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - IndexType ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::getOrAddStateIndex(CompressedState const& state, InternalStateInformation& internalStateInformation, std::queue<storm::storage::BitVector>& stateQueue) { + template <typename ValueType, typename RewardModelType, typename StateType> + StateType ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex(CompressedState const& state) { uint32_t newIndex = internalStateInformation.reachableStates.size(); // Check, if the state was already registered. std::pair<uint32_t, std::size_t> actualIndexBucketPair = internalStateInformation.stateStorage.findOrAddAndGetBucket(state, newIndex); if (actualIndexBucketPair.first == newIndex) { - stateQueue.push(state); - internalStateInformation.reachableStates.push_back(state); + statesToExplore.push(state); + ++internalStateInformation.numberOfStates; } return actualIndexBucketPair.first; } - - template <typename ValueType, typename RewardModelType, typename IndexType> - boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::getActiveCommandsByActionIndex(storm::prism::Program const& program,storm::expressions::ExpressionEvaluator<ValueType> const& evaluator, uint_fast64_t const& actionIndex) { - boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> result((std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>())); - - // Iterate over all modules. - for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { - storm::prism::Module const& module = program.getModule(i); - - // If the module has no command labeled with the given action, we can skip this module. - if (!module.hasActionIndex(actionIndex)) { - continue; - } - - std::set<uint_fast64_t> const& commandIndices = module.getCommandIndicesByActionIndex(actionIndex); - - // If the module contains the action, but there is no command in the module that is labeled with - // this action, we don't have any feasible command combinations. - if (commandIndices.empty()) { - return boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>>(); - } - - std::vector<std::reference_wrapper<storm::prism::Command const>> commands; - - // Look up commands by their indices and add them if the guard evaluates to true in the given state. - for (uint_fast64_t commandIndex : commandIndices) { - storm::prism::Command const& command = module.getCommand(commandIndex); - if (evaluator.asBool(command.getGuardExpression())) { - commands.push_back(command); - } - } - - // If there was no enabled command although the module has some command with the required action label, - // we must not return anything. - if (commands.size() == 0) { - return boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>>(); - } - - result.get().push_back(std::move(commands)); - } - - return result; - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - std::vector<Choice<ValueType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::getUnlabeledTransitions(storm::prism::Program const& program, bool discreteTimeModel, InternalStateInformation& internalStateInformation, VariableInformation const& variableInformation, storm::storage::BitVector const& currentState, bool choiceLabeling, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator, std::queue<storm::storage::BitVector>& stateQueue, storm::utility::ConstantsComparator<ValueType> const& comparator) { - std::vector<Choice<ValueType>> result; - - // Iterate over all modules. - for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { - storm::prism::Module const& module = program.getModule(i); - - // Iterate over all commands. - for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) { - storm::prism::Command const& command = module.getCommand(j); - - // Only consider unlabeled commands. - if (command.isLabeled()) continue; - - // Skip the command, if it is not enabled. - if (!evaluator.asBool(command.getGuardExpression())) { - continue; - } - - result.push_back(Choice<ValueType>(0, choiceLabeling)); - Choice<ValueType>& choice = result.back(); - - // Remember the command labels only if we were asked to. - if (choiceLabeling) { - choice.addChoiceLabel(command.getGlobalIndex()); - } - - // Iterate over all updates of the current command. - ValueType probabilitySum = storm::utility::zero<ValueType>(); - for (uint_fast64_t k = 0; k < command.getNumberOfUpdates(); ++k) { - storm::prism::Update const& update = command.getUpdate(k); - - // Obtain target state index and add it to the list of known states. If it has not yet been - // seen, we also add it to the set of states that have yet to be explored. - uint32_t stateIndex = getOrAddStateIndex(applyUpdate(variableInformation, currentState, update, evaluator), internalStateInformation, stateQueue); - - // Update the choice by adding the probability/target state to it. - ValueType probability = evaluator.asRational(update.getLikelihoodExpression()); - choice.addProbability(stateIndex, probability); - probabilitySum += probability; - } - - // Check that the resulting distribution is in fact a distribution. - STORM_LOG_THROW(!discreteTimeModel || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for command '" << command << "' (actually sum to " << probabilitySum << ")."); - } - } - - return result; - } - - template <typename ValueType, typename RewardModelType, typename IndexType> - std::vector<Choice<ValueType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::getLabeledTransitions(storm::prism::Program const& program, bool discreteTimeModel, InternalStateInformation& internalStateInformation, VariableInformation const& variableInformation, storm::storage::BitVector const& currentState, bool choiceLabeling, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator, std::queue<storm::storage::BitVector>& stateQueue, storm::utility::ConstantsComparator<ValueType> const& comparator) { - std::vector<Choice<ValueType>> result; - - for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) { - boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists = getActiveCommandsByActionIndex(program, evaluator, actionIndex); - - // Only process this action label, if there is at least one feasible solution. - if (optionalActiveCommandLists) { - std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> const& activeCommandList = optionalActiveCommandLists.get(); - std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> iteratorList(activeCommandList.size()); - - // Initialize the list of iterators. - for (size_t i = 0; i < activeCommandList.size(); ++i) { - iteratorList[i] = activeCommandList[i].cbegin(); - } - - // As long as there is one feasible combination of commands, keep on expanding it. - bool done = false; - while (!done) { - std::unordered_map<CompressedState, ValueType>* currentTargetStates = new std::unordered_map<CompressedState, ValueType>(); - std::unordered_map<CompressedState, ValueType>* newTargetStates = new std::unordered_map<CompressedState, ValueType>(); - currentTargetStates->emplace(currentState, storm::utility::one<ValueType>()); - - for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { - storm::prism::Command const& command = *iteratorList[i]; - - for (uint_fast64_t j = 0; j < command.getNumberOfUpdates(); ++j) { - storm::prism::Update const& update = command.getUpdate(j); - - for (auto const& stateProbabilityPair : *currentTargetStates) { - // Compute the new state under the current update and add it to the set of new target states. - CompressedState newTargetState = applyUpdate(variableInformation, stateProbabilityPair.first, currentState, update, evaluator); - newTargetStates->emplace(newTargetState, stateProbabilityPair.second * evaluator.asRational(update.getLikelihoodExpression())); - } - } - - // If there is one more command to come, shift the target states one time step back. - if (i < iteratorList.size() - 1) { - delete currentTargetStates; - currentTargetStates = newTargetStates; - newTargetStates = new std::unordered_map<CompressedState, ValueType>(); - } - } - - // At this point, we applied all commands of the current command combination and newTargetStates - // contains all target states and their respective probabilities. That means we are now ready to - // add the choice to the list of transitions. - result.push_back(Choice<ValueType>(actionIndex, choiceLabeling)); - - // Now create the actual distribution. - Choice<ValueType>& choice = result.back(); - - // Remember the command labels only if we were asked to. - if (choiceLabeling) { - // Add the labels of all commands to this choice. - for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { - choice.addChoiceLabel(iteratorList[i]->get().getGlobalIndex()); - } - } - - ValueType probabilitySum = storm::utility::zero<ValueType>(); - for (auto const& stateProbabilityPair : *newTargetStates) { - uint32_t actualIndex = getOrAddStateIndex(stateProbabilityPair.first, internalStateInformation, stateQueue); - choice.addProbability(actualIndex, stateProbabilityPair.second); - probabilitySum += stateProbabilityPair.second; - } - - // Check that the resulting distribution is in fact a distribution. - STORM_LOG_THROW(!discreteTimeModel || !comparator.isConstant(probabilitySum) || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ")."); - - // Dispose of the temporary maps. - delete currentTargetStates; - delete newTargetStates; - - // Now, check whether there is one more command combination to consider. - bool movedIterator = false; - for (int_fast64_t j = iteratorList.size() - 1; j >= 0; --j) { - ++iteratorList[j]; - if (iteratorList[j] != activeCommandList[j].end()) { - movedIterator = true; - } else { - // Reset the iterator to the beginning of the list. - iteratorList[j] = activeCommandList[j].begin(); - } - } - - done = !movedIterator; - } - } - } - - return result; - } - template <typename ValueType, typename RewardModelType, typename IndexType> - boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::buildMatrices(storm::prism::Program const& program, VariableInformation const& variableInformation, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, bool commandLabels, bool deterministicModel, bool discreteTimeModel, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression) { + template <typename ValueType, typename RewardModelType, typename StateType> + boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression) { // Create choice labels, if requested, boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> choiceLabels; - if (commandLabels) { + if (options.buildCommandLabels) { choiceLabels = std::vector<boost::container::flat_set<uint_fast64_t>>(); } @@ -606,7 +371,7 @@ namespace storm { // Get the current state and unpack it. storm::storage::BitVector currentState = stateQueue.front(); stateQueue.pop(); - IndexType stateIndex = internalStateInformation.stateStorage.getValue(currentState); + StateType stateIndex = internalStateInformation.stateStorage.getValue(currentState); STORM_LOG_TRACE("Exploring state with id " << stateIndex << "."); unpackStateIntoEvaluator(currentState, variableInformation, evaluator); @@ -615,11 +380,7 @@ namespace storm { std::vector<Choice<ValueType>> allLabeledChoices; bool deadlockOnPurpose = false; if (terminalExpression && evaluator.asBool(terminalExpression.get())) { - //std::cout << unpackStateIntoValuation(currentState, variableInformation).toString(true) << std::endl; - //allUnlabeledChoices = getUnlabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); - //allLabeledChoices = getLabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); - - // Nothing to do in this case. + // Nothing to do in this case. deadlockOnPurpose = true; } else { // Retrieve all choices for the current state. @@ -886,8 +647,8 @@ namespace storm { return choiceLabels; } - template <typename ValueType, typename RewardModelType, typename IndexType> - typename ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::buildModelComponents(storm::prism::Program const& program, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, Options const& options) { + template <typename ValueType, typename RewardModelType, typename StateType> + typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents(storm::prism::Program const& program, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, Options const& options) { ModelComponents modelComponents; uint_fast64_t bitOffset = 0; @@ -991,8 +752,8 @@ namespace storm { return modelComponents; } - template <typename ValueType, typename RewardModelType, typename IndexType> - storm::models::sparse::StateLabeling ExplicitPrismModelBuilder<ValueType, RewardModelType, IndexType>::buildStateLabeling(storm::prism::Program const& program, VariableInformation const& variableInformation, InternalStateInformation const& internalStateInformation) { + template <typename ValueType, typename RewardModelType, typename StateType> + storm::models::sparse::StateLabeling ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildStateLabeling(storm::prism::Program const& program, VariableInformation const& variableInformation, InternalStateInformation const& internalStateInformation) { storm::expressions::ExpressionEvaluator<ValueType> evaluator(program.getManager()); std::vector<storm::prism::Label> const& labels = program.getLabels(); diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index 9bd62ad2b..5e4f5b2c5 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -22,9 +22,11 @@ #include "src/storage/SparseMatrix.h" #include "src/settings/SettingsManager.h" -#include "src/utility/constants.h" + #include "src/utility/prism.h" +#include "src/generator/VariableInformation.h" + namespace storm { namespace utility { template<typename ValueType> class ConstantsComparator; @@ -37,7 +39,7 @@ namespace storm { // Forward-declare classes. template <typename ValueType> struct RewardModelBuilder; - template<typename ValueType, typename RewardModelType = storm::models::sparse::StandardRewardModel<ValueType>, typename IndexType = uint32_t> + template<typename ValueType, typename RewardModelType = storm::models::sparse::StandardRewardModel<ValueType>, typename StateType = uint32_t> class ExplicitPrismModelBuilder { public: typedef storm::storage::BitVector CompressedState; @@ -47,16 +49,16 @@ namespace storm { InternalStateInformation(uint64_t bitsPerState); // This member stores all the states and maps them to their unique indices. - storm::storage::BitVectorHashMap<IndexType> stateStorage; + storm::storage::BitVectorHashMap<StateType> stateStorage; // A list of initial states in terms of their global indices. - std::vector<IndexType> initialStateIndices; + std::vector<StateType> initialStateIndices; // The number of bits of each state. uint64_t bitsPerState; - // A list of reachable states as indices in the stateToIndexMap. - std::vector<storm::storage::BitVector> reachableStates; + // The number of states that were found in the exploration so far. + uint_fast64_t numberOfStates; }; // A structure holding information about the reachable state space that can be retrieved from the outside. @@ -174,6 +176,13 @@ namespace storm { boost::optional<boost::variant<storm::expressions::Expression, std::string>> negatedTerminalStates; }; + /*! + * Creates a builder for the given program. + * + * @param program The program to build. + */ + ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options = Options()); + /*! * Convert the program given at construction time to an abstract model. The type of the model is the one * specified in the program. The given reward model name selects the rewards that the model will contain. @@ -184,7 +193,7 @@ namespace storm { * @param rewardModel The reward model that is to be built. * @return The explicit model that was given by the probabilistic program. */ - std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> translateProgram(storm::prism::Program program, Options const& options = Options()); + std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> translate(); /*! * If requested in the options, information about the variable valuations in the reachable states can be @@ -195,39 +204,14 @@ namespace storm { StateInformation const& getStateInformation() const; /*! - * Retrieves the program that was actually translated (i.e. including constant substitutions etc.). Note - * that this function may only be called after a succesful translation. + * Retrieves the program that was actually translated (i.e. including constant substitutions etc.). * * @return The translated program. */ storm::prism::Program const& getTranslatedProgram() const; private: - static void unpackStateIntoEvaluator(storm::storage::BitVector const& currentState, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator); - - static storm::expressions::SimpleValuation unpackStateIntoValuation(storm::storage::BitVector const& currentState, VariableInformation const& variableInformation); - - /*! - * Applies an update to the given state and returns the resulting new state object. This methods does not - * modify the given state but returns a new one. - * - * @params state The state to which to apply the update. - * @params update The update to apply. - * @return The resulting state. - */ - static CompressedState applyUpdate(VariableInformation const& variableInformation, CompressedState const& state, storm::prism::Update const& update, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator); - - /*! - * Applies an update to the given state and returns the resulting new state object. The update is evaluated - * over the variable values of the given base state. This methods does not modify the given state but - * returns a new one. - * - * @param state The state to which to apply the update. - * @param baseState The state used for evaluating the update. - * @param update The update to apply. - * @return The resulting state. - */ - static CompressedState applyUpdate(VariableInformation const& variableInformation, CompressedState const& state, CompressedState const& baseState, storm::prism::Update const& update, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator); + storm::expressions::SimpleValuation unpackStateIntoValuation(storm::storage::BitVector const& currentState); /*! * Retrieves the state id of the given state. If the state has not been encountered yet, it will be added to @@ -239,29 +223,8 @@ namespace storm { * @param internalStateInformation The information about the already explored part of the reachable state space. * @return A pair indicating whether the state was already discovered before and the state id of the state. */ - static IndexType getOrAddStateIndex(CompressedState const& state, InternalStateInformation& internalStateInformation, std::queue<storm::storage::BitVector>& stateQueue); + StateType getOrAddStateIndex(CompressedState const& state); - /*! - * Retrieves all commands that are labeled with the given label and enabled in the given state, grouped by - * modules. - * - * This function will iterate over all modules and retrieve all commands that are labeled with the given - * action and active (i.e. enabled) in the current state. The result is a list of lists of commands in which - * the inner lists contain all commands of exactly one module. If a module does not have *any* (including - * disabled) commands, there will not be a list of commands of that module in the result. If, however, the - * module has a command with a relevant label, but no enabled one, nothing is returned to indicate that there - * is no legal transition possible. - * - * @param The program in which to search for active commands. - * @param state The current state. - * @param actionIndex The index of the action label to select. - * @return A list of lists of active commands or nothing. - */ - static boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> getActiveCommandsByActionIndex(storm::prism::Program const& program,storm::expressions::ExpressionEvaluator<ValueType> const& evaluator, uint_fast64_t const& actionIndex); - - static std::vector<Choice<ValueType>> getUnlabeledTransitions(storm::prism::Program const& program, bool discreteTimeModel, InternalStateInformation& internalStateInformation, VariableInformation const& variableInformation, storm::storage::BitVector const& currentState, bool choiceLabeling, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator, std::queue<storm::storage::BitVector>& stateQueue, storm::utility::ConstantsComparator<ValueType> const& comparator); - - static std::vector<Choice<ValueType>> getLabeledTransitions(storm::prism::Program const& program, bool discreteTimeModel, InternalStateInformation& internalStateInformation, VariableInformation const& variableInformation, storm::storage::BitVector const& currentState, bool choiceLabeling, storm::expressions::ExpressionEvaluator<ValueType> const& evaluator, std::queue<storm::storage::BitVector>& stateQueue, storm::utility::ConstantsComparator<ValueType> const& comparator); /*! * Builds the transition matrix and the transition reward matrix based for the given program. * @@ -279,7 +242,7 @@ namespace storm { * @return A tuple containing a vector with all rows at which the nondeterministic choices of each state begin * and a vector containing the labels associated with each choice. */ - static boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> buildMatrices(storm::prism::Program const& program, VariableInformation const& variableInformation, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, bool commandLabels, bool deterministicModel, bool discreteTimeModel, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression); + boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression); /*! * Explores the state space of the given program and returns the components of the model as a result. @@ -289,7 +252,7 @@ namespace storm { * @param options A set of options used to customize the building process. * @return A structure containing the components of the resulting model. */ - ModelComponents buildModelComponents(storm::prism::Program const& program, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, Options const& options); + ModelComponents buildModelComponents(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels); /*! * Builds the state labeling for the given program. @@ -299,14 +262,27 @@ namespace storm { * @param internalStateInformation Information about the state space of the program. * @return The state labeling of the given program. */ - static storm::models::sparse::StateLabeling buildStateLabeling(storm::prism::Program const& program, VariableInformation const& variableInformation, InternalStateInformation const& internalStateInformation); + storm::models::sparse::StateLabeling buildStateLabeling(); + + // The program to translate. The necessary transformations are performed upon construction of the builder. + storm::prism::Program program; + + // The options to be used for translating the program. + Options options; + + // The variable information. + storm::generator::VariableInformation variableInformation; + + // Internal information about the states that were explored. + InternalStateInformation internalStateInformation; // This member holds information about reachable states that can be retrieved from the outside after a // successful build. boost::optional<StateInformation> stateInformation; - // This member holds the program that was most recently translated (if any). - boost::optional<storm::prism::Program> preparedProgram; + // A queue of states that still need to be explored. + std::queue<storm::storage::BitVector> statesToExplore; + }; } // namespace adapters diff --git a/src/generator/Choice.cpp b/src/generator/Choice.cpp index 3b6609ebd..8f42b7b3a 100644 --- a/src/generator/Choice.cpp +++ b/src/generator/Choice.cpp @@ -6,7 +6,7 @@ namespace storm { namespace generator { template<typename ValueType, typename StateType> - Choice<ValueType, StateType>::Choice(uint_fast64_t actionIndex, bool markovian) : markovian(markovian), actionIndex(actionIndex), distribution(), totalMass(storm::utility::zero<ValueType>()), choiceReward(storm::utility::zero<ValueType>()) { + Choice<ValueType, StateType>::Choice(uint_fast64_t actionIndex, bool markovian) : markovian(markovian), actionIndex(actionIndex), distribution(), totalMass(storm::utility::zero<ValueType>()), choiceRewards() { // Intentionally left empty. } @@ -89,7 +89,7 @@ namespace storm { template<typename ValueType, typename StateType> void Choice<ValueType, StateType>::addChoiceReward(ValueType const& value) { - choiceReward += value; + choiceRewards.push_back(value); } template<typename ValueType, typename StateType> diff --git a/src/generator/Choice.h b/src/generator/Choice.h index 07eb6efbe..78287d62e 100644 --- a/src/generator/Choice.h +++ b/src/generator/Choice.h @@ -142,8 +142,8 @@ namespace storm { // The total probability mass (or rates) of this choice. ValueType totalMass; - // The reward value associated with this choice. - ValueType choiceReward; + // The reward values associated with this choice. + std::vector<ValueType> choiceRewards; // The labels that are associated with this choice. boost::optional<LabelSet> choiceLabels; diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index 1fdc38cfb..cf89bd1e6 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -11,15 +11,15 @@ namespace storm { namespace generator { + typedef storm::storage::BitVector CompressedState; + template<typename ValueType, typename StateType = uint32_t> class NextStateGenerator { public: - typedef storm::storage::BitVector InternalStateType; - typedef StateType (*StateToIdCallback)(InternalStateType const&); - + typedef StateType (*StateToIdCallback)(CompressedState const&); + virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; - virtual std::vector<Choice<ValueType>> expand(StateType const& state, StateToIdCallback stateToIdCallback) = 0; - virtual ValueType getStateReward(StateType const& state) = 0; + virtual std::vector<Choice<ValueType>> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) = 0; }; } } diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index 0acf4d8dd..f49aa1b29 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -1,22 +1,263 @@ #include "src/generator/PrismNextStateGenerator.h" +#include "src/utility/constants.h" +#include "src/utility/macros.h" +#include "src/exceptions/WrongFormatException.h" + namespace storm { namespace generator { template<typename ValueType, typename StateType> - PrismNextStateGenerator<ValueType, StateType>::PrismNextStateGenerator(storm::prism::Program const& program) : program(program) { + PrismNextStateGenerator<ValueType, StateType>::PrismNextStateGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation, bool buildChoiceLabeling) : program(program), selectedRewardModels(), buildChoiceLabeling(buildChoiceLabeling), variableInformation(variableInformation), evaluator(), comparator() { // Intentionally left empty. } template<typename ValueType, typename StateType> - std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::expand(StateType const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) { - // TODO + void PrismNextStateGenerator<ValueType, StateType>::addRewardModel(storm::prism::RewardModel const& rewardModel) { + selectedRewardModels.push_back(rewardModel); } template<typename ValueType, typename StateType> - ValueType PrismNextStateGenerator<ValueType, StateType>::getStateReward(StateType const& state) { + std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) { // TODO } + template<typename ValueType, typename StateType> + void PrismNextStateGenerator<ValueType, StateType>::unpackStateIntoEvaluator(storm::storage::BitVector const& state) { + for (auto const& booleanVariable : variableInformation.booleanVariables) { + evaluator.setBooleanValue(booleanVariable.variable, state.get(booleanVariable.bitOffset)); + } + for (auto const& integerVariable : variableInformation.integerVariables) { + evaluator.setIntegerValue(integerVariable.variable, state.getAsInt(integerVariable.bitOffset, integerVariable.bitWidth) + integerVariable.lowerBound); + } + } + + template<typename ValueType, typename StateType> + CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) { + return applyUpdate(variableInformation, state, state, update); + } + + template<typename ValueType, typename StateType> + CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, CompressedState const& baseState, storm::prism::Update const& update) { + CompressedState newState(state); + + auto assignmentIt = update.getAssignments().begin(); + auto assignmentIte = update.getAssignments().end(); + + // Iterate over all boolean assignments and carry them out. + auto boolIt = variableInformation.booleanVariables.begin(); + for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasBooleanType(); ++assignmentIt) { + while (assignmentIt->getVariable() != boolIt->variable) { + ++boolIt; + } + newState.set(boolIt->bitOffset, evaluator.asBool(assignmentIt->getExpression())); + } + + // Iterate over all integer assignments and carry them out. + auto integerIt = variableInformation.integerVariables.begin(); + for (; assignmentIt != assignmentIte && assignmentIt->getExpression().hasIntegerType(); ++assignmentIt) { + while (assignmentIt->getVariable() != integerIt->variable) { + ++integerIt; + } + int_fast64_t assignedValue = evaluator.asInt(assignmentIt->getExpression()); + STORM_LOG_THROW(assignedValue <= integerIt->upperBound, storm::exceptions::WrongFormatException, "The update " << update << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '" << assignmentIt->getVariableName() << "'."); + newState.setFromInt(integerIt->bitOffset, integerIt->bitWidth, assignedValue - integerIt->lowerBound); + STORM_LOG_ASSERT(static_cast<int_fast64_t>(newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth)) + integerIt->lowerBound == assignedValue, "Writing to the bit vector bucket failed (read " << newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth) << " but wrote " << assignedValue << ")."); + } + + // Check that we processed all assignments. + STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed."); + + return newState; + } + + template<typename ValueType, typename StateType> + boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> PrismNextStateGenerator<ValueType, StateType>::getActiveCommandsByActionIndex(uint_fast64_t const& actionIndex) { + boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> result((std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>())); + + // Iterate over all modules. + for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { + storm::prism::Module const& module = program.getModule(i); + + // If the module has no command labeled with the given action, we can skip this module. + if (!module.hasActionIndex(actionIndex)) { + continue; + } + + std::set<uint_fast64_t> const& commandIndices = module.getCommandIndicesByActionIndex(actionIndex); + + // If the module contains the action, but there is no command in the module that is labeled with + // this action, we don't have any feasible command combinations. + if (commandIndices.empty()) { + return boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>>(); + } + + std::vector<std::reference_wrapper<storm::prism::Command const>> commands; + + // Look up commands by their indices and add them if the guard evaluates to true in the given state. + for (uint_fast64_t commandIndex : commandIndices) { + storm::prism::Command const& command = module.getCommand(commandIndex); + if (evaluator.asBool(command.getGuardExpression())) { + commands.push_back(command); + } + } + + // If there was no enabled command although the module has some command with the required action label, + // we must not return anything. + if (commands.size() == 0) { + return boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>>(); + } + + result.get().push_back(std::move(commands)); + } + + return result; + } + + template<typename ValueType, typename StateType> + std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getUnlabeledTransitions(CompressedState const& state, StateToIdCallback stateToIdCallback) { + std::vector<Choice<ValueType>> result; + + // Iterate over all modules. + for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { + storm::prism::Module const& module = program.getModule(i); + + // Iterate over all commands. + for (uint_fast64_t j = 0; j < module.getNumberOfCommands(); ++j) { + storm::prism::Command const& command = module.getCommand(j); + + // Only consider unlabeled commands. + if (command.isLabeled()) continue; + + // Skip the command, if it is not enabled. + if (!evaluator.asBool(command.getGuardExpression())) { + continue; + } + + result.push_back(Choice<ValueType>(0, buildChoiceLabeling)); + Choice<ValueType>& choice = result.back(); + + // Remember the command labels only if we were asked to. + if (buildChoiceLabeling) { + choice.addChoiceLabel(command.getGlobalIndex()); + } + + // Iterate over all updates of the current command. + ValueType probabilitySum = storm::utility::zero<ValueType>(); + for (uint_fast64_t k = 0; k < command.getNumberOfUpdates(); ++k) { + storm::prism::Update const& update = command.getUpdate(k); + + // Obtain target state index and add it to the list of known states. If it has not yet been + // seen, we also add it to the set of states that have yet to be explored. + StateType stateIndex = stateToIdCallback(applyUpdate(state, update)); + + // Update the choice by adding the probability/target state to it. + ValueType probability = evaluator.asRational(update.getLikelihoodExpression()); + choice.addProbability(stateIndex, probability); + probabilitySum += probability; + } + + // Check that the resulting distribution is in fact a distribution. + STORM_LOG_THROW(!program.isDiscreteTimeModel() || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for command '" << command << "' (actually sum to " << probabilitySum << ")."); + } + } + + return result; + } + + template<typename ValueType, typename StateType> + std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getLabeledTransitions(CompressedState const& state, StateToIdCallback stateToIdCallback) { + std::vector<Choice<ValueType>> result; + + for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) { + boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists = getActiveCommandsByActionIndex(program, evaluator, actionIndex); + + // Only process this action label, if there is at least one feasible solution. + if (optionalActiveCommandLists) { + std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>> const& activeCommandList = optionalActiveCommandLists.get(); + std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>::const_iterator> iteratorList(activeCommandList.size()); + + // Initialize the list of iterators. + for (size_t i = 0; i < activeCommandList.size(); ++i) { + iteratorList[i] = activeCommandList[i].cbegin(); + } + + // As long as there is one feasible combination of commands, keep on expanding it. + bool done = false; + while (!done) { + std::unordered_map<CompressedState, ValueType>* currentTargetStates = new std::unordered_map<CompressedState, ValueType>(); + std::unordered_map<CompressedState, ValueType>* newTargetStates = new std::unordered_map<CompressedState, ValueType>(); + currentTargetStates->emplace(state, storm::utility::one<ValueType>()); + + for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { + storm::prism::Command const& command = *iteratorList[i]; + + for (uint_fast64_t j = 0; j < command.getNumberOfUpdates(); ++j) { + storm::prism::Update const& update = command.getUpdate(j); + + for (auto const& stateProbabilityPair : *currentTargetStates) { + // Compute the new state under the current update and add it to the set of new target states. + CompressedState newTargetState = applyUpdate(stateProbabilityPair.first, state, update); + newTargetStates->emplace(newTargetState, stateProbabilityPair.second * evaluator.asRational(update.getLikelihoodExpression())); + } + } + + // If there is one more command to come, shift the target states one time step back. + if (i < iteratorList.size() - 1) { + delete currentTargetStates; + currentTargetStates = newTargetStates; + newTargetStates = new std::unordered_map<CompressedState, ValueType>(); + } + } + + // At this point, we applied all commands of the current command combination and newTargetStates + // contains all target states and their respective probabilities. That means we are now ready to + // add the choice to the list of transitions. + result.push_back(Choice<ValueType>(actionIndex, buildChoiceLabeling)); + + // Now create the actual distribution. + Choice<ValueType>& choice = result.back(); + + // Remember the command labels only if we were asked to. + if (buildChoiceLabeling) { + // Add the labels of all commands to this choice. + for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { + choice.addChoiceLabel(iteratorList[i]->get().getGlobalIndex()); + } + } + + ValueType probabilitySum = storm::utility::zero<ValueType>(); + for (auto const& stateProbabilityPair : *newTargetStates) { + StateType actualIndex = stateToIdCallback(stateProbabilityPair.first); + choice.addProbability(actualIndex, stateProbabilityPair.second); + probabilitySum += stateProbabilityPair.second; + } + + // Check that the resulting distribution is in fact a distribution. + STORM_LOG_THROW(!program.isDiscreteTimeModel() || !comparator.isConstant(probabilitySum) || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ")."); + + // Dispose of the temporary maps. + delete currentTargetStates; + delete newTargetStates; + + // Now, check whether there is one more command combination to consider. + bool movedIterator = false; + for (int_fast64_t j = iteratorList.size() - 1; j >= 0; --j) { + ++iteratorList[j]; + if (iteratorList[j] != activeCommandList[j].end()) { + movedIterator = true; + } else { + // Reset the iterator to the beginning of the list. + iteratorList[j] = activeCommandList[j].begin(); + } + } + + done = !movedIterator; + } + } + } + + return result; + } } } \ No newline at end of file diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index 1330d4420..f5fabe5f7 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -2,8 +2,10 @@ #define STORM_GENERATOR_PRISMNEXTSTATEGENERATOR_H_ #include "src/generator/NextStateGenerator.h" +#include "src/generator/VariableInformation.h" #include "src/storage/prism/Program.h" +#include "src/storage/expressions/ExpressionEvaluator.h" namespace storm { namespace generator { @@ -13,17 +15,97 @@ namespace storm { public: typedef typename NextStateGenerator<ValueType, StateType>::StateToIdCallback StateToIdCallback; - PrismNextStateGenerator(storm::prism::Program const& program); + PrismNextStateGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation, bool buildChoiceLabeling); - virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; - virtual std::vector<Choice<ValueType>> expand(StateType const& state, StateToIdCallback stateToIdCallback) override; - virtual ValueType getStateReward(StateType const& state) override; + /*! + * Adds a reward model to the list of selected reward models () + */ + void addRewardModel(storm::prism::RewardModel const& rewardModel); + virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; + virtual std::vector<Choice<ValueType>> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) override; + private: + /*! + * Unpacks the compressed state into the evaluator. + * + * @param state The state to unpack. + */ + void unpackStateIntoEvaluator(CompressedState const& state); + + /*! + * Applies an update to the given state and returns the resulting new state object. This methods does not + * modify the given state but returns a new one. + * + * @params state The state to which to apply the update. + * @params update The update to apply. + * @return The resulting state. + */ + CompressedState applyUpdate(CompressedState const& state, storm::prism::Update const& update); + + /*! + * Applies an update to the given state and returns the resulting new state object. The update is evaluated + * over the variable values of the given base state. This methods does not modify the given state but + * returns a new one. + * + * @param state The state to which to apply the update. + * @param baseState The state used for evaluating the update. + * @param update The update to apply. + * @return The resulting state. + */ + CompressedState applyUpdate(CompressedState const& state, CompressedState const& baseState, storm::prism::Update const& update); + + /*! + * Retrieves all commands that are labeled with the given label and enabled in the given state, grouped by + * modules. + * + * This function will iterate over all modules and retrieve all commands that are labeled with the given + * action and active (i.e. enabled) in the current state. The result is a list of lists of commands in which + * the inner lists contain all commands of exactly one module. If a module does not have *any* (including + * disabled) commands, there will not be a list of commands of that module in the result. If, however, the + * module has a command with a relevant label, but no enabled one, nothing is returned to indicate that there + * is no legal transition possible. + * + * @param The program in which to search for active commands. + * @param state The current state. + * @param actionIndex The index of the action label to select. + * @return A list of lists of active commands or nothing. + */ + boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> getActiveCommandsByActionIndex(uint_fast64_t const& actionIndex); + + /*! + * Retrieves all unlabeled choices possible from the given state. + * + * @param state The state for which to retrieve the unlabeled choices. + * @return The unlabeled choices of the state. + */ + std::vector<Choice<ValueType>> getUnlabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback); + + /*! + * Retrieves all labeled choices possible from the given state. + * + * @param state The state for which to retrieve the unlabeled choices. + * @return The labeled choices of the state. + */ + std::vector<Choice<ValueType>> getLabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback); + // The program used for the generation of next states. - storm::prism::Program program; + storm::prism::Program const& program; + + // The reward models that need to be considered. + std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels; + + // A flag that stores whether or not to build the choice labeling. + bool buildChoiceLabeling; + + // Information about how the variables are packed + VariableInformation const& variableInformation; + // An evaluator used to evaluate expressions. + storm::expressions::ExpressionEvaluator<ValueType> evaluator; + // A comparator used to compare constants. + storm::utility::ConstantsComparator<ValueType> comparator; }; } diff --git a/src/generator/VariableInformation.cpp b/src/generator/VariableInformation.cpp new file mode 100644 index 000000000..7f7ed357e --- /dev/null +++ b/src/generator/VariableInformation.cpp @@ -0,0 +1,42 @@ +#include "src/generator/prism/VariableInformation.h" + +#include "src/utility/macros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace generator { + + BooleanVariableInformation::BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset) : variable(variable), initialValue(initialValue), bitOffset(bitOffset) { + // Intentionally left empty. + } + + IntegerVariableInformation::IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth) : variable(variable), initialValue(initialValue), lowerBound(lowerBound), upperBound(upperBound), bitOffset(bitOffset), bitWidth(bitWidth) { + // Intentionally left empty. + } + + VariableInformation::VariableInformation(storm::expressions::ExpressionManager const& manager) : manager(manager) { + // Intentionally left empty. + } + + uint_fast64_t VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const { + auto const& booleanIndex = booleanVariableToIndexMap.find(variable); + if (booleanIndex != booleanVariableToIndexMap.end()) { + return booleanVariables[booleanIndex->second].bitOffset; + } + auto const& integerIndex = integerVariableToIndexMap.find(variable); + if (integerIndex != integerVariableToIndexMap.end()) { + return integerVariables[integerIndex->second].bitOffset; + } + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit index of unknown variable."); + } + + uint_fast64_t VariableInformation::getBitWidth(storm::expressions::Variable const& variable) const { + auto const& integerIndex = integerVariableToIndexMap.find(variable); + if (integerIndex != integerVariableToIndexMap.end()) { + return integerVariables[integerIndex->second].bitWidth; + } + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit width of unknown variable."); + } + + } +} \ No newline at end of file diff --git a/src/generator/VariableInformation.h b/src/generator/VariableInformation.h new file mode 100644 index 000000000..c1f06e741 --- /dev/null +++ b/src/generator/VariableInformation.h @@ -0,0 +1,71 @@ +#ifndef STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ +#define STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ + +#include <vector> +#include <boost/container/flat_map.hpp> + +#include "src/storage/expressions/Variable.h" + +namespace storm { + namespace generator { + + // A structure storing information about the boolean variables of the program. + struct BooleanVariableInformation { + BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset); + + // The boolean variable. + storm::expressions::Variable variable; + + // Its initial value. + bool initialValue; + + // Its bit offset in the compressed state. + uint_fast64_t bitOffset; + }; + + // A structure storing information about the integer variables of the program. + struct IntegerVariableInformation { + IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth); + + // The integer variable. + storm::expressions::Variable variable; + + // Its initial value. + int_fast64_t initialValue; + + // The lower bound of its range. + int_fast64_t lowerBound; + + // The upper bound of its range. + int_fast64_t upperBound; + + // Its bit offset in the compressed state. + uint_fast64_t bitOffset; + + // Its bit width in the compressed state. + uint_fast64_t bitWidth; + }; + + // A structure storing information about the used variables of the program. + struct VariableInformation { + VariableInformation(storm::expressions::ExpressionManager const& manager); + + // Provide methods to access the bit offset and width of variables in the compressed state. + uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const; + uint_fast64_t getBitWidth(storm::expressions::Variable const& variable) const; + + // The known boolean variables. + boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> booleanVariableToIndexMap; + std::vector<BooleanVariableInformation> booleanVariables; + + // The known integer variables. + boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> integerVariableToIndexMap; + std::vector<IntegerVariableInformation> integerVariables; + + storm::expressions::ExpressionManager const& manager; + }; + + } +} + +#endif /* STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ */ \ No newline at end of file diff --git a/src/generator/prism/VariableInformation.cpp b/src/generator/prism/VariableInformation.cpp deleted file mode 100644 index f648eda1b..000000000 --- a/src/generator/prism/VariableInformation.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "src/generator/prism/VariableInformation.h" - -#include "src/utility/macros.h" -#include "src/exceptions/InvalidArgumentException.h" - -namespace storm { - namespace generator { - namespace prism { - - BooleanVariableInformation::BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset) : variable(variable), initialValue(initialValue), bitOffset(bitOffset) { - // Intentionally left empty. - } - - IntegerVariableInformation::IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth) : variable(variable), initialValue(initialValue), lowerBound(lowerBound), upperBound(upperBound), bitOffset(bitOffset), bitWidth(bitWidth) { - // Intentionally left empty. - } - - VariableInformation::VariableInformation(storm::expressions::ExpressionManager const& manager) : manager(manager) { - // Intentionally left empty. - } - - uint_fast64_t VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const { - auto const& booleanIndex = booleanVariableToIndexMap.find(variable); - if (booleanIndex != booleanVariableToIndexMap.end()) { - return booleanVariables[booleanIndex->second].bitOffset; - } - auto const& integerIndex = integerVariableToIndexMap.find(variable); - if (integerIndex != integerVariableToIndexMap.end()) { - return integerVariables[integerIndex->second].bitOffset; - } - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit index of unknown variable."); - } - - uint_fast64_t VariableInformation::getBitWidth(storm::expressions::Variable const& variable) const { - auto const& integerIndex = integerVariableToIndexMap.find(variable); - if (integerIndex != integerVariableToIndexMap.end()) { - return integerVariables[integerIndex->second].bitWidth; - } - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot look-up bit width of unknown variable."); - } - - } - } -} \ No newline at end of file diff --git a/src/generator/prism/VariableInformation.h b/src/generator/prism/VariableInformation.h deleted file mode 100644 index 56ac7bda2..000000000 --- a/src/generator/prism/VariableInformation.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ -#define STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ - -#include <vector> -#include <boost/container/flat_map.hpp> - -#include "src/storage/expressions/Variable.h" - -namespace storm { - namespace generator { - namespace prism { - - // A structure storing information about the boolean variables of the program. - struct BooleanVariableInformation { - BooleanVariableInformation(storm::expressions::Variable const& variable, bool initialValue, uint_fast64_t bitOffset); - - // The boolean variable. - storm::expressions::Variable variable; - - // Its initial value. - bool initialValue; - - // Its bit offset in the compressed state. - uint_fast64_t bitOffset; - }; - - // A structure storing information about the integer variables of the program. - struct IntegerVariableInformation { - IntegerVariableInformation(storm::expressions::Variable const& variable, int_fast64_t initialValue, int_fast64_t lowerBound, int_fast64_t upperBound, uint_fast64_t bitOffset, uint_fast64_t bitWidth); - - // The integer variable. - storm::expressions::Variable variable; - - // Its initial value. - int_fast64_t initialValue; - - // The lower bound of its range. - int_fast64_t lowerBound; - - // The upper bound of its range. - int_fast64_t upperBound; - - // Its bit offset in the compressed state. - uint_fast64_t bitOffset; - - // Its bit width in the compressed state. - uint_fast64_t bitWidth; - }; - - // A structure storing information about the used variables of the program. - struct VariableInformation { - VariableInformation(storm::expressions::ExpressionManager const& manager); - - // Provide methods to access the bit offset and width of variables in the compressed state. - uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const; - uint_fast64_t getBitWidth(storm::expressions::Variable const& variable) const; - - // The known boolean variables. - boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> booleanVariableToIndexMap; - std::vector<BooleanVariableInformation> booleanVariables; - - // The known integer variables. - boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> integerVariableToIndexMap; - std::vector<IntegerVariableInformation> integerVariables; - - storm::expressions::ExpressionManager const& manager; - }; - - } - } -} - -#endif /* STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ */ \ No newline at end of file diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index bf8caf4a8..0f5aea4a8 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -77,6 +77,10 @@ namespace storm { return modelType; } + bool Program::isDiscreteTimeModel() const { + return modelType == ModelType::DTMC || modelType == ModelType::MDP; + } + bool Program::hasUndefinedConstants() const { for (auto const& constant : this->getConstants()) { if (!constant.isDefined()) { diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index 45ac16b62..1d091c294 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -66,6 +66,18 @@ namespace storm { * @return The type of the model. */ ModelType getModelType() const; + + /*! + * Retrieves whether the model is a discrete-time model, i.e. a DTMC or an MDP. + * + * @return True iff the model is a discrete-time model. + */ + bool isDiscreteTimeModel() const; + + /*! + * Retrieves whether the model is one without nondeterministic choices, i.e. a DTMC or a CTMC. + */ + bool isDeterministicModel() const; /*! * Retrieves whether there are undefined constants of any type in the program. From 865345c7bf69a3dd6c208aa016d55d0d03145ff8 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 24 Feb 2016 10:27:52 +0100 Subject: [PATCH 03/17] a little morning code Former-commit-id: 9cb63427c66ed48e3454d1e221a4a02cf60e5514 --- src/generator/NextStateGenerator.h | 4 +-- src/generator/PrismNextStateGenerator.cpp | 7 +---- src/generator/PrismNextStateGenerator.h | 21 +++----------- src/generator/StateBehavior.cpp | 17 +++++++++++ src/generator/StateBehavior.h | 35 +++++++++++++++++++++++ 5 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 src/generator/StateBehavior.cpp create mode 100644 src/generator/StateBehavior.h diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index cf89bd1e6..8e8e4bbdb 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -7,7 +7,7 @@ #include "src/storage/sparse/StateType.h" #include "src/storage/BitVector.h" -#include "src/generator/Choice.h" +#include "src/generator/StateBehavior.h" namespace storm { namespace generator { @@ -19,7 +19,7 @@ namespace storm { typedef StateType (*StateToIdCallback)(CompressedState const&); virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; - virtual std::vector<Choice<ValueType>> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) = 0; + virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) = 0; }; } } diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index f49aa1b29..a23ed29a6 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -18,7 +18,7 @@ namespace storm { } template<typename ValueType, typename StateType> - std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) { + StateBehavior<ValueType, StateType> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) { // TODO } @@ -34,11 +34,6 @@ namespace storm { template<typename ValueType, typename StateType> CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) { - return applyUpdate(variableInformation, state, state, update); - } - - template<typename ValueType, typename StateType> - CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, CompressedState const& baseState, storm::prism::Update const& update) { CompressedState newState(state); auto assignmentIt = update.getAssignments().begin(); diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index f5fabe5f7..d3660ce05 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -23,7 +23,7 @@ namespace storm { void addRewardModel(storm::prism::RewardModel const& rewardModel); virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; - virtual std::vector<Choice<ValueType>> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) override; + virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) override; private: /*! @@ -34,27 +34,14 @@ namespace storm { void unpackStateIntoEvaluator(CompressedState const& state); /*! - * Applies an update to the given state and returns the resulting new state object. This methods does not - * modify the given state but returns a new one. - * - * @params state The state to which to apply the update. + * Applies an update to the state currently loaded into the evaluator and applies the resulting values to + * the given compressed state. + * @params state The state to which to apply the new values. * @params update The update to apply. * @return The resulting state. */ CompressedState applyUpdate(CompressedState const& state, storm::prism::Update const& update); - /*! - * Applies an update to the given state and returns the resulting new state object. The update is evaluated - * over the variable values of the given base state. This methods does not modify the given state but - * returns a new one. - * - * @param state The state to which to apply the update. - * @param baseState The state used for evaluating the update. - * @param update The update to apply. - * @return The resulting state. - */ - CompressedState applyUpdate(CompressedState const& state, CompressedState const& baseState, storm::prism::Update const& update); - /*! * Retrieves all commands that are labeled with the given label and enabled in the given state, grouped by * modules. diff --git a/src/generator/StateBehavior.cpp b/src/generator/StateBehavior.cpp new file mode 100644 index 000000000..5868a1e40 --- /dev/null +++ b/src/generator/StateBehavior.cpp @@ -0,0 +1,17 @@ +#include "src/generator/StateBehavior.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType> + void StateBehavior<ValueType, StateType>::addChoice(Choice<ValueType, StateType>&& choice) { + choices.push_back(std::move(choice)); + } + + template<typename ValueType, typename StateType> + void StateBehavior<ValueType, StateType>::addStateReward(ValueType const& stateReward) { + stateRewards.push_back(stateReward); + } + + } +} \ No newline at end of file diff --git a/src/generator/StateBehavior.h b/src/generator/StateBehavior.h new file mode 100644 index 000000000..b4feecb34 --- /dev/null +++ b/src/generator/StateBehavior.h @@ -0,0 +1,35 @@ +#ifndef STORM_GENERATOR_PRISM_STATEBEHAVIOR_H_ +#define STORM_GENERATOR_PRISM_STATEBEHAVIOR_H_ + +#include <cstdint> + +#include "src/generator/Choice.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType = uint32_t> + class StateBehavior { + public: + /*! + * Adds the given choice to the behavior of the state. + */ + void addChoice(Choice<ValueType, StateType>&& choice); + + /*! + * Adds the given state reward to the behavior of the state. + */ + void addStateReward(ValueType const& stateReward); + + private: + // The choices available in the state. + std::vector<Choice<ValueType, StateType>> choices; + + // The state rewards (under the different, selected reward models) of the state. + std::vector<ValueType> stateRewards; + }; + + } +} + +#endif /* STORM_GENERATOR_PRISM_STATEBEHAVIOR_H_ */ From a75e0f5323f138c70384a54c1cc449c69e37c1c2 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 24 Feb 2016 18:58:25 +0100 Subject: [PATCH 04/17] more work wrt cleaner model exploration Former-commit-id: f24d618bdf08e22aa28f7f6baf045b56bc98e9d8 --- src/builder/ExplicitPrismModelBuilder.cpp | 184 ++++++++--------- src/builder/ExplicitPrismModelBuilder.h | 7 +- src/generator/NextStateGenerator.h | 8 +- src/generator/PrismNextStateGenerator.cpp | 136 ++++++++++++- src/generator/PrismNextStateGenerator.h | 7 +- src/generator/VariableInformation.cpp | 37 +++- src/generator/VariableInformation.h | 9 +- src/storage/BitVector.cpp | 229 ++++++++++------------ src/storage/BitVector.h | 7 +- 9 files changed, 386 insertions(+), 238 deletions(-) diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 039048111..4a777c6f9 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -11,6 +11,8 @@ #include "src/settings/modules/GeneralSettings.h" +#include "src/generator/PrismNextStateGenerator.h" + #include "src/utility/prism.h" #include "src/utility/macros.h" #include "src/utility/ConstantsComparator.h" @@ -37,7 +39,7 @@ namespace storm { stateRewardVector.resize(rowGroupCount); optionalStateRewardVector = std::move(stateRewardVector); } - + boost::optional<std::vector<ValueType>> optionalStateActionRewardVector; if (hasStateActionRewards) { stateActionRewardVector.resize(rowCount); @@ -75,12 +77,12 @@ namespace storm { ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), bitsPerState(bitsPerState), numberOfStates() { // Intentionally left empty. } - + template <typename ValueType, typename RewardModelType, typename StateType> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), rewardModels(), choiceLabeling() { // Intentionally left empty. } - + template <typename ValueType, typename RewardModelType, typename StateType> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options() : buildCommandLabels(false), buildAllRewardModels(true), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { // Intentionally left empty. @@ -135,7 +137,7 @@ namespace storm { } } } - + template <typename ValueType, typename RewardModelType, typename StateType> void ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::preserveFormula(storm::logic::Formula const& formula) { // If we already had terminal states, we need to erase them. @@ -180,7 +182,7 @@ namespace storm { } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : options(options), program(program) { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : options(options), program(program), variableInformation(program), internalStateInformation(variableInformation.getTotalBitOffset()) { // Start by defining the undefined constants in the model. if (options.constantDefinitions) { this->program = program.defineUndefinedConstants(options.constantDefinitions.get()); @@ -189,56 +191,48 @@ namespace storm { } // If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message. -#ifdef STORM_HAVE_CARL - // If the program either has undefined constants or we are building a parametric model, but the parameters - // not only appear in the probabilities, we re if (!std::is_same<ValueType, storm::RationalFunction>::value && this->program.hasUndefinedConstants()) { -#else - if (this->program->hasUndefinedConstants()) { -#endif - std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = this->program.getUndefinedConstants(); - std::stringstream stream; - bool printComma = false; - for (auto const& constant : undefinedConstants) { - if (printComma) { - stream << ", "; - } else { - printComma = true; - } - stream << constant.get().getName() << " (" << constant.get().getType() << ")"; + std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = this->program.getUndefinedConstants(); + std::stringstream stream; + bool printComma = false; + for (auto const& constant : undefinedConstants) { + if (printComma) { + stream << ", "; + } else { + printComma = true; } - stream << "."; - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); -#ifdef STORM_HAVE_CARL - } else if (std::is_same<ValueType, storm::RationalFunction>::value && !this->program.hasUndefinedConstantsOnlyInUpdateProbabilitiesAndRewards()) { - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, which is not admitted."); -#endif + stream << constant.get().getName() << " (" << constant.get().getType() << ")"; } - - // If the set of labels we are supposed to built is restricted, we need to remove the other labels from the program. - if (options.labelsToBuild) { - if (!options.buildAllLabels) { - this->program.filterLabels(options.labelsToBuild.get()); - } + stream << "."; + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); + } else if (std::is_same<ValueType, storm::RationalFunction>::value && !this->program.hasUndefinedConstantsOnlyInUpdateProbabilitiesAndRewards()) { + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, which is not admitted."); + } + + // If the set of labels we are supposed to built is restricted, we need to remove the other labels from the program. + if (options.labelsToBuild) { + if (!options.buildAllLabels) { + this->program.filterLabels(options.labelsToBuild.get()); } + } + + // If we need to build labels for expressions that may appear in some formula, we need to add appropriate + // labels to the program. + if (options.expressionLabels) { + std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = this->program.getConstantsSubstitution(); - // If we need to build labels for expressions that may appear in some formula, we need to add appropriate - // labels to the program. - if (options.expressionLabels) { - std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = this->program.getConstantsSubstitution(); - - for (auto const& expression : options.expressionLabels.get()) { - std::stringstream stream; - stream << expression.substitute(constantsSubstitution); - std::string name = stream.str(); - if (!this->program.hasLabel(name)) { - this->program.addLabel(name, expression); - } + for (auto const& expression : options.expressionLabels.get()) { + std::stringstream stream; + stream << expression.substitute(constantsSubstitution); + std::string name = stream.str(); + if (!this->program.hasLabel(name)) { + this->program.addLabel(name, expression); } } - - // Now that the program is fixed, we we need to substitute all constants with their concrete value. - this->program = program.substituteConstants(); + } + + // Now that the program is fixed, we we need to substitute all constants with their concrete value. + this->program = program.substituteConstants(); } template <typename ValueType, typename RewardModelType, typename StateType> @@ -255,15 +249,15 @@ namespace storm { template <typename ValueType, typename RewardModelType, typename StateType> std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::translate() { STORM_LOG_DEBUG("Building representation of program:" << std::endl << *program << std::endl); - + // Select the appropriate reward models (after the constants have been substituted). std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels; - + // First, we make sure that all selected reward models actually exist. for (auto const& rewardModelName : options.rewardModelsToBuild) { STORM_LOG_THROW(rewardModelName.empty() || program.hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'."); } - + for (auto const& rewardModel : program.getRewardModels()) { if (options.buildAllRewardModels || options.rewardModelsToBuild.find(rewardModel.getName()) != options.rewardModelsToBuild.end()) { selectedRewardModels.push_back(rewardModel); @@ -274,7 +268,7 @@ namespace storm { if (selectedRewardModels.empty() && program.getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") { selectedRewardModels.push_back(program.getRewardModel(0)); } - + ModelComponents modelComponents = buildModelComponents(*program, selectedRewardModels, options); std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> result; @@ -295,10 +289,10 @@ namespace storm { return result; } - + template <typename ValueType, typename RewardModelType, typename StateType> storm::expressions::SimpleValuation ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::unpackStateIntoValuation(storm::storage::BitVector const& currentState) { - storm::expressions::SimpleValuation result(variableInformation.manager.getSharedPointer()); + storm::expressions::SimpleValuation result(program.getManager().getSharedPointer()); for (auto const& booleanVariable : variableInformation.booleanVariables) { result.setBooleanValue(booleanVariable.variable, currentState.get(booleanVariable.bitOffset)); } @@ -322,17 +316,54 @@ namespace storm { return actualIndexBucketPair.first; } - + template <typename ValueType, typename RewardModelType, typename StateType> - boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression) { + boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression) { // Create choice labels, if requested, boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> choiceLabels; if (options.buildCommandLabels) { choiceLabels = std::vector<boost::container::flat_set<uint_fast64_t>>(); } + + // Create a generator that is able to expand states. + storm::generator::PrismNextStateGenerator<ValueType, StateType> generator(program, variableInformation, options.buildCommandLabels); + for (auto const& rewardModel : selectedRewardModels) { + generator.addRewardModel(rewardModel.get()); + } + + // Create a callback for the next-state generator to enable it to request the index of states. + std::function<StateType (CompressedState const&)> stateToIdCallback = std::bind1st(&ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this); - // A comparator that can be used to check whether we actually have distributions. - storm::utility::ConstantsComparator<ValueType> comparator; + // Let the generator create all initial states. + generator.getInitialStates(stateToIdCallback); + + // Now explore the current state until there is no more reachable state. + uint_fast64_t currentRow = 0; + + // Perform a DFS through the model. + while (!statesToExplore.empty()) { + // Get the first state in the queue. + std::pair<CompressedState, StateType> currentState = internalStateInformation.stateStorage.getBucketAndValue(statesToExplore.front()); + statesToExplore.pop(); + + STORM_LOG_TRACE("Exploring state with id " << currentState.second << "."); + + bool deadlockOnPurpose = false; + if (terminalExpression && evaluator.asBool(terminalExpression.get())) { + // Nothing to do in this case. + deadlockOnPurpose = true; + } else { + // Retrieve all choices for the current state. + allUnlabeledChoices = getUnlabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); + allLabeledChoices = getLabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); + } + + + } + + + + // Initialize a queue and insert the initial state. std::queue<storm::storage::BitVector> stateQueue; @@ -420,7 +451,7 @@ namespace storm { std::cout << unpackStateIntoValuation(currentState, variableInformation).toString(true) << std::endl; STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Error while creating sparse matrix from probabilistic program: found deadlock state. For fixing these, please provide the appropriate option."); - + } } else if (totalNumberOfChoices == 1) { if (!deterministicModel) { @@ -468,7 +499,7 @@ namespace storm { // or compose them to one choice. if (deterministicModel) { Choice<ValueType> globalChoice; - + // We need to prepare the entries of those vectors that are going to be used. auto builderIt = rewardModelBuilders.begin(); for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { @@ -571,7 +602,7 @@ namespace storm { } else { // If the model is nondeterministic, we add all choices individually. transitionMatrixBuilder.newRowGroup(currentRow); - + auto builderIt = rewardModelBuilders.begin(); for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { if (rewardModelIt->get().hasStateRewards()) { @@ -651,36 +682,7 @@ namespace storm { typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents(storm::prism::Program const& program, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, Options const& options) { ModelComponents modelComponents; - uint_fast64_t bitOffset = 0; - VariableInformation variableInformation(program.getManager()); - for (auto const& booleanVariable : program.getGlobalBooleanVariables()) { - variableInformation.booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), bitOffset); - ++bitOffset; - variableInformation.booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = variableInformation.booleanVariables.size() - 1; - } - for (auto const& integerVariable : program.getGlobalIntegerVariables()) { - int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt(); - int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt(); - uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1))); - variableInformation.integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, bitOffset, bitwidth); - bitOffset += bitwidth; - variableInformation.integerVariableToIndexMap[integerVariable.getExpressionVariable()] = variableInformation.integerVariables.size() - 1; - } - for (auto const& module : program.getModules()) { - for (auto const& booleanVariable : module.getBooleanVariables()) { - variableInformation.booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), bitOffset); - ++bitOffset; - variableInformation.booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = variableInformation.booleanVariables.size() - 1; - } - for (auto const& integerVariable : module.getIntegerVariables()) { - int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt(); - int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt(); - uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1))); - variableInformation.integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, bitOffset, bitwidth); - bitOffset += bitwidth; - variableInformation.integerVariableToIndexMap[integerVariable.getExpressionVariable()] = variableInformation.integerVariables.size() - 1; - } - } + VariableInformation variableInformation(program); // Create the structure for storing the reachable state space. uint64_t bitsPerState = ((bitOffset / 64) + 1) * 64; diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index 5e4f5b2c5..21c45e30b 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -242,7 +242,7 @@ namespace storm { * @return A tuple containing a vector with all rows at which the nondeterministic choices of each state begin * and a vector containing the labels associated with each choice. */ - boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression); + boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression); /*! * Explores the state space of the given program and returns the components of the model as a result. @@ -280,8 +280,9 @@ namespace storm { // successful build. boost::optional<StateInformation> stateInformation; - // A queue of states that still need to be explored. - std::queue<storm::storage::BitVector> statesToExplore; + // A queue of states that still need to be explored. The indices in this queue are the bucket indices in the + // bit vector hash map holding the compressed states. + std::queue<std::size_t> statesToExplore; }; diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index 8e8e4bbdb..4248ab90a 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -16,10 +16,10 @@ namespace storm { template<typename ValueType, typename StateType = uint32_t> class NextStateGenerator { public: - typedef StateType (*StateToIdCallback)(CompressedState const&); - - virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; - virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) = 0; + typedef std::function<StateType (CompressedState const&)> StateToIdCallback; + + virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) = 0; + virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) = 0; }; } } diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index a23ed29a6..602abb678 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -15,13 +15,108 @@ namespace storm { template<typename ValueType, typename StateType> void PrismNextStateGenerator<ValueType, StateType>::addRewardModel(storm::prism::RewardModel const& rewardModel) { selectedRewardModels.push_back(rewardModel); + hasStateActionRewards |= rewardModel.hasStateActionRewards(); } template<typename ValueType, typename StateType> - StateBehavior<ValueType, StateType> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) { - // TODO + std::vector<StateType> PrismNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) { + // FIXME, TODO, whatever } + template<typename ValueType, typename StateType> + StateBehavior<ValueType, StateType> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) { + // Start by unpacking the state into the evaluator so we can quickly evaluate expressions later. + unpackStateIntoEvaluator(state); + + // Prepare the result, in case we return early. + StateBehavior<ValueType, StateType> result; + + // First, construct the state rewards, as we may return early if there are no choices later and we already + // need the state rewards then. + for (auto const& rewardModel : selectedRewardModels) { + ValueType stateReward = storm::utility::zero<ValueType>(); + if (rewardModel->hasStateRewards()) { + for (auto const& stateReward : rewardModel->getStateRewards()) { + if (evaluator.asBool(stateReward.getStatePredicateExpression())) { + stateReward += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); + } + } + } + result.addStateReward(stateReward); + } + + // Get all choices for the state. + std::vector<Choice<ValueType>> allChoices = getUnlabeledChoices(state, stateToIdCallback); + std::vector<Choice<ValueType>> allLabeledChoices = getLabeledChoices(state, stateToIdCallback); + for (auto& choice : allLabeledChoices) { + allChoices.push_back(std::move(choice)); + } + + std::size_t totalNumberOfChoices = allChoices.size(); + + // If there is not a single choice, we return immediately, because the state has no behavior (other than + // the state reward). + if (totalNumberOfChoices == 0) { + return result; + } + + // If the model is a deterministic model, we need to fuse the choices into one. + if (program.isDeterministicModel() && totalNumberOfChoices > 1) { + Choice<ValueType> globalChoice; + + // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs + // this is equal to the number of choices, which is why we initialize it like this here. + ValueType totalExitRate = static_cast<ValueType>(totalNumberOfChoices); + + // Iterate over all choices and combine the probabilities/rates into one choice. + for (auto const& choice : allChoices) { + for (auto const& stateProbabilityPair : choice) { + if (program.isDiscreteTimeModel()) { + globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second / totalNumberOfChoices; + if (hasStateActionRewards) { + totalExitRate += choice.getTotalMass(); + } + } else { + globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second; + } + } + + if (buildChoiceLabeling) { + globalChoice.addChoiceLabels(choice.getChoiceLabels()); + } + } + + // Now construct the state-action reward for all selected reward models. + for (auto const& rewardModel : selectedRewardModels) { + ValueType stateActionReward = storm::utility::zero<ValueType>(); + if (rewardModel->hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel->getStateActionRewards()) { + for (auto const& choice : allChoices) { + if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { + stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) / totalExitRate; + } + } + + } + } + globalChoice.addChoiceReward(stateActionReward); + } + + // Move the newly fused choice in place. + allChoices.clear(); + allChoices.push_back(std::move(globalChoice)); + } + + // Move all remaining choices in place. + for (auto& choice : allChoices) { + result.addChoice(std::move(choice)); + } + + return result; + } + + + template<typename ValueType, typename StateType> void PrismNextStateGenerator<ValueType, StateType>::unpackStateIntoEvaluator(storm::storage::BitVector const& state) { for (auto const& booleanVariable : variableInformation.booleanVariables) { @@ -31,7 +126,7 @@ namespace storm { evaluator.setIntegerValue(integerVariable.variable, state.getAsInt(integerVariable.bitOffset, integerVariable.bitWidth) + integerVariable.lowerBound); } } - + template<typename ValueType, typename StateType> CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) { CompressedState newState(state); @@ -110,7 +205,7 @@ namespace storm { } template<typename ValueType, typename StateType> - std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getUnlabeledTransitions(CompressedState const& state, StateToIdCallback stateToIdCallback) { + std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getUnlabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback) { std::vector<Choice<ValueType>> result; // Iterate over all modules. @@ -129,7 +224,7 @@ namespace storm { continue; } - result.push_back(Choice<ValueType>(0, buildChoiceLabeling)); + result.push_back(Choice<ValueType>(command.getActionIndex())); Choice<ValueType>& choice = result.back(); // Remember the command labels only if we were asked to. @@ -152,6 +247,19 @@ namespace storm { probabilitySum += probability; } + // Create the state-action reward for the newly created choice. + for (auto const& rewardModel : selectedRewardModels) { + ValueType stateActionReward = storm::utility::zero<ValueType>(); + if (rewardModel->hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel->getStateActionRewards()) { + if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { + stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass(); + } + } + } + choice.addChoiceReward(stateActionReward); + } + // Check that the resulting distribution is in fact a distribution. STORM_LOG_THROW(!program.isDiscreteTimeModel() || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for command '" << command << "' (actually sum to " << probabilitySum << ")."); } @@ -161,7 +269,7 @@ namespace storm { } template<typename ValueType, typename StateType> - std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getLabeledTransitions(CompressedState const& state, StateToIdCallback stateToIdCallback) { + std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getLabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback) { std::vector<Choice<ValueType>> result; for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) { @@ -208,7 +316,7 @@ namespace storm { // At this point, we applied all commands of the current command combination and newTargetStates // contains all target states and their respective probabilities. That means we are now ready to // add the choice to the list of transitions. - result.push_back(Choice<ValueType>(actionIndex, buildChoiceLabeling)); + result.push_back(Choice<ValueType>(actionIndex)); // Now create the actual distribution. Choice<ValueType>& choice = result.back(); @@ -221,6 +329,7 @@ namespace storm { } } + // Add the probabilities/rates to the newly created choice. ValueType probabilitySum = storm::utility::zero<ValueType>(); for (auto const& stateProbabilityPair : *newTargetStates) { StateType actualIndex = stateToIdCallback(stateProbabilityPair.first); @@ -231,6 +340,19 @@ namespace storm { // Check that the resulting distribution is in fact a distribution. STORM_LOG_THROW(!program.isDiscreteTimeModel() || !comparator.isConstant(probabilitySum) || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ")."); + // Create the state-action reward for the newly created choice. + for (auto const& rewardModel : selectedRewardModels) { + ValueType stateActionReward = storm::utility::zero<ValueType>(); + if (rewardModel->hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel->getStateActionRewards()) { + if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { + stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass(); + } + } + } + choice.addChoiceReward(stateActionReward); + } + // Dispose of the temporary maps. delete currentTargetStates; delete newTargetStates; diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index d3660ce05..282a2f25f 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -22,8 +22,8 @@ namespace storm { */ void addRewardModel(storm::prism::RewardModel const& rewardModel); - virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0; - virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) override; + virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; + virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) override; private: /*! @@ -82,6 +82,9 @@ namespace storm { // The reward models that need to be considered. std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels; + // A flag that stores whether at least one of the selected reward models has state-action rewards. + bool hasStateActionRewards; + // A flag that stores whether or not to build the choice labeling. bool buildChoiceLabeling; diff --git a/src/generator/VariableInformation.cpp b/src/generator/VariableInformation.cpp index 7f7ed357e..bb20c5225 100644 --- a/src/generator/VariableInformation.cpp +++ b/src/generator/VariableInformation.cpp @@ -1,4 +1,4 @@ -#include "src/generator/prism/VariableInformation.h" +#include "src/generator/VariableInformation.h" #include "src/utility/macros.h" #include "src/exceptions/InvalidArgumentException.h" @@ -14,8 +14,39 @@ namespace storm { // Intentionally left empty. } - VariableInformation::VariableInformation(storm::expressions::ExpressionManager const& manager) : manager(manager) { - // Intentionally left empty. + VariableInformation::VariableInformation(storm::prism::Program const& program) : totalBitOffset(0) { + for (auto const& booleanVariable : program.getGlobalBooleanVariables()) { + booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), totalBitOffset); + ++totalBitOffset; + booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = booleanVariables.size() - 1; + } + for (auto const& integerVariable : program.getGlobalIntegerVariables()) { + int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt(); + int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt(); + uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1))); + integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, totalBitOffset, bitwidth); + totalBitOffset += bitwidth; + integerVariableToIndexMap[integerVariable.getExpressionVariable()] = integerVariables.size() - 1; + } + for (auto const& module : program.getModules()) { + for (auto const& booleanVariable : module.getBooleanVariables()) { + booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), totalBitOffset); + ++totalBitOffset; + booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = booleanVariables.size() - 1; + } + for (auto const& integerVariable : module.getIntegerVariables()) { + int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt(); + int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt(); + uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1))); + integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, totalBitOffset, bitwidth); + totalBitOffset += bitwidth; + integerVariableToIndexMap[integerVariable.getExpressionVariable()] = integerVariables.size() - 1; + } + } + } + + uint_fast64_t VariableInformation::getTotalBitOffset() const { + return totalBitOffset; } uint_fast64_t VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const { diff --git a/src/generator/VariableInformation.h b/src/generator/VariableInformation.h index c1f06e741..a06156336 100644 --- a/src/generator/VariableInformation.h +++ b/src/generator/VariableInformation.h @@ -5,6 +5,7 @@ #include <boost/container/flat_map.hpp> #include "src/storage/expressions/Variable.h" +#include "src/storage/prism/Program.h" namespace storm { namespace generator { @@ -48,12 +49,16 @@ namespace storm { // A structure storing information about the used variables of the program. struct VariableInformation { - VariableInformation(storm::expressions::ExpressionManager const& manager); + VariableInformation(storm::prism::Program const& program); + uint_fast64_t getTotalBitOffset() const; // Provide methods to access the bit offset and width of variables in the compressed state. uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const; uint_fast64_t getBitWidth(storm::expressions::Variable const& variable) const; + // The total bit offset over all variables. + uint_fast64_t totalBitOffset; + // The known boolean variables. boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> booleanVariableToIndexMap; std::vector<BooleanVariableInformation> booleanVariables; @@ -61,8 +66,6 @@ namespace storm { // The known integer variables. boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> integerVariableToIndexMap; std::vector<IntegerVariableInformation> integerVariables; - - storm::expressions::ExpressionManager const& manager; }; } diff --git a/src/storage/BitVector.cpp b/src/storage/BitVector.cpp index cd5f46009..38b94d852 100644 --- a/src/storage/BitVector.cpp +++ b/src/storage/BitVector.cpp @@ -60,7 +60,7 @@ namespace storm { return currentIndex == other.currentIndex; } - BitVector::BitVector() : bitCount(0), bucketVector() { + BitVector::BitVector() : bitCount(0), buckets(nullptr) { // Intentionally left empty. } @@ -73,10 +73,17 @@ namespace storm { // Initialize the storage with the required values. if (init) { - bucketVector = std::vector<uint64_t>(bucketCount, -1ll); + buckets = new uint64_t[bucketCount]; + std::fill_n(buckets, bucketCount, -1ull); truncateLastBucket(); } else { - bucketVector = std::vector<uint64_t>(bucketCount, 0); + buckets = new uint64_t[bucketCount](); + } + } + + BitVector::~BitVector() { + if (buckets != nullptr) { + delete buckets; } } @@ -89,23 +96,22 @@ namespace storm { // Intentionally left empty. } - BitVector::BitVector(uint_fast64_t bucketCount, uint_fast64_t bitCount) : bitCount(bitCount), bucketVector(bucketCount) { + BitVector::BitVector(uint_fast64_t bucketCount, uint_fast64_t bitCount) : bitCount(bitCount) { STORM_LOG_ASSERT((bucketCount << 6) == bitCount, "Bit count does not match number of buckets."); + buckets = new uint64_t[bucketCount](); } - BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount), bucketVector(other.bucketVector) { - // Intentionally left empty. + BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount) { + buckets = new uint64_t[other.bucketCount()]; + std::copy_n(other.buckets, other.bucketCount(), buckets); } - //BitVector::BitVector(BitVector&& other) : bitCount(other.bitCount), bucketVector(std::move(other.bucketVector)) { - // Intentionally left empty. - //} - BitVector& BitVector::operator=(BitVector const& other) { // Only perform the assignment if the source and target are not identical. if (this != &other) { bitCount = other.bitCount; - bucketVector = std::vector<uint64_t>(other.bucketVector); + buckets = new uint64_t[other.bucketCount()]; + std::copy_n(other.buckets, other.bucketCount(), buckets); } return *this; } @@ -117,9 +123,9 @@ namespace storm { return false; } - std::vector<uint64_t>::const_iterator first1 = this->bucketVector.begin(); - std::vector<uint64_t>::const_iterator last1 = this->bucketVector.end(); - std::vector<uint64_t>::const_iterator first2 = other.bucketVector.begin(); + uint64_t* first1 = this->buckets; + uint64_t* last1 = this->buckets + this->bucketCount(); + uint64_t* first2 = other.buckets; for (; first1 != last1; ++first1, ++first2) { if (*first1 < *first2) { @@ -135,7 +141,8 @@ namespace storm { // Only perform the assignment if the source and target are not identical. if (this != &other) { bitCount = other.bitCount; - bucketVector = std::move(other.bucketVector); + this->buckets = other.buckets; + other.buckets = nullptr; } return *this; @@ -146,14 +153,7 @@ namespace storm { if (this->bitCount != other.bitCount) return false; // If the lengths match, we compare the buckets one by one. - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) { - if (*it1 != *it2) { - return false; - } - } - - // All buckets were equal, so the bit vectors are equal. - return true; + return std::equal(this->buckets, this->buckets + this->bucketCount(), other.buckets); } bool BitVector::operator!=(BitVector const& other) const { @@ -166,9 +166,9 @@ namespace storm { uint64_t mask = 1ull << (63 - (index & mod64mask)); if (value) { - bucketVector[bucket] |= mask; + buckets[bucket] |= mask; } else { - bucketVector[bucket] &= ~mask; + buckets[bucket] &= ~mask; } } @@ -182,7 +182,7 @@ namespace storm { bool BitVector::operator[](uint_fast64_t index) const { uint64_t bucket = index >> 6; uint64_t mask = 1ull << (63 - (index & mod64mask)); - return (this->bucketVector[bucket] & mask) == mask; + return (this->buckets[bucket] & mask) == mask; } bool BitVector::get(uint_fast64_t index) const { @@ -197,89 +197,76 @@ namespace storm { ++newBucketCount; } - if (newBucketCount > bucketVector.size()) { + if (newBucketCount > this->bucketCount()) { + uint64_t* newBuckets = new uint64_t[newBucketCount]; + std::copy_n(buckets, this->bucketCount(), newBuckets); if (init) { - bucketVector.back() |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull); - bucketVector.resize(newBucketCount, -1ull); + newBuckets[this->bucketCount() - 1] |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull); + std::fill_n(newBuckets, newBucketCount - this->bucketCount(), -1ull); } else { - bucketVector.resize(newBucketCount, 0); + std::fill_n(newBuckets, newBucketCount - this->bucketCount(), 0); } + delete buckets; + buckets = newBuckets; bitCount = newLength; } else { // If the underlying storage does not need to grow, we have to insert the missing bits. if (init) { - bucketVector.back() |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull); + buckets[this->bucketCount() - 1] |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull); } bitCount = newLength; } truncateLastBucket(); } else { - bitCount = newLength; uint_fast64_t newBucketCount = newLength >> 6; if ((newLength & mod64mask) != 0) { ++newBucketCount; } - bucketVector.resize(newBucketCount); + // If the number of buckets needs to be reduced, we resize it now. Otherwise, we can just truncate the + // last bucket. + if (newBucketCount < this->bucketCount()) { + uint64_t* newBuckets = new uint64_t[newBucketCount]; + std::copy_n(buckets, newBucketCount, newBuckets); + delete buckets; + buckets = newBuckets; + bitCount = newLength; + } + bitCount = newLength; truncateLastBucket(); } } BitVector BitVector::operator&(BitVector const& other) const { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - BitVector result(bitCount); - std::vector<uint64_t>::iterator it = result.bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) { - *it = *it1 & *it2; - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return a & b; }); return result; } BitVector& BitVector::operator&=(BitVector const& other) { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - - std::vector<uint64_t>::iterator it = bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) { - *it &= *otherIt; - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, this->buckets, [] (uint64_t const& a, uint64_t const& b) { return a & b; }); return *this; } BitVector BitVector::operator|(BitVector const& other) const { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - BitVector result(bitCount); - std::vector<uint64_t>::iterator it = result.bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) { - *it = *it1 | *it2; - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return a | b; }); return result; } BitVector& BitVector::operator|=(BitVector const& other) { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - - std::vector<uint64_t>::iterator it = bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) { - *it |= *otherIt; - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, this->buckets, [] (uint64_t const& a, uint64_t const& b) { return a | b; }); return *this; } BitVector BitVector::operator^(BitVector const& other) const { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - BitVector result(bitCount); - std::vector<uint64_t>::iterator it = result.bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) { - *it = *it1 ^ *it2; - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return a ^ b; }); result.truncateLastBucket(); return result; } @@ -314,19 +301,13 @@ namespace storm { BitVector BitVector::operator~() const { BitVector result(this->bitCount); - std::vector<uint64_t>::iterator it = result.bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it) { - *it = ~(*it1); - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), result.buckets, [] (uint64_t const& a) { return ~a; }); result.truncateLastBucket(); return result; } void BitVector::complement() { - for (auto& element : bucketVector) { - element = ~element; - } + std::transform(this->buckets, this->buckets + this->bucketCount(), this->buckets, [] (uint64_t const& a) { return ~a; }); truncateLastBucket(); } @@ -334,11 +315,7 @@ namespace storm { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); BitVector result(bitCount); - std::vector<uint64_t>::iterator it = result.bucketVector.begin(); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) { - *it = ~(*it1) | *it2; - } - + std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return (~a | b); }); result.truncateLastBucket(); return result; } @@ -346,7 +323,11 @@ namespace storm { bool BitVector::isSubsetOf(BitVector const& other) const { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) { + uint64_t const* it1 = buckets; + uint64_t const* ite1 = buckets + bucketCount(); + uint64_t const* it2 = other.buckets; + + for (; it1 != ite1; ++it1, ++it2) { if ((*it1 & *it2) != *it1) { return false; } @@ -357,7 +338,11 @@ namespace storm { bool BitVector::isDisjointFrom(BitVector const& other) const { STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match."); - for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) { + uint64_t const* it1 = buckets; + uint64_t const* ite1 = buckets + bucketCount(); + uint64_t const* it2 = other.buckets; + + for (; it1 != ite1; ++it1, ++it2) { if ((*it1 & *it2) != 0) { return false; } @@ -372,9 +357,9 @@ namespace storm { // Compute the first bucket that needs to be checked and the number of buckets. uint64_t index = bitIndex >> 6; - std::vector<uint64_t>::const_iterator first1 = bucketVector.begin() + index; - std::vector<uint64_t>::const_iterator first2 = other.bucketVector.begin(); - std::vector<uint64_t>::const_iterator last2 = other.bucketVector.end(); + uint64_t const* first1 = buckets + index; + uint64_t const* first2 = other.buckets; + uint64_t const* last2 = other.buckets + other.bucketCount(); for (; first2 != last2; ++first1, ++first2) { if (*first1 != *first2) { @@ -391,10 +376,10 @@ namespace storm { // Compute the first bucket that needs to be checked and the number of buckets. uint64_t index = bitIndex >> 6; - std::vector<uint64_t>::iterator first1 = bucketVector.begin() + index; - std::vector<uint64_t>::const_iterator first2 = other.bucketVector.begin(); - std::vector<uint64_t>::const_iterator last2 = other.bucketVector.end(); - + uint64_t* first1 = buckets + index; + uint64_t const* first2 = other.buckets; + uint64_t const* last2 = other.buckets + other.bucketCount(); + for (; first2 != last2; ++first1, ++first2) { *first1 = *first2; } @@ -406,8 +391,8 @@ namespace storm { STORM_LOG_ASSERT(index + numberOfBuckets <= this->bucketCount(), "Argument is out-of-range."); storm::storage::BitVector result(numberOfBuckets, numberOfBits); - std::copy(this->bucketVector.begin() + index, this->bucketVector.begin() + index + numberOfBuckets, result.bucketVector.begin()); - + std::copy(this->buckets + index, this->buckets + index + numberOfBuckets, result.buckets); + result.truncateLastBucket(); return result; } @@ -425,10 +410,10 @@ namespace storm { if (bitIndexInBucket + numberOfBits < 64) { // If the value stops before the end of the bucket, we need to erase some lower bits. mask &= ~((1ull << (64 - (bitIndexInBucket + numberOfBits))) - 1ull); - return (bucketVector[bucket] & mask) >> (64 - (bitIndexInBucket + numberOfBits)); + return (buckets[bucket] & mask) >> (64 - (bitIndexInBucket + numberOfBits)); } else if (bitIndexInBucket + numberOfBits > 64) { // In this case, the integer "crosses" the bucket line. - uint64_t result = (bucketVector[bucket] & mask); + uint64_t result = (buckets[bucket] & mask); ++bucket; // Compute the remaining number of bits. @@ -440,13 +425,13 @@ namespace storm { // Strip away everything from the second bucket that is beyond the final index and add it to the // intermediate result. mask = ~((1ull << (64 - numberOfBits)) - 1ull); - uint64_t lowerBits = bucketVector[bucket] & mask; + uint64_t lowerBits = buckets[bucket] & mask; result |= (lowerBits >> (64 - numberOfBits)); return result; } else { // In this case, it suffices to take the current mask. - return bucketVector[bucket] & mask; + return buckets[bucket] & mask; } } @@ -466,10 +451,10 @@ namespace storm { if (bitIndexInBucket + numberOfBits < 64) { // If the value stops before the end of the bucket, we need to erase some lower bits. mask &= ~((1ull << (64 - (bitIndexInBucket + numberOfBits))) - 1ull); - bucketVector[bucket] = (bucketVector[bucket] & ~mask) | (value << (64 - (bitIndexInBucket + numberOfBits))); + buckets[bucket] = (buckets[bucket] & ~mask) | (value << (64 - (bitIndexInBucket + numberOfBits))); } else if (bitIndexInBucket + numberOfBits > 64) { // Write the part of the value that falls into the first bucket. - bucketVector[bucket] = (bucketVector[bucket] & ~mask) | (value >> (numberOfBits + (bitIndexInBucket - 64))); + buckets[bucket] = (buckets[bucket] & ~mask) | (value >> (numberOfBits + (bitIndexInBucket - 64))); ++bucket; // Compute the remaining number of bits. @@ -480,45 +465,41 @@ namespace storm { // Put the remaining bits in their place. mask = ((1ull << (64 - numberOfBits)) - 1ull); - bucketVector[bucket] = (bucketVector[bucket] & mask) | value; + buckets[bucket] = (buckets[bucket] & mask) | value; } else { - bucketVector[bucket] = (bucketVector[bucket] & ~mask) | value; + buckets[bucket] = (buckets[bucket] & ~mask) | value; } } bool BitVector::empty() const { - for (auto& element : bucketVector) { - if (element != 0) { - return false; - } - } - return true; + uint64_t* last = buckets + bucketCount(); + uint64_t* it = std::find(buckets, last, 0); + return it != last; } bool BitVector::full() const { // Check that all buckets except the last one have all bits set. - for (uint_fast64_t index = 0; index < bucketVector.size() - 1; ++index) { - if (bucketVector[index] != -1ull) { + uint64_t* last = buckets + bucketCount() - 1; + for (uint64_t const* it = buckets; it != last; ++it) { + if (*it != -1ull) { return false; } } // Now check whether the relevant bits are set in the last bucket. uint64_t mask = ~((1ull << (64 - (bitCount & mod64mask))) - 1ull); - if ((bucketVector.back() & mask) != mask) { + if ((*last & mask) != mask) { return false; } return true; } void BitVector::clear() { - for (auto& element : bucketVector) { - element = 0; - } + std::fill_n(buckets, this->bucketCount(), 0); } uint_fast64_t BitVector::getNumberOfSetBits() const { - return getNumberOfSetBitsBeforeIndex(bucketVector.size() << 6); + return getNumberOfSetBitsBeforeIndex(bitCount); } uint_fast64_t BitVector::getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const { @@ -529,14 +510,14 @@ namespace storm { for (uint_fast64_t i = 0; i < bucket; ++i) { // Check if we are using g++ or clang++ and, if so, use the built-in function #if (defined (__GNUG__) || defined(__clang__)) - result += __builtin_popcountll(bucketVector[i]); + result += __builtin_popcountll(buckets[i]); #elif defined WINDOWS #include <nmmintrin.h> // If the target machine does not support SSE4, this will fail. result += _mm_popcnt_u64(bucketVector[i]); #else uint_fast32_t cnt; - uint_fast64_t bitset = bucketVector[i]; + uint_fast64_t bitset = buckets[i]; for (cnt = 0; bitset; cnt++) { bitset &= bitset - 1; } @@ -548,7 +529,7 @@ namespace storm { uint64_t tmp = index & mod64mask; if (tmp != 0) { tmp = ~((1ll << (64 - (tmp & mod64mask))) - 1ll); - tmp &= bucketVector[bucket]; + tmp &= buckets[bucket]; // Check if we are using g++ or clang++ and, if so, use the built-in function #if (defined (__GNUG__) || defined(__clang__)) result += __builtin_popcountll(tmp); @@ -585,19 +566,19 @@ namespace storm { } std::size_t BitVector::getSizeInBytes() const { - return sizeof (*this) + sizeof (uint64_t) * bucketVector.size(); + return sizeof (*this) + sizeof (uint64_t) * bucketCount(); } BitVector::const_iterator BitVector::begin() const { - return const_iterator(bucketVector.data(), 0, bitCount); + return const_iterator(buckets, 0, bitCount); } BitVector::const_iterator BitVector::end() const { - return const_iterator(bucketVector.data(), bitCount, bitCount, false); + return const_iterator(buckets, bitCount, bitCount, false); } uint_fast64_t BitVector::getNextSetIndex(uint_fast64_t startingIndex) const { - return getNextSetIndex(bucketVector.data(), startingIndex, bitCount); + return getNextSetIndex(buckets, startingIndex, bitCount); } uint_fast64_t BitVector::getNextSetIndex(uint64_t const* dataPtr, uint_fast64_t startingIndex, uint_fast64_t endIndex) { @@ -641,17 +622,17 @@ namespace storm { void BitVector::truncateLastBucket() { if ((bitCount & mod64mask) != 0) { - bucketVector.back() &= ~((1ll << (64 - (bitCount & mod64mask))) - 1ll); + buckets[bucketCount() - 1] &= ~((1ll << (64 - (bitCount & mod64mask))) - 1ll); } } size_t BitVector::bucketCount() const { - return bucketVector.size(); + return bitCount >> 6; } - std::ostream& operator<<(std::ostream& out, BitVector const& bitVector) { - out << "bit vector(" << bitVector.getNumberOfSetBits() << "/" << bitVector.bitCount << ") ["; - for (auto index : bitVector) { + std::ostream& operator<<(std::ostream& out, BitVector const& bitvector) { + out << "bit vector(" << bitvector.getNumberOfSetBits() << "/" << bitvector.bitCount << ") ["; + for (auto index : bitvector) { out << index << " "; } out << "]"; @@ -659,13 +640,13 @@ namespace storm { return out; } - std::size_t NonZeroBitVectorHash::operator()(storm::storage::BitVector const& bv) const { - STORM_LOG_ASSERT(bv.size() > 0, "Cannot hash bit vector of zero size."); + std::size_t NonZeroBitVectorHash::operator()(storm::storage::BitVector const& bitvector) const { + STORM_LOG_ASSERT(bitvector.size() > 0, "Cannot hash bit vector of zero size."); std::size_t result = 0; - for (uint_fast64_t index = 0; index < bv.bucketVector.size(); ++index) { + for (uint_fast64_t index = 0; index < bitvector.bucketCount(); ++index) { result ^= result << 3; - result ^= result >> bv.getAsInt(index << 6, 5); + result ^= result >> bitvector.getAsInt(index << 6, 5); } // Erase the last bit and add one to definitely make this hash value non-zero. diff --git a/src/storage/BitVector.h b/src/storage/BitVector.h index f9c088ceb..a6ad89cc3 100644 --- a/src/storage/BitVector.h +++ b/src/storage/BitVector.h @@ -104,6 +104,11 @@ namespace storm { */ BitVector(); + /* + * Deconstructs a bit vector by deleting the underlying storage. + */ + ~BitVector(); + /*! * Constructs a bit vector which can hold the given number of bits and initializes all bits with the * provided truth value. @@ -494,7 +499,7 @@ namespace storm { uint_fast64_t bitCount; // The underlying storage of 64-bit buckets for all bits of this bit vector. - std::vector<uint64_t> bucketVector; + uint64_t* buckets; // A bit mask that can be used to reduce a modulo 64 operation to a logical "and". static const uint_fast64_t mod64mask = (1 << 6) - 1; From 9eec5b140c88d6d6ae180d22854676b06329a274 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Thu, 25 Feb 2016 18:27:23 +0100 Subject: [PATCH 05/17] refactoring of model builder Former-commit-id: f049f5a5bf4d759caae6a037cc12ca5ae56bfda0 --- src/builder/ExplicitPrismModelBuilder.cpp | 394 ++++------------------ src/builder/ExplicitPrismModelBuilder.h | 18 +- src/generator/Choice.cpp | 5 + src/generator/Choice.h | 5 + src/generator/CompressedState.cpp | 22 ++ src/generator/CompressedState.h | 30 ++ src/generator/NextStateGenerator.h | 9 +- src/generator/PrismNextStateGenerator.cpp | 49 ++- src/generator/PrismNextStateGenerator.h | 18 +- src/generator/StateBehavior.cpp | 35 ++ src/generator/StateBehavior.h | 38 +++ src/generator/VariableInformation.h | 6 +- 12 files changed, 264 insertions(+), 365 deletions(-) create mode 100644 src/generator/CompressedState.cpp create mode 100644 src/generator/CompressedState.h diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 4a777c6f9..232d7f263 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -14,6 +14,7 @@ #include "src/generator/PrismNextStateGenerator.h" #include "src/utility/prism.h" +#include "src/utility/constants.h" #include "src/utility/macros.h" #include "src/utility/ConstantsComparator.h" #include "src/exceptions/WrongFormatException.h" @@ -29,7 +30,7 @@ namespace storm { template <typename ValueType> struct RewardModelBuilder { public: - RewardModelBuilder(bool deterministicModel, bool hasStateRewards, bool hasStateActionRewards, bool hasTransitionRewards) : hasStateRewards(hasStateRewards), hasStateActionRewards(hasStateActionRewards), hasTransitionRewards(hasTransitionRewards), stateRewardVector(), stateActionRewardVector(), transitionRewardMatrixBuilder(0, 0, 0, false, !deterministicModel, 0) { + RewardModelBuilder(bool deterministicModel, bool hasStateRewards, bool hasStateActionRewards, bool hasTransitionRewards) : hasStateRewards(hasStateRewards), hasStateActionRewards(hasStateActionRewards), hasTransitionRewards(hasTransitionRewards), stateRewardVector(), stateActionRewardVector() { // Intentionally left empty. } @@ -46,12 +47,7 @@ namespace storm { optionalStateActionRewardVector = std::move(stateActionRewardVector); } - boost::optional<storm::storage::SparseMatrix<ValueType>> optionalTransitionRewardMatrix; - if (hasTransitionRewards) { - optionalTransitionRewardMatrix = transitionRewardMatrixBuilder.build(rowCount, columnCount, rowGroupCount); - } - - return storm::models::sparse::StandardRewardModel<ValueType>(std::move(optionalStateRewardVector), std::move(optionalStateActionRewardVector), std::move(optionalTransitionRewardMatrix)); + return storm::models::sparse::StandardRewardModel<ValueType>(std::move(optionalStateRewardVector), std::move(optionalStateActionRewardVector)); } bool hasStateRewards; @@ -63,9 +59,6 @@ namespace storm { // The state-action reward vector. std::vector<ValueType> stateActionRewardVector; - - // A builder that is used for constructing the transition reward matrix. - storm::storage::SparseMatrixBuilder<ValueType> transitionRewardMatrixBuilder; }; template <typename ValueType, typename RewardModelType, typename StateType> @@ -182,7 +175,7 @@ namespace storm { } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : options(options), program(program), variableInformation(program), internalStateInformation(variableInformation.getTotalBitOffset()) { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : program(program), options(options), variableInformation(program), internalStateInformation(variableInformation.getTotalBitOffset()) { // Start by defining the undefined constants in the model. if (options.constantDefinitions) { this->program = program.defineUndefinedConstants(options.constantDefinitions.get()); @@ -248,7 +241,7 @@ namespace storm { template <typename ValueType, typename RewardModelType, typename StateType> std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::translate() { - STORM_LOG_DEBUG("Building representation of program:" << std::endl << *program << std::endl); + STORM_LOG_DEBUG("Building representation of program:" << std::endl << program << std::endl); // Select the appropriate reward models (after the constants have been substituted). std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels; @@ -269,7 +262,7 @@ namespace storm { selectedRewardModels.push_back(program.getRewardModel(0)); } - ModelComponents modelComponents = buildModelComponents(*program, selectedRewardModels, options); + ModelComponents modelComponents = buildModelComponents(selectedRewardModels); std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> result; switch (program.getModelType()) { @@ -304,13 +297,13 @@ namespace storm { template <typename ValueType, typename RewardModelType, typename StateType> StateType ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex(CompressedState const& state) { - uint32_t newIndex = internalStateInformation.reachableStates.size(); + uint32_t newIndex = internalStateInformation.numberOfStates; // Check, if the state was already registered. std::pair<uint32_t, std::size_t> actualIndexBucketPair = internalStateInformation.stateStorage.findOrAddAndGetBucket(state, newIndex); if (actualIndexBucketPair.first == newIndex) { - statesToExplore.push(state); + statesToExplore.push_back(state); ++internalStateInformation.numberOfStates; } @@ -327,12 +320,15 @@ namespace storm { // Create a generator that is able to expand states. storm::generator::PrismNextStateGenerator<ValueType, StateType> generator(program, variableInformation, options.buildCommandLabels); + if (terminalExpression) { + generator.setTerminalExpression(terminalExpression.get()); + } for (auto const& rewardModel : selectedRewardModels) { generator.addRewardModel(rewardModel.get()); } // Create a callback for the next-state generator to enable it to request the index of states. - std::function<StateType (CompressedState const&)> stateToIdCallback = std::bind1st(&ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this); + std::function<StateType (CompressedState const&)> stateToIdCallback = std::bind(&ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this, std::placeholders::_1); // Let the generator create all initial states. generator.getInitialStates(stateToIdCallback); @@ -340,358 +336,108 @@ namespace storm { // Now explore the current state until there is no more reachable state. uint_fast64_t currentRow = 0; - // Perform a DFS through the model. + // Perform a search through the model. while (!statesToExplore.empty()) { // Get the first state in the queue. - std::pair<CompressedState, StateType> currentState = internalStateInformation.stateStorage.getBucketAndValue(statesToExplore.front()); - statesToExplore.pop(); - - STORM_LOG_TRACE("Exploring state with id " << currentState.second << "."); - - bool deadlockOnPurpose = false; - if (terminalExpression && evaluator.asBool(terminalExpression.get())) { - // Nothing to do in this case. - deadlockOnPurpose = true; - } else { - // Retrieve all choices for the current state. - allUnlabeledChoices = getUnlabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); - allLabeledChoices = getLabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); - } - - - } - - - - - - // Initialize a queue and insert the initial state. - std::queue<storm::storage::BitVector> stateQueue; - CompressedState initialState(internalStateInformation.bitsPerState); - - // We need to initialize the values of the variables to their initial value. - for (auto const& booleanVariable : variableInformation.booleanVariables) { - initialState.set(booleanVariable.bitOffset, booleanVariable.initialValue); - } - for (auto const& integerVariable : variableInformation.integerVariables) { - initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth, static_cast<uint_fast64_t>(integerVariable.initialValue - integerVariable.lowerBound)); - } - - // At this point, we determine whether there are reward models with state-action rewards, because we might - // want to know that quickly later on. - bool hasStateActionRewards = false; - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt) { - if (rewardModelIt->get().hasStateActionRewards()) { - hasStateActionRewards = true; - break; - } - } - - // Insert the initial state in the global state to index mapping and state queue. - uint32_t stateIndex = getOrAddStateIndex(initialState, internalStateInformation, stateQueue); - internalStateInformation.initialStateIndices.push_back(stateIndex); - - // Now explore the current state until there is no more reachable state. - uint_fast64_t currentRow = 0; - - // The evaluator used to determine the truth value of guards and predicates in the *current* state. - storm::expressions::ExpressionEvaluator<ValueType> evaluator(program.getManager()); - - // Perform a BFS through the model. - while (!stateQueue.empty()) { - // Get the current state and unpack it. - storm::storage::BitVector currentState = stateQueue.front(); - stateQueue.pop(); - StateType stateIndex = internalStateInformation.stateStorage.getValue(currentState); - STORM_LOG_TRACE("Exploring state with id " << stateIndex << "."); - unpackStateIntoEvaluator(currentState, variableInformation, evaluator); + CompressedState currentState = statesToExplore.front(); + StateType currentIndex = internalStateInformation.stateStorage.getValue(currentState); + statesToExplore.pop_front(); - // If a terminal expression was given, we check whether the current state needs to be explored further. - std::vector<Choice<ValueType>> allUnlabeledChoices; - std::vector<Choice<ValueType>> allLabeledChoices; - bool deadlockOnPurpose = false; - if (terminalExpression && evaluator.asBool(terminalExpression.get())) { - // Nothing to do in this case. - deadlockOnPurpose = true; - } else { - // Retrieve all choices for the current state. - allUnlabeledChoices = getUnlabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); - allLabeledChoices = getLabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator); - } + STORM_LOG_TRACE("Exploring state with id " << index << "."); - uint_fast64_t totalNumberOfChoices = allUnlabeledChoices.size() + allLabeledChoices.size(); + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(currentState, stateToIdCallback); - // If the current state does not have a single choice, we equip it with a self-loop if that was - // requested and issue an error otherwise. - if (totalNumberOfChoices == 0) { - if (!storm::settings::generalSettings().isDontFixDeadlocksSet() || deadlockOnPurpose) { - if (commandLabels) { + // If there is no behavior, we might have to introduce a self-loop. + if (behavior.empty()) { + if (!storm::settings::generalSettings().isDontFixDeadlocksSet() || !behavior.wasExpanded()) { + if (options.buildCommandLabels) { // Insert empty choice labeling for added self-loop transitions. choiceLabels.get().push_back(boost::container::flat_set<uint_fast64_t>()); } - if (!deterministicModel) { + if (!generator.isDeterministicModel()) { transitionMatrixBuilder.newRowGroup(currentRow); } - transitionMatrixBuilder.addNextValue(currentRow, stateIndex, storm::utility::one<ValueType>()); + transitionMatrixBuilder.addNextValue(currentRow, currentIndex, storm::utility::one<ValueType>()); auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateRewards()) { + for (auto const& rewardModel : selectedRewardModels) { + if (rewardModel.get().hasStateRewards()) { builderIt->stateRewardVector.push_back(storm::utility::zero<ValueType>()); } - if (rewardModelIt->get().hasStateActionRewards()) { + if (rewardModel.get().hasStateActionRewards()) { builderIt->stateActionRewardVector.push_back(storm::utility::zero<ValueType>()); } + ++builderIt; } ++currentRow; } else { - std::cout << unpackStateIntoValuation(currentState, variableInformation).toString(true) << std::endl; + std::cout << unpackStateIntoValuation(currentState).toString(true) << std::endl; STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Error while creating sparse matrix from probabilistic program: found deadlock state. For fixing these, please provide the appropriate option."); - } - } else if (totalNumberOfChoices == 1) { - if (!deterministicModel) { - transitionMatrixBuilder.newRowGroup(currentRow); - } - - bool labeledChoice = allUnlabeledChoices.empty() ? true : false; - Choice<ValueType> const& globalChoice = labeledChoice ? allLabeledChoices.front() : allUnlabeledChoices.front(); - + } else { + // Add the state rewards to the corresponding reward models. auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateRewards()) { - builderIt->stateRewardVector.push_back(storm::utility::zero<ValueType>()); - for (auto const& stateReward : rewardModelIt->get().getStateRewards()) { - if (evaluator.asBool(stateReward.getStatePredicateExpression())) { - builderIt->stateRewardVector.back() += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); - } - } - } - - if (rewardModelIt->get().hasStateActionRewards()) { - builderIt->stateActionRewardVector.push_back(storm::utility::zero<ValueType>()); - for (auto const& stateActionReward : rewardModelIt->get().getStateActionRewards()) { - if ((labeledChoice && stateActionReward.isLabeled() && stateActionReward.getActionIndex() == globalChoice.getActionIndex()) || (!labeledChoice && !stateActionReward.isLabeled())) { - if (evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - builderIt->stateActionRewardVector.back() += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())); - } - } - } + auto stateRewardIt = behavior.getStateRewards().begin(); + for (auto const& rewardModel : selectedRewardModels) { + if (rewardModel.get().hasStateRewards()) { + builderIt->stateRewardVector.push_back(*stateRewardIt); } + ++stateRewardIt; + ++builderIt; } - for (auto const& stateProbabilityPair : globalChoice) { - transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); - } - - if (commandLabels) { - // Now add the resulting distribution as the only choice of the current state. - choiceLabels.get().push_back(globalChoice.getChoiceLabels()); + // If the model is nondeterministic, we need to open a row group. + if (!generator.isDeterministicModel()) { + transitionMatrixBuilder.newRowGroup(currentRow); } - ++currentRow; - } else { - // Then, based on whether the model is deterministic or not, either add the choices individually - // or compose them to one choice. - if (deterministicModel) { - Choice<ValueType> globalChoice; - - // We need to prepare the entries of those vectors that are going to be used. - auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateRewards()) { - builderIt->stateRewardVector.push_back(storm::utility::zero<ValueType>()); - for (auto const& stateReward : rewardModelIt->get().getStateRewards()) { - if (evaluator.asBool(stateReward.getStatePredicateExpression())) { - builderIt->stateRewardVector.back() += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); - } - } - } - - if (rewardModelIt->get().hasStateActionRewards()) { - builderIt->stateActionRewardVector.push_back(storm::utility::zero<ValueType>()); - } - } - - // If there is one state-action reward model, we need to scale the rewards according to the - // multiple choices. - ValueType totalExitMass = storm::utility::zero<ValueType>(); - if (hasStateActionRewards) { - if (discreteTimeModel) { - totalExitMass = static_cast<ValueType>(totalNumberOfChoices); - } else { - // In the CTMC, we need to compute the exit rate of the state here, sin - for (auto const& choice : allUnlabeledChoices) { - totalExitMass += choice.getTotalMass(); - } - for (auto const& choice : allLabeledChoices) { - totalExitMass += choice.getTotalMass(); - } - } - } - - // Combine all the choices and scale them with the total number of choices of the current state. - for (auto const& choice : allUnlabeledChoices) { - if (commandLabels) { - globalChoice.addChoiceLabels(choice.getChoiceLabels()); - } - - auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateActionRewards()) { - for (auto const& stateActionReward : rewardModelIt->get().getStateActionRewards()) { - if (!stateActionReward.isLabeled()) { - if (evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - builderIt->stateActionRewardVector.back() += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass() / totalExitMass; - } - } - } - } - } - - for (auto const& stateProbabilityPair : choice) { - if (discreteTimeModel) { - globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second / totalNumberOfChoices; - } else { - globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second; - } - } - } - for (auto const& choice : allLabeledChoices) { - if (commandLabels) { - globalChoice.addChoiceLabels(choice.getChoiceLabels()); - } - - auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateActionRewards()) { - for (auto const& stateActionReward : rewardModelIt->get().getStateActionRewards()) { - if (stateActionReward.isLabeled() && stateActionReward.getActionIndex() == choice.getActionIndex()) { - if (evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - builderIt->stateActionRewardVector.back() += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass() / totalExitMass; - } - } - } - } - } - - for (auto const& stateProbabilityPair : choice) { - if (discreteTimeModel) { - globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second / totalNumberOfChoices; - } else { - globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second; - } - } + // Now add all choices. + for (auto const& choice : behavior) { + // Add command labels if requested. + if (options.buildCommandLabels) { + choiceLabels.get().push_back(choice.getChoiceLabels()); } - - if (commandLabels) { - // Now add the resulting distribution as the only choice of the current state. - choiceLabels.get().push_back(globalChoice.getChoiceLabels()); - } - - for (auto const& stateProbabilityPair : globalChoice) { + // Add the probabilistic behavior to the matrix. + for (auto const& stateProbabilityPair : behavior) { transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); } - ++currentRow; - } else { - // If the model is nondeterministic, we add all choices individually. - transitionMatrixBuilder.newRowGroup(currentRow); - + // Add the rewards to the reward models. auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateRewards()) { - builderIt->stateRewardVector.push_back(storm::utility::zero<ValueType>()); - - for (auto const& stateReward : rewardModelIt->get().getStateRewards()) { - if (evaluator.asBool(stateReward.getStatePredicateExpression())) { - builderIt->stateRewardVector.back() += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); - } - } + auto choiceRewardIt = behavior.getChoiceRewards().begin(); + for (auto const& rewardModel : selectedRewardModels) { + if (rewardModel.get().hasStateActionRewards()) { + builderIt->stateActionRewardVector.push_back(*choiceRewardIt); } + ++choiceRewardIt; + ++builderIt; } - // First, process all unlabeled choices. - for (auto const& choice : allUnlabeledChoices) { - std::map<uint_fast64_t, ValueType> stateToRewardMap; - if (commandLabels) { - choiceLabels.get().emplace_back(std::move(choice.getChoiceLabels())); - } - - auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateActionRewards()) { - builderIt->stateActionRewardVector.push_back(storm::utility::zero<ValueType>()); - for (auto const& stateActionReward : rewardModelIt->get().getStateActionRewards()) { - if (!stateActionReward.isLabeled()) { - if (evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - builderIt->stateActionRewardVector.back() += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())); - } - } - } - } - } - - for (auto const& stateProbabilityPair : choice) { - transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); - } - - ++currentRow; - } - - // Then, process all labeled choices. - for (auto const& choice : allLabeledChoices) { - std::map<uint_fast64_t, ValueType> stateToRewardMap; - if (commandLabels) { - choiceLabels.get().emplace_back(std::move(choice.getChoiceLabels())); - } - - auto builderIt = rewardModelBuilders.begin(); - for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) { - if (rewardModelIt->get().hasStateActionRewards()) { - builderIt->stateActionRewardVector.push_back(storm::utility::zero<ValueType>()); - for (auto const& stateActionReward : rewardModelIt->get().getStateActionRewards()) { - if (stateActionReward.isLabeled() && stateActionReward.getActionIndex() == choice.getActionIndex()) { - if (evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - builderIt->stateActionRewardVector.back() += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())); - } - } - } - } - } - - for (auto const& stateProbabilityPair : choice) { - transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); - } - - ++currentRow; - } + ++currentRow; } } } - + return choiceLabels; } template <typename ValueType, typename RewardModelType, typename StateType> - typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents(storm::prism::Program const& program, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, Options const& options) { + typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels) { ModelComponents modelComponents; - VariableInformation variableInformation(program); - // Create the structure for storing the reachable state space. - uint64_t bitsPerState = ((bitOffset / 64) + 1) * 64; + uint64_t bitsPerState = ((variableInformation.getTotalBitOffset() / 64) + 1) * 64; InternalStateInformation internalStateInformation(bitsPerState); // Determine whether we have to combine different choices to one or whether this model can have more than // one choice per state. - bool deterministicModel = program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::CTMC; - bool discreteTimeModel = program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::MDP; + bool deterministicModel = program.isDeterministicModel(); + bool discreteTimeModel = program.isDiscreteTimeModel(); // Prepare the transition matrix builder and the reward model builders. storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); @@ -741,13 +487,13 @@ namespace storm { } // Build the state labeling. - modelComponents.stateLabeling = buildStateLabeling(program, variableInformation, internalStateInformation); + modelComponents.stateLabeling = buildStateLabeling(); // Finally -- if requested -- build the state information that can be retrieved from the outside. if (options.buildStateInformation) { - stateInformation = StateInformation(internalStateInformation.reachableStates.size()); + stateInformation = StateInformation(internalStateInformation.numberOfStates); for (auto const& bitVectorIndexPair : internalStateInformation.stateStorage) { - stateInformation.get().valuations[bitVectorIndexPair.second] = unpackStateIntoValuation(bitVectorIndexPair.first, variableInformation); + stateInformation.get().valuations[bitVectorIndexPair.second] = unpackStateIntoValuation(bitVectorIndexPair.first); } } @@ -755,23 +501,23 @@ namespace storm { } template <typename ValueType, typename RewardModelType, typename StateType> - storm::models::sparse::StateLabeling ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildStateLabeling(storm::prism::Program const& program, VariableInformation const& variableInformation, InternalStateInformation const& internalStateInformation) { - storm::expressions::ExpressionEvaluator<ValueType> evaluator(program.getManager()); - + storm::models::sparse::StateLabeling ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildStateLabeling() { std::vector<storm::prism::Label> const& labels = program.getLabels(); - storm::models::sparse::StateLabeling result(internalStateInformation.reachableStates.size()); + storm::expressions::ExpressionEvaluator<ValueType> evaluator(program.getManager()); + storm::models::sparse::StateLabeling result(internalStateInformation.numberOfStates); // Initialize labeling. for (auto const& label : labels) { result.addLabel(label.getName()); } - for (uint_fast64_t index = 0; index < internalStateInformation.reachableStates.size(); index++) { - unpackStateIntoEvaluator(internalStateInformation.reachableStates[index], variableInformation, evaluator); + for (auto const& stateIndexPair : internalStateInformation.stateStorage) { + unpackStateIntoEvaluator(stateIndexPair.first, variableInformation, evaluator); + for (auto const& label : labels) { // Add label to state, if the corresponding expression is true. if (evaluator.asBool(label.getStatePredicateExpression())) { - result.addLabelToState(label.getName(), index); + result.addLabelToState(label.getName(), stateIndexPair.second); } } } diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index 21c45e30b..54fe9cf30 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -4,7 +4,7 @@ #include <memory> #include <utility> #include <vector> -#include <queue> +#include <deque> #include <cstdint> #include <boost/functional/hash.hpp> #include <boost/container/flat_set.hpp> @@ -22,9 +22,9 @@ #include "src/storage/SparseMatrix.h" #include "src/settings/SettingsManager.h" - #include "src/utility/prism.h" +#include "src/generator/CompressedState.h" #include "src/generator/VariableInformation.h" namespace storm { @@ -35,6 +35,7 @@ namespace storm { namespace builder { using namespace storm::utility::prism; + using namespace storm::generator; // Forward-declare classes. template <typename ValueType> struct RewardModelBuilder; @@ -42,8 +43,6 @@ namespace storm { template<typename ValueType, typename RewardModelType = storm::models::sparse::StandardRewardModel<ValueType>, typename StateType = uint32_t> class ExplicitPrismModelBuilder { public: - typedef storm::storage::BitVector CompressedState; - // A structure holding information about the reachable state space while building it. struct InternalStateInformation { InternalStateInformation(uint64_t bitsPerState); @@ -271,7 +270,7 @@ namespace storm { Options options; // The variable information. - storm::generator::VariableInformation variableInformation; + VariableInformation variableInformation; // Internal information about the states that were explored. InternalStateInformation internalStateInformation; @@ -280,9 +279,12 @@ namespace storm { // successful build. boost::optional<StateInformation> stateInformation; - // A queue of states that still need to be explored. The indices in this queue are the bucket indices in the - // bit vector hash map holding the compressed states. - std::queue<std::size_t> statesToExplore; + // A set of states that still need to be explored. + std::deque<CompressedState> statesToExplore; + + // An optional mapping from row groups to the indices of the states that they reflect. This needs to be built + // in case the exploration order is not BFS. +// boost::optional<std::vector<StateType, StateType>> rowGroupToIndexMapping; }; diff --git a/src/generator/Choice.cpp b/src/generator/Choice.cpp index 8f42b7b3a..f80831ca0 100644 --- a/src/generator/Choice.cpp +++ b/src/generator/Choice.cpp @@ -92,6 +92,11 @@ namespace storm { choiceRewards.push_back(value); } + template<typename ValueType, typename StateType> + std::vector<ValueType> const& Choice<ValueType, StateType>::getChoiceRewards() const { + return choiceRewards; + } + template<typename ValueType, typename StateType> std::size_t Choice<ValueType, StateType>::size() const { return distribution.size(); diff --git a/src/generator/Choice.h b/src/generator/Choice.h index 78287d62e..95810bfb2 100644 --- a/src/generator/Choice.h +++ b/src/generator/Choice.h @@ -124,6 +124,11 @@ namespace storm { */ void addChoiceReward(ValueType const& value); + /*! + * Retrieves the rewards for this choice under selected reward models. + */ + std::vector<ValueType> const& getChoiceRewards() const; + /*! * Retrieves the size of the distribution associated with this choice. */ diff --git a/src/generator/CompressedState.cpp b/src/generator/CompressedState.cpp new file mode 100644 index 000000000..1f71d2407 --- /dev/null +++ b/src/generator/CompressedState.cpp @@ -0,0 +1,22 @@ +#include "src/generator/CompressedState.h" + +#include "src/generator/VariableInformation.h" +#include "src/storage/expressions/ExpressionEvaluator.h" + +namespace storm { + namespace generator { + + template<typename ValueType> + static void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator) { + for (auto const& booleanVariable : variableInformation.booleanVariables) { + evaluator.setBooleanValue(booleanVariable.variable, state.get(booleanVariable.bitOffset)); + } + for (auto const& integerVariable : variableInformation.integerVariables) { + evaluator.setIntegerValue(integerVariable.variable, state.getAsInt(integerVariable.bitOffset, integerVariable.bitWidth) + integerVariable.lowerBound); + } + + } + + + } +} \ No newline at end of file diff --git a/src/generator/CompressedState.h b/src/generator/CompressedState.h new file mode 100644 index 000000000..512db591f --- /dev/null +++ b/src/generator/CompressedState.h @@ -0,0 +1,30 @@ +#ifndef STORM_GENERATOR_COMPRESSEDSTATE_H_ +#define STORM_GENERATOR_COMPRESSEDSTATE_H_ + +#include "src/storage/BitVector.h" + +namespace storm { + namespace expressions { + template<typename ValueType> class ExpressionEvaluator; + } + + namespace generator { + + typedef storm::storage::BitVector CompressedState; + + class VariableInformation; + + /*! + * Unpacks the compressed state into the evaluator. + * + * @param state The state to unpack. + * @param variableInformation The information about how the variables are packed with the state. + * @param evaluator The evaluator into which to load the state. + */ + template<typename ValueType> + static void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator); + } +} + +#endif /* STORM_GENERATOR_COMPRESSEDSTATE_H_ */ + diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index 4248ab90a..f70709779 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -4,20 +4,17 @@ #include <vector> #include <cstdint> -#include "src/storage/sparse/StateType.h" -#include "src/storage/BitVector.h" - +#include "src/generator/CompressedState.h" #include "src/generator/StateBehavior.h" namespace storm { namespace generator { - typedef storm::storage::BitVector CompressedState; - template<typename ValueType, typename StateType = uint32_t> class NextStateGenerator { public: typedef std::function<StateType (CompressedState const&)> StateToIdCallback; - + + virtual bool isDeterministicModel() const = 0; virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) = 0; virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) = 0; }; diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index 602abb678..f8243998f 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -18,16 +18,39 @@ namespace storm { hasStateActionRewards |= rewardModel.hasStateActionRewards(); } + template<typename ValueType, typename StateType> + void PrismNextStateGenerator<ValueType, StateType>::setTerminalExpression(storm::expressions::Expression const& terminalExpression) { + this->terminalExpression = terminalExpression; + } + + template<typename ValueType, typename StateType> + bool PrismNextStateGenerator<ValueType, StateType>::isDeterministicModel() const { + return program.isDeterministicModel(); + } + template<typename ValueType, typename StateType> std::vector<StateType> PrismNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) { - // FIXME, TODO, whatever + // FIXME: This only works for models with exactly one initial state. We should make this more general. + CompressedState initialState(variableInformation.getTotalBitOffset()); + + // We need to initialize the values of the variables to their initial value. + for (auto const& booleanVariable : variableInformation.booleanVariables) { + initialState.set(booleanVariable.bitOffset, booleanVariable.initialValue); + } + for (auto const& integerVariable : variableInformation.integerVariables) { + initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth, static_cast<uint_fast64_t>(integerVariable.initialValue - integerVariable.lowerBound)); + } + + // Register initial state and return it. + StateType id = stateToIdCallback(initialState); + return {id}; } template<typename ValueType, typename StateType> StateBehavior<ValueType, StateType> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) { - // Start by unpacking the state into the evaluator so we can quickly evaluate expressions later. - unpackStateIntoEvaluator(state); - + // Since almost all subsequent operations are based on the evaluator, we load the state into it now. + unpackStateIntoEvaluator(state, variableInformation, evaluator); + // Prepare the result, in case we return early. StateBehavior<ValueType, StateType> result; @@ -45,6 +68,11 @@ namespace storm { result.addStateReward(stateReward); } + // If a terminal expression was set and we must not expand this state, return now. + if (terminalExpression && evaluator.asBool(terminalExpression.get())) { + return result; + } + // Get all choices for the state. std::vector<Choice<ValueType>> allChoices = getUnlabeledChoices(state, stateToIdCallback); std::vector<Choice<ValueType>> allLabeledChoices = getLabeledChoices(state, stateToIdCallback); @@ -112,21 +140,10 @@ namespace storm { result.addChoice(std::move(choice)); } + result.setExpanded(); return result; } - - - template<typename ValueType, typename StateType> - void PrismNextStateGenerator<ValueType, StateType>::unpackStateIntoEvaluator(storm::storage::BitVector const& state) { - for (auto const& booleanVariable : variableInformation.booleanVariables) { - evaluator.setBooleanValue(booleanVariable.variable, state.get(booleanVariable.bitOffset)); - } - for (auto const& integerVariable : variableInformation.integerVariables) { - evaluator.setIntegerValue(integerVariable.variable, state.getAsInt(integerVariable.bitOffset, integerVariable.bitWidth) + integerVariable.lowerBound); - } - } - template<typename ValueType, typename StateType> CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) { CompressedState newState(state); diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index 282a2f25f..6e400e0eb 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -22,17 +22,16 @@ namespace storm { */ void addRewardModel(storm::prism::RewardModel const& rewardModel); - virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; - virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) override; - - private: /*! - * Unpacks the compressed state into the evaluator. - * - * @param state The state to unpack. + * Sets an expression such that if it evaluates to true in a state, prevents the exploration. */ - void unpackStateIntoEvaluator(CompressedState const& state); + void setTerminalExpression(storm::expressions::Expression const& terminalExpression); + virtual bool isDeterministicModel() const override; + virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; + virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) override; + + private: /*! * Applies an update to the state currently loaded into the evaluator and applies the resulting values to * the given compressed state. @@ -87,6 +86,9 @@ namespace storm { // A flag that stores whether or not to build the choice labeling. bool buildChoiceLabeling; + + // An optional expression that governs which states must not be explored. + boost::optional<storm::expressions::Expression> terminalExpression; // Information about how the variables are packed VariableInformation const& variableInformation; diff --git a/src/generator/StateBehavior.cpp b/src/generator/StateBehavior.cpp index 5868a1e40..bd5d489b7 100644 --- a/src/generator/StateBehavior.cpp +++ b/src/generator/StateBehavior.cpp @@ -3,6 +3,11 @@ namespace storm { namespace generator { + template<typename ValueType, typename StateType> + StateBehavior<ValueType, StateType>::StateBehavior() : expanded(false) { + // Intentionally left empty. + } + template<typename ValueType, typename StateType> void StateBehavior<ValueType, StateType>::addChoice(Choice<ValueType, StateType>&& choice) { choices.push_back(std::move(choice)); @@ -13,5 +18,35 @@ namespace storm { stateRewards.push_back(stateReward); } + template<typename ValueType, typename StateType> + bool StateBehavior<ValueType, StateType>::setExpanded(bool newValue) { + this->expanded = newValue; + } + + template<typename ValueType, typename StateType> + bool StateBehavior<ValueType, StateType>::wasExpanded() const { + return expanded; + } + + template<typename ValueType, typename StateType> + bool StateBehavior<ValueType, StateType>::empty() const { + return choices.empty(); + } + + template<typename ValueType, typename StateType> + typename std::vector<Choice<ValueType, StateType>>::const_iterator StateBehavior<ValueType, StateType>::begin() const { + return choices.begin(); + } + + template<typename ValueType, typename StateType> + typename std::vector<Choice<ValueType, StateType>>::const_iterator StateBehavior<ValueType, StateType>::end() const { + return choices.end(); + } + + template<typename ValueType, typename StateType> + std::vector<ValueType> const& StateBehavior<ValueType, StateType>::getStateRewards() const { + return stateRewards; + } + } } \ No newline at end of file diff --git a/src/generator/StateBehavior.h b/src/generator/StateBehavior.h index b4feecb34..9cbd26617 100644 --- a/src/generator/StateBehavior.h +++ b/src/generator/StateBehavior.h @@ -11,6 +11,11 @@ namespace storm { template<typename ValueType, typename StateType = uint32_t> class StateBehavior { public: + /*! + * Creates an empty behavior, i.e. the state was not yet expanded. + */ + StateBehavior(); + /*! * Adds the given choice to the behavior of the state. */ @@ -21,12 +26,45 @@ namespace storm { */ void addStateReward(ValueType const& stateReward); + /*! + * Sets whether the state was expanded. + */ + bool setExpanded(bool newValue = true); + + /*! + * Retrieves whether the state was expanded. + */ + bool wasExpanded() const; + + /*! + * Retrieves whether the behavior is empty in the sense that there are no available choices. + */ + bool empty() const; + + /*! + * Retrieves an iterator to the choices available in the behavior. + */ + typename std::vector<Choice<ValueType, StateType>>::const_iterator begin() const; + + /*! + * Retrieves an iterator past the choices available in the behavior. + */ + typename std::vector<Choice<ValueType, StateType>>::const_iterator end() const; + + /*! + * Retrieves the list of state rewards under selected reward models. + */ + std::vector<ValueType> const& getStateRewards() const; + private: // The choices available in the state. std::vector<Choice<ValueType, StateType>> choices; // The state rewards (under the different, selected reward models) of the state. std::vector<ValueType> stateRewards; + + // A flag indicating whether the state was actually expanded. + bool expanded; }; } diff --git a/src/generator/VariableInformation.h b/src/generator/VariableInformation.h index a06156336..c77d8f965 100644 --- a/src/generator/VariableInformation.h +++ b/src/generator/VariableInformation.h @@ -1,5 +1,5 @@ -#ifndef STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ -#define STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ +#ifndef STORM_GENERATOR_VARIABLEINFORMATION_H_ +#define STORM_GENERATOR_VARIABLEINFORMATION_H_ #include <vector> #include <boost/container/flat_map.hpp> @@ -71,4 +71,4 @@ namespace storm { } } -#endif /* STORM_GENERATOR_PRISM_VARIABLEINFORMATION_H_ */ \ No newline at end of file +#endif /* STORM_GENERATOR_VARIABLEINFORMATION_H_ */ \ No newline at end of file From fad28df7d626159be5348c9b4466e5697647e66e Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Fri, 26 Feb 2016 17:34:38 +0100 Subject: [PATCH 06/17] first working version of next-state generator for PRISM models Former-commit-id: 548a725e254bcbb924de20f37f24c93fcd916c9b --- src/builder/ExplicitPrismModelBuilder.cpp | 17 +++-- src/builder/ExplicitPrismModelBuilder.h | 2 +- src/generator/Choice.cpp | 31 +++------- src/generator/Choice.h | 23 ++----- src/generator/CompressedState.cpp | 5 +- src/generator/CompressedState.h | 2 +- src/generator/PrismNextStateGenerator.cpp | 62 ++++++++++--------- src/generator/PrismNextStateGenerator.h | 2 + src/generator/StateBehavior.cpp | 7 ++- src/generator/StateBehavior.h | 2 +- src/generator/VariableInformation.cpp | 8 ++- src/generator/VariableInformation.h | 3 +- src/storage/BitVector.cpp | 41 +++++++----- src/storage/BitVector.h | 2 +- src/storage/BitVectorHashMap.cpp | 2 +- src/storage/BitVectorHashMap.h | 2 +- src/storage/Distribution.cpp | 10 +++ src/storage/Distribution.h | 26 ++++++-- src/storage/prism/Program.cpp | 4 ++ .../builder/ExplicitPrismModelBuilderTest.cpp | 32 +++++----- .../GmmxxCtmcCslModelCheckerTest.cpp | 8 +-- .../GmmxxDtmcPrctlModelCheckerTest.cpp | 2 +- .../GmmxxMdpPrctlModelCheckerTest.cpp | 2 +- .../NativeCtmcCslModelCheckerTest.cpp | 8 +-- .../MilpPermissiveSchedulerTest.cpp | 2 +- .../SmtPermissiveSchedulerTest.cpp | 2 +- .../storage/BitVectorHashMapTest.cpp | 18 +++++- test/functional/storage/BitVectorTest.cpp | 18 ------ ...sticModelBisimulationDecompositionTest.cpp | 2 +- test/functional/utility/GraphTest.cpp | 8 +-- 30 files changed, 195 insertions(+), 158 deletions(-) diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 232d7f263..c0188de50 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -175,7 +175,7 @@ namespace storm { } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : program(program), options(options), variableInformation(program), internalStateInformation(variableInformation.getTotalBitOffset()) { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : program(program), options(options) { // Start by defining the undefined constants in the model. if (options.constantDefinitions) { this->program = program.defineUndefinedConstants(options.constantDefinitions.get()); @@ -226,6 +226,12 @@ namespace storm { // Now that the program is fixed, we we need to substitute all constants with their concrete value. this->program = program.substituteConstants(); + + // Create the variable information for the transformed program. + this->variableInformation = VariableInformation(this->program); + + // Create the internal state storage. + this->internalStateInformation = InternalStateInformation(variableInformation.getTotalBitOffset(true)); } template <typename ValueType, typename RewardModelType, typename StateType> @@ -331,7 +337,7 @@ namespace storm { std::function<StateType (CompressedState const&)> stateToIdCallback = std::bind(&ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this, std::placeholders::_1); // Let the generator create all initial states. - generator.getInitialStates(stateToIdCallback); + this->internalStateInformation.initialStateIndices = generator.getInitialStates(stateToIdCallback); // Now explore the current state until there is no more reachable state. uint_fast64_t currentRow = 0; @@ -403,13 +409,13 @@ namespace storm { } // Add the probabilistic behavior to the matrix. - for (auto const& stateProbabilityPair : behavior) { + for (auto const& stateProbabilityPair : choice) { transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); } // Add the rewards to the reward models. auto builderIt = rewardModelBuilders.begin(); - auto choiceRewardIt = behavior.getChoiceRewards().begin(); + auto choiceRewardIt = choice.getChoiceRewards().begin(); for (auto const& rewardModel : selectedRewardModels) { if (rewardModel.get().hasStateActionRewards()) { builderIt->stateActionRewardVector.push_back(*choiceRewardIt); @@ -437,7 +443,6 @@ namespace storm { // Determine whether we have to combine different choices to one or whether this model can have more than // one choice per state. bool deterministicModel = program.isDeterministicModel(); - bool discreteTimeModel = program.isDiscreteTimeModel(); // Prepare the transition matrix builder and the reward model builders. storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); @@ -477,7 +482,7 @@ namespace storm { STORM_LOG_TRACE("Making the states satisfying " << terminalExpression.get() << " terminal."); } - modelComponents.choiceLabeling = buildMatrices(program, variableInformation, selectedRewardModels, internalStateInformation, options.buildCommandLabels, deterministicModel, discreteTimeModel, transitionMatrixBuilder, rewardModelBuilders, terminalExpression); + modelComponents.choiceLabeling = buildMatrices(selectedRewardModels, transitionMatrixBuilder, rewardModelBuilders, terminalExpression); modelComponents.transitionMatrix = transitionMatrixBuilder.build(); // Now finalize all reward models. diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index 54fe9cf30..a5e09bfae 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -45,7 +45,7 @@ namespace storm { public: // A structure holding information about the reachable state space while building it. struct InternalStateInformation { - InternalStateInformation(uint64_t bitsPerState); + InternalStateInformation(uint64_t bitsPerState = 64); // This member stores all the states and maps them to their unique indices. storm::storage::BitVectorHashMap<StateType> stateStorage; diff --git a/src/generator/Choice.cpp b/src/generator/Choice.cpp index f80831ca0..9202f2c5e 100644 --- a/src/generator/Choice.cpp +++ b/src/generator/Choice.cpp @@ -1,5 +1,7 @@ #include "src/generator/Choice.h" +#include "src/adapters/CarlAdapter.h" + #include "src/utility/constants.h" namespace storm { @@ -61,30 +63,10 @@ namespace storm { return totalMass; } - template<typename ValueType, typename StateType> - ValueType& Choice<ValueType, StateType>::getOrAddEntry(StateType const& state) { - auto stateProbabilityPair = distribution.find(state); - - if (stateProbabilityPair == distribution.end()) { - distribution[state] = ValueType(); - } - return distribution.at(state); - } - - template<typename ValueType, typename StateType> - ValueType const& Choice<ValueType, StateType>::getOrAddEntry(StateType const& state) const { - auto stateProbabilityPair = distribution.find(state); - - if (stateProbabilityPair == distribution.end()) { - distribution[state] = ValueType(); - } - return distribution.at(state); - } - template<typename ValueType, typename StateType> void Choice<ValueType, StateType>::addProbability(StateType const& state, ValueType const& value) { totalMass += value; - distribution[state] += value; + distribution.addProbability(state, value); } template<typename ValueType, typename StateType> @@ -97,6 +79,11 @@ namespace storm { return choiceRewards; } + template<typename ValueType, typename StateType> + bool Choice<ValueType, StateType>::isMarkovian() const { + return markovian; + } + template<typename ValueType, typename StateType> std::size_t Choice<ValueType, StateType>::size() const { return distribution.size(); @@ -112,5 +99,7 @@ namespace storm { return out; } + template class Choice<double>; + template class Choice<storm::RationalFunction>; } } \ No newline at end of file diff --git a/src/generator/Choice.h b/src/generator/Choice.h index 95810bfb2..76b6c8d16 100644 --- a/src/generator/Choice.h +++ b/src/generator/Choice.h @@ -96,24 +96,6 @@ namespace storm { */ ValueType getTotalMass() const; - /*! - * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, - * yet. - * - * @param state The state for which to add the entry. - * @return A reference to the entry that is associated with the given state. - */ - ValueType& getOrAddEntry(StateType const& state); - - /*! - * Retrieves the entry in the choice that is associated with the given state and creates one if none exists, - * yet. - * - * @param state The state for which to add the entry. - * @return A reference to the entry that is associated with the given state. - */ - ValueType const& getOrAddEntry(StateType const& state) const; - /*! * Adds the given probability value to the given state in the underlying distribution. */ @@ -129,6 +111,11 @@ namespace storm { */ std::vector<ValueType> const& getChoiceRewards() const; + /*! + * Retrieves whether the choice is Markovian. + */ + bool isMarkovian() const; + /*! * Retrieves the size of the distribution associated with this choice. */ diff --git a/src/generator/CompressedState.cpp b/src/generator/CompressedState.cpp index 1f71d2407..c63504ebb 100644 --- a/src/generator/CompressedState.cpp +++ b/src/generator/CompressedState.cpp @@ -7,7 +7,7 @@ namespace storm { namespace generator { template<typename ValueType> - static void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator) { + void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator) { for (auto const& booleanVariable : variableInformation.booleanVariables) { evaluator.setBooleanValue(booleanVariable.variable, state.get(booleanVariable.bitOffset)); } @@ -17,6 +17,7 @@ namespace storm { } - + template void unpackStateIntoEvaluator<double>(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<double>& evaluator); + template void unpackStateIntoEvaluator<storm::RationalFunction>(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<storm::RationalFunction>& evaluator); } } \ No newline at end of file diff --git a/src/generator/CompressedState.h b/src/generator/CompressedState.h index 512db591f..52f0b4a62 100644 --- a/src/generator/CompressedState.h +++ b/src/generator/CompressedState.h @@ -22,7 +22,7 @@ namespace storm { * @param evaluator The evaluator into which to load the state. */ template<typename ValueType> - static void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator); + void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator<ValueType>& evaluator); } } diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index f8243998f..ad3ce3080 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -8,7 +8,7 @@ namespace storm { namespace generator { template<typename ValueType, typename StateType> - PrismNextStateGenerator<ValueType, StateType>::PrismNextStateGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation, bool buildChoiceLabeling) : program(program), selectedRewardModels(), buildChoiceLabeling(buildChoiceLabeling), variableInformation(variableInformation), evaluator(), comparator() { + PrismNextStateGenerator<ValueType, StateType>::PrismNextStateGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation, bool buildChoiceLabeling) : program(program), selectedRewardModels(), buildChoiceLabeling(buildChoiceLabeling), variableInformation(variableInformation), evaluator(program.getManager()), comparator() { // Intentionally left empty. } @@ -57,15 +57,15 @@ namespace storm { // First, construct the state rewards, as we may return early if there are no choices later and we already // need the state rewards then. for (auto const& rewardModel : selectedRewardModels) { - ValueType stateReward = storm::utility::zero<ValueType>(); - if (rewardModel->hasStateRewards()) { - for (auto const& stateReward : rewardModel->getStateRewards()) { + ValueType stateRewardValue = storm::utility::zero<ValueType>(); + if (rewardModel.get().hasStateRewards()) { + for (auto const& stateReward : rewardModel.get().getStateRewards()) { if (evaluator.asBool(stateReward.getStatePredicateExpression())) { - stateReward += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); + stateRewardValue += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); } } } - result.addStateReward(stateReward); + result.addStateReward(stateRewardValue); } // If a terminal expression was set and we must not expand this state, return now. @@ -94,21 +94,22 @@ namespace storm { // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs // this is equal to the number of choices, which is why we initialize it like this here. - ValueType totalExitRate = static_cast<ValueType>(totalNumberOfChoices); + ValueType totalExitRate = program.isDiscreteTimeModel() ? static_cast<ValueType>(totalNumberOfChoices) : storm::utility::zero<ValueType>(); // Iterate over all choices and combine the probabilities/rates into one choice. for (auto const& choice : allChoices) { for (auto const& stateProbabilityPair : choice) { if (program.isDiscreteTimeModel()) { - globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second / totalNumberOfChoices; - if (hasStateActionRewards) { - totalExitRate += choice.getTotalMass(); - } + globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices); } else { - globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second; + globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second); } } + if (hasStateActionRewards && !program.isDiscreteTimeModel()) { + totalExitRate += choice.getTotalMass(); + } + if (buildChoiceLabeling) { globalChoice.addChoiceLabels(choice.getChoiceLabels()); } @@ -116,18 +117,18 @@ namespace storm { // Now construct the state-action reward for all selected reward models. for (auto const& rewardModel : selectedRewardModels) { - ValueType stateActionReward = storm::utility::zero<ValueType>(); - if (rewardModel->hasStateActionRewards()) { - for (auto const& stateActionReward : rewardModel->getStateActionRewards()) { + ValueType stateActionRewardValue = storm::utility::zero<ValueType>(); + if (rewardModel.get().hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) { for (auto const& choice : allChoices) { if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) / totalExitRate; + stateActionRewardValue += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass() / totalExitRate; } } } } - globalChoice.addChoiceReward(stateActionReward); + globalChoice.addChoiceReward(stateActionRewardValue); } // Move the newly fused choice in place. @@ -266,15 +267,15 @@ namespace storm { // Create the state-action reward for the newly created choice. for (auto const& rewardModel : selectedRewardModels) { - ValueType stateActionReward = storm::utility::zero<ValueType>(); - if (rewardModel->hasStateActionRewards()) { - for (auto const& stateActionReward : rewardModel->getStateActionRewards()) { + ValueType stateActionRewardValue = storm::utility::zero<ValueType>(); + if (rewardModel.get().hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) { if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass(); + stateActionRewardValue += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass(); } } } - choice.addChoiceReward(stateActionReward); + choice.addChoiceReward(stateActionRewardValue); } // Check that the resulting distribution is in fact a distribution. @@ -290,7 +291,7 @@ namespace storm { std::vector<Choice<ValueType>> result; for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) { - boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists = getActiveCommandsByActionIndex(program, evaluator, actionIndex); + boost::optional<std::vector<std::vector<std::reference_wrapper<storm::prism::Command const>>>> optionalActiveCommandLists = getActiveCommandsByActionIndex(actionIndex); // Only process this action label, if there is at least one feasible solution. if (optionalActiveCommandLists) { @@ -317,7 +318,7 @@ namespace storm { for (auto const& stateProbabilityPair : *currentTargetStates) { // Compute the new state under the current update and add it to the set of new target states. - CompressedState newTargetState = applyUpdate(stateProbabilityPair.first, state, update); + CompressedState newTargetState = applyUpdate(stateProbabilityPair.first, update); newTargetStates->emplace(newTargetState, stateProbabilityPair.second * evaluator.asRational(update.getLikelihoodExpression())); } } @@ -359,15 +360,15 @@ namespace storm { // Create the state-action reward for the newly created choice. for (auto const& rewardModel : selectedRewardModels) { - ValueType stateActionReward = storm::utility::zero<ValueType>(); - if (rewardModel->hasStateActionRewards()) { - for (auto const& stateActionReward : rewardModel->getStateActionRewards()) { + ValueType stateActionRewardValue = storm::utility::zero<ValueType>(); + if (rewardModel.get().hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) { if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass(); + stateActionRewardValue += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass(); } } } - choice.addChoiceReward(stateActionReward); + choice.addChoiceReward(stateActionRewardValue); } // Dispose of the temporary maps. @@ -393,5 +394,8 @@ namespace storm { return result; } + + template class PrismNextStateGenerator<double>; + template class PrismNextStateGenerator<storm::RationalFunction>; } } \ No newline at end of file diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index 6e400e0eb..e9c63502e 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -7,6 +7,8 @@ #include "src/storage/prism/Program.h" #include "src/storage/expressions/ExpressionEvaluator.h" +#include "src/utility/ConstantsComparator.h" + namespace storm { namespace generator { diff --git a/src/generator/StateBehavior.cpp b/src/generator/StateBehavior.cpp index bd5d489b7..e14417779 100644 --- a/src/generator/StateBehavior.cpp +++ b/src/generator/StateBehavior.cpp @@ -1,5 +1,7 @@ #include "src/generator/StateBehavior.h" +#include "src/adapters/CarlAdapter.h" + namespace storm { namespace generator { @@ -19,7 +21,7 @@ namespace storm { } template<typename ValueType, typename StateType> - bool StateBehavior<ValueType, StateType>::setExpanded(bool newValue) { + void StateBehavior<ValueType, StateType>::setExpanded(bool newValue) { this->expanded = newValue; } @@ -47,6 +49,9 @@ namespace storm { std::vector<ValueType> const& StateBehavior<ValueType, StateType>::getStateRewards() const { return stateRewards; } + + template class StateBehavior<double>; + template class StateBehavior<storm::RationalFunction>; } } \ No newline at end of file diff --git a/src/generator/StateBehavior.h b/src/generator/StateBehavior.h index 9cbd26617..393c3280c 100644 --- a/src/generator/StateBehavior.h +++ b/src/generator/StateBehavior.h @@ -29,7 +29,7 @@ namespace storm { /*! * Sets whether the state was expanded. */ - bool setExpanded(bool newValue = true); + void setExpanded(bool newValue = true); /*! * Retrieves whether the state was expanded. diff --git a/src/generator/VariableInformation.cpp b/src/generator/VariableInformation.cpp index bb20c5225..a5be93a6e 100644 --- a/src/generator/VariableInformation.cpp +++ b/src/generator/VariableInformation.cpp @@ -45,8 +45,12 @@ namespace storm { } } - uint_fast64_t VariableInformation::getTotalBitOffset() const { - return totalBitOffset; + uint_fast64_t VariableInformation::getTotalBitOffset(bool roundTo64Bit) const { + uint_fast64_t result = totalBitOffset; + if (roundTo64Bit) { + result = ((result >> 6) + 1) << 6; + } + return result; } uint_fast64_t VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const { diff --git a/src/generator/VariableInformation.h b/src/generator/VariableInformation.h index c77d8f965..8c2841dfd 100644 --- a/src/generator/VariableInformation.h +++ b/src/generator/VariableInformation.h @@ -49,8 +49,9 @@ namespace storm { // A structure storing information about the used variables of the program. struct VariableInformation { + VariableInformation() = default; VariableInformation(storm::prism::Program const& program); - uint_fast64_t getTotalBitOffset() const; + uint_fast64_t getTotalBitOffset(bool roundTo64Bit = false) const; // Provide methods to access the bit offset and width of variables in the compressed state. uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const; diff --git a/src/storage/BitVector.cpp b/src/storage/BitVector.cpp index 38b94d852..3e25e488d 100644 --- a/src/storage/BitVector.cpp +++ b/src/storage/BitVector.cpp @@ -64,7 +64,7 @@ namespace storm { // Intentionally left empty. } - BitVector::BitVector(uint_fast64_t length, bool init) : bitCount(length) { + BitVector::BitVector(uint_fast64_t length, bool init) : bitCount(length), buckets(nullptr) { // Compute the correct number of buckets needed to store the given number of bits. uint_fast64_t bucketCount = length >> 6; if ((length & mod64mask) != 0) { @@ -96,12 +96,12 @@ namespace storm { // Intentionally left empty. } - BitVector::BitVector(uint_fast64_t bucketCount, uint_fast64_t bitCount) : bitCount(bitCount) { + BitVector::BitVector(uint_fast64_t bucketCount, uint_fast64_t bitCount) : bitCount(bitCount), buckets(nullptr) { STORM_LOG_ASSERT((bucketCount << 6) == bitCount, "Bit count does not match number of buckets."); buckets = new uint64_t[bucketCount](); } - BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount) { + BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount), buckets(nullptr) { buckets = new uint64_t[other.bucketCount()]; std::copy_n(other.buckets, other.bucketCount(), buckets); } @@ -136,6 +136,11 @@ namespace storm { } return false; } + + BitVector::BitVector(BitVector&& other) : bitCount(other.bitCount), buckets(other.buckets) { + other.bitCount = 0; + other.buckets = nullptr; + } BitVector& BitVector::operator=(BitVector&& other) { // Only perform the assignment if the source and target are not identical. @@ -202,11 +207,13 @@ namespace storm { std::copy_n(buckets, this->bucketCount(), newBuckets); if (init) { newBuckets[this->bucketCount() - 1] |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull); - std::fill_n(newBuckets, newBucketCount - this->bucketCount(), -1ull); + std::fill_n(newBuckets + this->bucketCount(), newBucketCount - this->bucketCount(), -1ull); } else { - std::fill_n(newBuckets, newBucketCount - this->bucketCount(), 0); + std::fill_n(newBuckets + this->bucketCount(), newBucketCount - this->bucketCount(), 0); + } + if (buckets != nullptr) { + delete buckets; } - delete buckets; buckets = newBuckets; bitCount = newLength; } else { @@ -228,7 +235,9 @@ namespace storm { if (newBucketCount < this->bucketCount()) { uint64_t* newBuckets = new uint64_t[newBucketCount]; std::copy_n(buckets, newBucketCount, newBuckets); - delete buckets; + if (buckets != nullptr) { + delete buckets; + } buckets = newBuckets; bitCount = newLength; } @@ -473,19 +482,19 @@ namespace storm { bool BitVector::empty() const { uint64_t* last = buckets + bucketCount(); - uint64_t* it = std::find(buckets, last, 0); - return it != last; + uint64_t* it = std::find_if(buckets, last, [] (uint64_t const& a) { return a != 0; }); + return it == last; } bool BitVector::full() const { // Check that all buckets except the last one have all bits set. uint64_t* last = buckets + bucketCount() - 1; - for (uint64_t const* it = buckets; it != last; ++it) { + for (uint64_t const* it = buckets; it < last; ++it) { if (*it != -1ull) { return false; } } - + // Now check whether the relevant bits are set in the last bucket. uint64_t mask = ~((1ull << (64 - (bitCount & mod64mask))) - 1ull); if ((*last & mask) != mask) { @@ -627,7 +636,11 @@ namespace storm { } size_t BitVector::bucketCount() const { - return bitCount >> 6; + size_t result = (bitCount >> 6); + if ((bitCount & mod64mask) != 0) { + ++result; + } + return result; } std::ostream& operator<<(std::ostream& out, BitVector const& bitvector) { @@ -670,7 +683,7 @@ namespace storm { namespace std { - std::size_t hash<storm::storage::BitVector>::operator()(storm::storage::BitVector const& bv) const { - return boost::hash_range(bv.bucketVector.begin(), bv.bucketVector.end()); + std::size_t hash<storm::storage::BitVector>::operator()(storm::storage::BitVector const& bitvector) const { + return boost::hash_range(bitvector.buckets, bitvector.buckets + bitvector.bucketCount()); } } \ No newline at end of file diff --git a/src/storage/BitVector.h b/src/storage/BitVector.h index a6ad89cc3..f15eef3ed 100644 --- a/src/storage/BitVector.h +++ b/src/storage/BitVector.h @@ -146,7 +146,7 @@ namespace storm { * * @param other The bit vector from which to move-construct. */ - BitVector(BitVector&& other) = default; + BitVector(BitVector&& other); /*! * Compares the given bit vector with the current one. diff --git a/src/storage/BitVectorHashMap.cpp b/src/storage/BitVectorHashMap.cpp index c184b764b..881fc016a 100644 --- a/src/storage/BitVectorHashMap.cpp +++ b/src/storage/BitVectorHashMap.cpp @@ -40,7 +40,7 @@ namespace storm { std::pair<storm::storage::BitVector, ValueType> BitVectorHashMap<ValueType, Hash1, Hash2>::BitVectorHashMapIterator::operator*() const { return map.getBucketAndValue(*indexIt); } - + template<class ValueType, class Hash1, class Hash2> BitVectorHashMap<ValueType, Hash1, Hash2>::BitVectorHashMap(uint64_t bucketSize, uint64_t initialSize, double loadFactor) : loadFactor(loadFactor), bucketSize(bucketSize), numberOfElements(0) { STORM_LOG_ASSERT(bucketSize % 64 == 0, "Bucket size must be a multiple of 64."); diff --git a/src/storage/BitVectorHashMap.h b/src/storage/BitVectorHashMap.h index 3b0c7aa70..c53928b09 100644 --- a/src/storage/BitVectorHashMap.h +++ b/src/storage/BitVectorHashMap.h @@ -55,7 +55,7 @@ namespace storm { * @param loadFactor The load factor that determines at which point the size of the underlying storage is * increased. */ - BitVectorHashMap(uint64_t bucketSize, uint64_t initialSize, double loadFactor = 0.75); + BitVectorHashMap(uint64_t bucketSize = 64, uint64_t initialSize = 1000, double loadFactor = 0.75); /*! * Searches for the given key in the map. If it is found, the mapped-to value is returned. Otherwise, the diff --git a/src/storage/Distribution.cpp b/src/storage/Distribution.cpp index 516227277..5843e964b 100644 --- a/src/storage/Distribution.cpp +++ b/src/storage/Distribution.cpp @@ -78,6 +78,11 @@ namespace storm { return this->distribution.begin(); } + template<typename ValueType, typename StateType> + typename Distribution<ValueType, StateType>::const_iterator Distribution<ValueType, StateType>::cbegin() const { + return this->begin(); + } + template<typename ValueType, typename StateType> typename Distribution<ValueType, StateType>::iterator Distribution<ValueType, StateType>::end() { return this->distribution.end(); @@ -87,6 +92,11 @@ namespace storm { typename Distribution<ValueType, StateType>::const_iterator Distribution<ValueType, StateType>::end() const { return this->distribution.end(); } + + template<typename ValueType, typename StateType> + typename Distribution<ValueType, StateType>::const_iterator Distribution<ValueType, StateType>::cend() const { + return this->end(); + } template<typename ValueType, typename StateType> void Distribution<ValueType, StateType>::scale(StateType const& state) { diff --git a/src/storage/Distribution.h b/src/storage/Distribution.h index 39e6eb6f9..ba61f0fbd 100644 --- a/src/storage/Distribution.h +++ b/src/storage/Distribution.h @@ -17,7 +17,7 @@ namespace storm { namespace storage { - template<typename ValueType, typename StateType=storm::storage::sparse::state_type> + template<typename ValueType, typename StateType = uint32_t> class Distribution { public: typedef boost::container::flat_map<StateType, ValueType> container_type; @@ -43,7 +43,7 @@ namespace storm { * @param other The distribution with which the current distribution is to be compared. * @return True iff the two distributions are equal. */ - bool equals(Distribution<ValueType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()) const; + bool equals(Distribution<ValueType, StateType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()) const; /*! * Assigns the given state the given probability under this distribution. @@ -73,7 +73,7 @@ namespace storm { * entry is removed. */ void shiftProbability(StateType const& fromState, StateType const& toState, ValueType const& probability, storm::utility::ConstantsComparator<ValueType> const& comparator = storm::utility::ConstantsComparator<ValueType>()); - + /*! * Retrieves an iterator to the elements in this distribution. * @@ -88,6 +88,13 @@ namespace storm { */ const_iterator begin() const; + /*! + * Retrieves an iterator to the elements in this distribution. + * + * @return The iterator to the elements in this distribution. + */ + const_iterator cbegin() const; + /*! * Retrieves an iterator past the elements in this distribution. * @@ -102,6 +109,13 @@ namespace storm { */ const_iterator end() const; + /*! + * Retrieves an iterator past the elements in this distribution. + * + * @return The iterator past the elements in this distribution. + */ + const_iterator cend() const; + /*! * Scales the distribution by multiplying all the probabilities with 1/p where p is the probability of moving * to the given state and sets the probability of moving to the given state to zero. If the probability is @@ -116,15 +130,15 @@ namespace storm { */ std::size_t size() const; - bool less(Distribution<ValueType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator) const; + bool less(Distribution<ValueType, StateType> const& other, storm::utility::ConstantsComparator<ValueType> const& comparator) const; private: // A list of states and the probabilities that are assigned to them. container_type distribution; }; - template<typename ValueType> - std::ostream& operator<<(std::ostream& out, Distribution<ValueType> const& distribution); + template<typename ValueType, typename StateType = uint32_t> + std::ostream& operator<<(std::ostream& out, Distribution<ValueType, StateType> const& distribution); } } diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 0f5aea4a8..999cf2f30 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -81,6 +81,10 @@ namespace storm { return modelType == ModelType::DTMC || modelType == ModelType::MDP; } + bool Program::isDeterministicModel() const { + return modelType == ModelType::DTMC || modelType == ModelType::CTMC; + } + bool Program::hasUndefinedConstants() const { for (auto const& constant : this->getConstants()) { if (!constant.isDefined()) { diff --git a/test/functional/builder/ExplicitPrismModelBuilderTest.cpp b/test/functional/builder/ExplicitPrismModelBuilderTest.cpp index 080b6c16f..c2151c74c 100644 --- a/test/functional/builder/ExplicitPrismModelBuilderTest.cpp +++ b/test/functional/builder/ExplicitPrismModelBuilderTest.cpp @@ -10,27 +10,27 @@ TEST(ExplicitPrismModelBuilderTest, Dtmc) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(13ul, model->getNumberOfStates()); EXPECT_EQ(20ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(677ul, model->getNumberOfStates()); EXPECT_EQ(867ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(8607ul, model->getNumberOfStates()); EXPECT_EQ(15113ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(273ul, model->getNumberOfStates()); EXPECT_EQ(397ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(1728ul, model->getNumberOfStates()); EXPECT_EQ(2505ul, model->getNumberOfTransitions()); } @@ -41,27 +41,27 @@ TEST(ExplicitPrismModelBuilderTest, Ctmc) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/cluster2.sm"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(276ul, model->getNumberOfStates()); EXPECT_EQ(1120ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/embedded2.sm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(3478ul, model->getNumberOfStates()); EXPECT_EQ(14639ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/polling2.sm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(12ul, model->getNumberOfStates()); EXPECT_EQ(22ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/fms2.sm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(810ul, model->getNumberOfStates()); EXPECT_EQ(3699ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/tandem5.sm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(66ul, model->getNumberOfStates()); EXPECT_EQ(189ul, model->getNumberOfTransitions()); } @@ -69,32 +69,32 @@ TEST(ExplicitPrismModelBuilderTest, Ctmc) { TEST(ExplicitPrismModelBuilderTest, Mdp) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(169ul, model->getNumberOfStates()); EXPECT_EQ(436ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader3.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(364ul, model->getNumberOfStates()); EXPECT_EQ(654ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(272ul, model->getNumberOfStates()); EXPECT_EQ(492ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/csma2-2.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(1038ul, model->getNumberOfStates()); EXPECT_EQ(1282ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/firewire3-0.5.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(4093ul, model->getNumberOfStates()); EXPECT_EQ(5585ul, model->getNumberOfTransitions()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(37ul, model->getNumberOfStates()); EXPECT_EQ(59ul, model->getNumberOfTransitions()); } \ No newline at end of file diff --git a/test/functional/modelchecker/GmmxxCtmcCslModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxCtmcCslModelCheckerTest.cpp index b36983962..dc9512a38 100644 --- a/test/functional/modelchecker/GmmxxCtmcCslModelCheckerTest.cpp +++ b/test/functional/modelchecker/GmmxxCtmcCslModelCheckerTest.cpp @@ -34,7 +34,7 @@ TEST(GmmxxCtmcCslModelCheckerTest, Cluster) { #endif options.buildAllRewardModels = false; options.rewardModelsToBuild.insert("num_repairs"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); @@ -117,7 +117,7 @@ TEST(GmmxxCtmcCslModelCheckerTest, Embedded) { #endif options.buildAllRewardModels = false; options.rewardModelsToBuild.insert("up"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); @@ -179,7 +179,7 @@ TEST(GmmxxCtmcCslModelCheckerTest, Polling) { std::shared_ptr<const storm::logic::Formula> formula(nullptr); // Build the model. - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); @@ -226,7 +226,7 @@ TEST(GmmxxCtmcCslModelCheckerTest, Tandem) { typename storm::builder::ExplicitPrismModelBuilder<double>::Options options; #endif options.rewardModelsToBuild.insert("customers"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); diff --git a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp index 1e131c335..36a139b9c 100644 --- a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp @@ -284,7 +284,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, LRA) { TEST(GmmxxDtmcPrctlModelCheckerTest, Conditional) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/modelchecker/test_conditional.pm"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_TRUE(model->getType() == storm::models::ModelType::Dtmc); ASSERT_EQ(4ul, model->getNumberOfStates()); ASSERT_EQ(5ul, model->getNumberOfTransitions()); diff --git a/test/functional/modelchecker/GmmxxMdpPrctlModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxMdpPrctlModelCheckerTest.cpp index 725e1376d..93643fd52 100644 --- a/test/functional/modelchecker/GmmxxMdpPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/GmmxxMdpPrctlModelCheckerTest.cpp @@ -197,7 +197,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, SchedulerGeneration) { // A parser that we use for conveniently constructing the formulas. storm::parser::FormulaParser formulaParser; - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); EXPECT_EQ(4ul, model->getNumberOfStates()); EXPECT_EQ(11ul, model->getNumberOfTransitions()); diff --git a/test/functional/modelchecker/NativeCtmcCslModelCheckerTest.cpp b/test/functional/modelchecker/NativeCtmcCslModelCheckerTest.cpp index 72788de80..0a1d8ee4c 100644 --- a/test/functional/modelchecker/NativeCtmcCslModelCheckerTest.cpp +++ b/test/functional/modelchecker/NativeCtmcCslModelCheckerTest.cpp @@ -32,7 +32,7 @@ TEST(NativeCtmcCslModelCheckerTest, Cluster) { #endif options.buildAllRewardModels = false; options.rewardModelsToBuild.insert("num_repairs"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); @@ -108,7 +108,7 @@ TEST(NativeCtmcCslModelCheckerTest, Embedded) { #endif options.buildAllRewardModels = false; options.rewardModelsToBuild.insert("up"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); @@ -163,7 +163,7 @@ TEST(NativeCtmcCslModelCheckerTest, Polling) { std::shared_ptr<const storm::logic::Formula> formula(nullptr); // Build the model. - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); @@ -204,7 +204,7 @@ TEST(NativeCtmcCslModelCheckerTest, Tandem) { #endif options.buildAllRewardModels = false; options.rewardModelsToBuild.insert("customers"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate(); ASSERT_EQ(storm::models::ModelType::Ctmc, model->getType()); std::shared_ptr<storm::models::sparse::Ctmc<double>> ctmc = model->as<storm::models::sparse::Ctmc<double>>(); uint_fast64_t initialState = *ctmc->getInitialStates().begin(); diff --git a/test/functional/permissiveschedulers/MilpPermissiveSchedulerTest.cpp b/test/functional/permissiveschedulers/MilpPermissiveSchedulerTest.cpp index 75bea3570..657d36575 100644 --- a/test/functional/permissiveschedulers/MilpPermissiveSchedulerTest.cpp +++ b/test/functional/permissiveschedulers/MilpPermissiveSchedulerTest.cpp @@ -28,7 +28,7 @@ TEST(MilpPermissiveSchedulerTest, DieSelection) { options.addConstantDefinitionsFromString(program, ""); options.buildCommandLabels = true; - std::shared_ptr<storm::models::sparse::Mdp<double>> mdp = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options)->as<storm::models::sparse::Mdp<double>>(); + std::shared_ptr<storm::models::sparse::Mdp<double>> mdp = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate()->as<storm::models::sparse::Mdp<double>>(); boost::optional<storm::ps::SubMDPPermissiveScheduler<>> perms = storm::ps::computePermissiveSchedulerViaMILP<>(*mdp, formula02); EXPECT_NE(perms, boost::none); diff --git a/test/functional/permissiveschedulers/SmtPermissiveSchedulerTest.cpp b/test/functional/permissiveschedulers/SmtPermissiveSchedulerTest.cpp index 4d87796b8..2790cd86a 100644 --- a/test/functional/permissiveschedulers/SmtPermissiveSchedulerTest.cpp +++ b/test/functional/permissiveschedulers/SmtPermissiveSchedulerTest.cpp @@ -31,7 +31,7 @@ TEST(SmtPermissiveSchedulerTest, DieSelection) { options.addConstantDefinitionsFromString(program, ""); options.buildCommandLabels = true; - std::shared_ptr<storm::models::sparse::Mdp<double>> mdp = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program, options)->as<storm::models::sparse::Mdp<double>>(); + std::shared_ptr<storm::models::sparse::Mdp<double>> mdp = storm::builder::ExplicitPrismModelBuilder<double>(program, options).translate()->as<storm::models::sparse::Mdp<double>>(); // boost::optional<storm::ps::SubMDPPermissiveScheduler<>> perms = storm::ps::computePermissiveSchedulerViaSMT<>(*mdp, formula02); // EXPECT_NE(perms, boost::none); diff --git a/test/functional/storage/BitVectorHashMapTest.cpp b/test/functional/storage/BitVectorHashMapTest.cpp index 616941f21..1149433fd 100644 --- a/test/functional/storage/BitVectorHashMapTest.cpp +++ b/test/functional/storage/BitVectorHashMapTest.cpp @@ -19,25 +19,41 @@ TEST(BitVectorHashMapTest, FindOrAdd) { ASSERT_NO_THROW(map.findOrAdd(second, 2)); EXPECT_EQ(1ul, map.findOrAdd(first, 3)); - + EXPECT_EQ(2ul, map.findOrAdd(second, 3)); + storm::storage::BitVector third(64); third.set(10); third.set(63); ASSERT_NO_THROW(map.findOrAdd(third, 3)); + EXPECT_EQ(1ul, map.findOrAdd(first, 2)); + EXPECT_EQ(2ul, map.findOrAdd(second, 1)); + EXPECT_EQ(3ul, map.findOrAdd(third, 1)); + storm::storage::BitVector fourth(64); fourth.set(12); fourth.set(14); ASSERT_NO_THROW(map.findOrAdd(fourth, 4)); + EXPECT_EQ(1ul, map.findOrAdd(first, 2)); + EXPECT_EQ(2ul, map.findOrAdd(second, 1)); + EXPECT_EQ(3ul, map.findOrAdd(third, 1)); + EXPECT_EQ(4ul, map.findOrAdd(fourth, 1)); + storm::storage::BitVector fifth(64); fifth.set(44); fifth.set(55); ASSERT_NO_THROW(map.findOrAdd(fifth, 5)); + EXPECT_EQ(1ul, map.findOrAdd(first, 2)); + EXPECT_EQ(2ul, map.findOrAdd(second, 1)); + EXPECT_EQ(3ul, map.findOrAdd(third, 1)); + EXPECT_EQ(4ul, map.findOrAdd(fourth, 1)); + EXPECT_EQ(5ul, map.findOrAdd(fifth, 1)); + storm::storage::BitVector sixth(64); sixth.set(45); sixth.set(55); diff --git a/test/functional/storage/BitVectorTest.cpp b/test/functional/storage/BitVectorTest.cpp index db92724fe..7266f829b 100644 --- a/test/functional/storage/BitVectorTest.cpp +++ b/test/functional/storage/BitVectorTest.cpp @@ -321,24 +321,6 @@ TEST(BitVectorTest, OperatorModulo) { ASSERT_FALSE(moduloResult.get(i)); } } - - storm::storage::BitVector vector3(31); - - for (uint_fast64_t i = 0; i < 15; ++i) { - vector3.set(i, i % 2 == 0); - } - - -#ifndef NDEBUG -#ifdef WINDOWS - EXPECT_EXIT(vector1 % vector3, ::testing::ExitedWithCode(0), ".*"); -#else - EXPECT_DEATH_IF_SUPPORTED(vector1 % vector3, ""); -#endif -#else - std::cerr << "WARNING: Not testing OperatorModulo size check, as assertions are disabled in release mode." << std::endl; - SUCCEED(); -#endif } TEST(BitVectorTest, OperatorNot) { diff --git a/test/functional/storage/NondeterministicModelBisimulationDecompositionTest.cpp b/test/functional/storage/NondeterministicModelBisimulationDecompositionTest.cpp index 3754b03fd..fe0a199b7 100644 --- a/test/functional/storage/NondeterministicModelBisimulationDecompositionTest.cpp +++ b/test/functional/storage/NondeterministicModelBisimulationDecompositionTest.cpp @@ -14,7 +14,7 @@ TEST(NondeterministicModelBisimulationDecomposition, TwoDice) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); // Build the die model without its reward model. - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); std::shared_ptr<storm::models::sparse::Mdp<double>> mdp = model->as<storm::models::sparse::Mdp<double>>(); diff --git a/test/functional/utility/GraphTest.cpp b/test/functional/utility/GraphTest.cpp index d209998d7..4c6bf0d50 100644 --- a/test/functional/utility/GraphTest.cpp +++ b/test/functional/utility/GraphTest.cpp @@ -181,7 +181,7 @@ TEST(GraphTest, SymbolicProb01MinMax_Sylvan) { TEST(GraphTest, ExplicitProb01) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_TRUE(model->getType() == storm::models::ModelType::Dtmc); @@ -202,7 +202,7 @@ TEST(GraphTest, ExplicitProb01) { TEST(GraphTest, ExplicitProb01MinMax) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader3.nm"); - std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + std::shared_ptr<storm::models::sparse::Model<double>> model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp); @@ -217,7 +217,7 @@ TEST(GraphTest, ExplicitProb01MinMax) { EXPECT_EQ(364ul, statesWithProbability01.second.getNumberOfSetBits()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/coin2-2.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp); @@ -238,7 +238,7 @@ TEST(GraphTest, ExplicitProb01MinMax) { EXPECT_EQ(35ul, statesWithProbability01.second.getNumberOfSetBits()); program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/csma2-2.nm"); - model = storm::builder::ExplicitPrismModelBuilder<double>().translateProgram(program); + model = storm::builder::ExplicitPrismModelBuilder<double>(program).translate(); ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp); From fff7b2d5db96476d5afb4b21f000070547646005 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Fri, 26 Feb 2016 19:51:41 +0100 Subject: [PATCH 07/17] fixed an allocation issue, performance is now roughly the same as before but memory consumption is reduced Former-commit-id: ff4480497502ea159c87a8de216f469683ebf2ea --- src/builder/ExplicitPrismModelBuilder.cpp | 16 ++++++++-------- src/builder/ExplicitPrismModelBuilder.h | 6 +++++- src/utility/storm.h | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index c0188de50..9a0408a3c 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -67,7 +67,12 @@ namespace storm { } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), bitsPerState(bitsPerState), numberOfStates() { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::InternalStateInformation::InternalStateInformation() : stateStorage(64, 10), initialStateIndices(), bitsPerState(64), numberOfStates() { + // Intentionally left empty. + } + + template <typename ValueType, typename RewardModelType, typename StateType> + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), initialStateIndices(), bitsPerState(bitsPerState), numberOfStates() { // Intentionally left empty. } @@ -349,7 +354,7 @@ namespace storm { StateType currentIndex = internalStateInformation.stateStorage.getValue(currentState); statesToExplore.pop_front(); - STORM_LOG_TRACE("Exploring state with id " << index << "."); + STORM_LOG_TRACE("Exploring state with id " << currentIndex << "."); storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(currentState, stateToIdCallback); @@ -423,7 +428,6 @@ namespace storm { ++choiceRewardIt; ++builderIt; } - ++currentRow; } } @@ -435,11 +439,7 @@ namespace storm { template <typename ValueType, typename RewardModelType, typename StateType> typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels) { ModelComponents modelComponents; - - // Create the structure for storing the reachable state space. - uint64_t bitsPerState = ((variableInformation.getTotalBitOffset() / 64) + 1) * 64; - InternalStateInformation internalStateInformation(bitsPerState); - + // Determine whether we have to combine different choices to one or whether this model can have more than // one choice per state. bool deterministicModel = program.isDeterministicModel(); diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index a5e09bfae..3e5614854 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -45,7 +45,11 @@ namespace storm { public: // A structure holding information about the reachable state space while building it. struct InternalStateInformation { - InternalStateInformation(uint64_t bitsPerState = 64); + // Builds an empty state information. + InternalStateInformation(); + + // Creates a state information structure for storing states of the given bit width. + InternalStateInformation(uint64_t bitsPerState); // This member stores all the states and maps them to their unique indices. storm::storage::BitVectorHashMap<StateType> stateStorage; diff --git a/src/utility/storm.h b/src/utility/storm.h index d4a042bc6..ef2e7ac7a 100644 --- a/src/utility/storm.h +++ b/src/utility/storm.h @@ -106,8 +106,8 @@ namespace storm { options.buildCommandLabels = true; } - storm::builder::ExplicitPrismModelBuilder<ValueType> builder; - result.model = builder.translateProgram(program, options); + storm::builder::ExplicitPrismModelBuilder<ValueType> builder(program, options); + result.model = builder.translate(); translatedProgram = builder.getTranslatedProgram(); } else if (settings.getEngine() == storm::settings::modules::GeneralSettings::Engine::Dd || settings.getEngine() == storm::settings::modules::GeneralSettings::Engine::Hybrid) { typename storm::builder::DdPrismModelBuilder<LibraryType>::Options options; From 0dfdfe7db80e499c74d4373b7b084bbd9be21b20 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sat, 27 Feb 2016 16:25:19 +0100 Subject: [PATCH 08/17] using flat_map in model building instead of unordered_map Former-commit-id: ff895d2bcc95582afafdaae0760df92ea801693d --- src/generator/PrismNextStateGenerator.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index ad3ce3080..fac3be0f5 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -1,5 +1,7 @@ #include "src/generator/PrismNextStateGenerator.h" +#include <boost/container/flat_map.hpp> + #include "src/utility/constants.h" #include "src/utility/macros.h" #include "src/exceptions/WrongFormatException.h" @@ -306,8 +308,9 @@ namespace storm { // As long as there is one feasible combination of commands, keep on expanding it. bool done = false; while (!done) { - std::unordered_map<CompressedState, ValueType>* currentTargetStates = new std::unordered_map<CompressedState, ValueType>(); - std::unordered_map<CompressedState, ValueType>* newTargetStates = new std::unordered_map<CompressedState, ValueType>(); + boost::container::flat_map<CompressedState, ValueType>* currentTargetStates = new boost::container::flat_map<CompressedState, ValueType>(); + boost::container::flat_map<CompressedState, ValueType>* newTargetStates = new boost::container::flat_map<CompressedState, ValueType>(); + currentTargetStates->emplace(state, storm::utility::one<ValueType>()); for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { @@ -327,7 +330,7 @@ namespace storm { if (i < iteratorList.size() - 1) { delete currentTargetStates; currentTargetStates = newTargetStates; - newTargetStates = new std::unordered_map<CompressedState, ValueType>(); + newTargetStates = new boost::container::flat_map<CompressedState, ValueType>(); } } From 55fd1b66c3c0a302fe4a44f28de218e6aedee7aa Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 29 Feb 2016 16:06:01 +0100 Subject: [PATCH 09/17] introducing exploration orders to explicit builder Former-commit-id: a56620eac2c37abbda99a16ff2dc880813e2b185 --- src/builder/ExplicitPrismModelBuilder.cpp | 65 +++++++++++++++- src/builder/ExplicitPrismModelBuilder.h | 22 ++++-- src/builder/ExplorationOrder.cpp | 22 ++++++ src/builder/ExplorationOrder.h | 17 +++++ src/settings/modules/GeneralSettings.cpp | 21 ++++++ src/settings/modules/GeneralSettings.h | 19 ++++- src/storage/BitVectorHashMap.cpp | 10 +++ src/storage/BitVectorHashMap.h | 8 ++ src/storage/SparseMatrix.cpp | 92 +++++++++++++++++++---- src/storage/SparseMatrix.h | 16 ++++ src/storage/prism/RewardModel.cpp | 2 +- src/utility/constants.cpp | 4 + 12 files changed, 273 insertions(+), 25 deletions(-) create mode 100644 src/builder/ExplorationOrder.cpp create mode 100644 src/builder/ExplorationOrder.h diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 9a0408a3c..1aeab458d 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -24,6 +24,7 @@ namespace storm { namespace builder { + /*! * A structure that is used to keep track of a reward model currently being built. */ @@ -82,17 +83,17 @@ namespace storm { } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options() : buildCommandLabels(false), buildAllRewardModels(true), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options() : explorationOrder(storm::settings::generalSettings().getExplorationOrder()), buildCommandLabels(false), buildAllRewardModels(true), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { // Intentionally left empty. } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options(storm::logic::Formula const& formula) : buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(std::set<std::string>()), expressionLabels(std::vector<storm::expressions::Expression>()), terminalStates(), negatedTerminalStates() { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options(storm::logic::Formula const& formula) : explorationOrder(storm::settings::generalSettings().getExplorationOrder()), buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(std::set<std::string>()), expressionLabels(std::vector<storm::expressions::Expression>()), terminalStates(), negatedTerminalStates() { this->preserveFormula(formula); } template <typename ValueType, typename RewardModelType, typename StateType> - ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options(std::vector<std::shared_ptr<const storm::logic::Formula>> const& formulas) : buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { + ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options(std::vector<std::shared_ptr<const storm::logic::Formula>> const& formulas) : explorationOrder(storm::settings::generalSettings().getExplorationOrder()), buildCommandLabels(false), buildAllRewardModels(false), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() { if (formulas.empty()) { this->buildAllRewardModels = true; this->buildAllLabels = true; @@ -253,6 +254,7 @@ namespace storm { template <typename ValueType, typename RewardModelType, typename StateType> std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::translate() { STORM_LOG_DEBUG("Building representation of program:" << std::endl << program << std::endl); + STORM_LOG_DEBUG("Exploration order is: " << options.explorationOrder); // Select the appropriate reward models (after the constants have been substituted). std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels; @@ -314,7 +316,16 @@ namespace storm { std::pair<uint32_t, std::size_t> actualIndexBucketPair = internalStateInformation.stateStorage.findOrAddAndGetBucket(state, newIndex); if (actualIndexBucketPair.first == newIndex) { - statesToExplore.push_back(state); + if (options.explorationOrder == ExplorationOrder::Dfs) { + statesToExplore.push_front(state); + + // Reserve one slot for the new state in the remapping. + stateRemapping.get().push_back(storm::utility::zero<StateType>()); + } else if (options.explorationOrder == ExplorationOrder::Bfs) { + statesToExplore.push_back(state); + } else { + STORM_LOG_ASSERT(false, "Invalid exploration order."); + } ++internalStateInformation.numberOfStates; } @@ -341,10 +352,18 @@ namespace storm { // Create a callback for the next-state generator to enable it to request the index of states. std::function<StateType (CompressedState const&)> stateToIdCallback = std::bind(&ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this, std::placeholders::_1); + // If the exploration order is something different from breadth-first, we need to keep track of the remapping + // from state ids to row groups. For this, we actually store the reversed mapping of row groups to state-ids + // and later reverse it. + if (options.explorationOrder != ExplorationOrder::Bfs) { + stateRemapping = std::vector<uint_fast64_t>(); + } + // Let the generator create all initial states. this->internalStateInformation.initialStateIndices = generator.getInitialStates(stateToIdCallback); // Now explore the current state until there is no more reachable state. + uint_fast64_t currentRowGroup = 0; uint_fast64_t currentRow = 0; // Perform a search through the model. @@ -354,6 +373,12 @@ namespace storm { StateType currentIndex = internalStateInformation.stateStorage.getValue(currentState); statesToExplore.pop_front(); + // If the exploration order differs from breadth-first, we remember that this row group was actually + // filled with the transitions of a different state. + if (options.explorationOrder != ExplorationOrder::Bfs) { + stateRemapping.get()[currentIndex] = currentRowGroup; + } + STORM_LOG_TRACE("Exploring state with id " << currentIndex << "."); storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(currentState, stateToIdCallback); @@ -384,6 +409,7 @@ namespace storm { } ++currentRow; + ++currentRowGroup; } else { std::cout << unpackStateIntoValuation(currentState).toString(true) << std::endl; STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, @@ -430,9 +456,40 @@ namespace storm { } ++currentRow; } + ++currentRowGroup; } } + // If the exploration order was not breadth-first, we need to fix the entries in the matrix according to + // (reversed) mapping of row groups to indices. + if (options.explorationOrder != ExplorationOrder::Bfs) { + STORM_LOG_ASSERT(stateRemapping, "Unable to fix columns without mapping."); + std::vector<uint_fast64_t> const& remapping = stateRemapping.get(); + + // We need to fix the following entities: + // (a) the transition matrix + // (b) the initial states + // (c) the hash map storing the mapping states -> ids + + // Fix (a). + transitionMatrixBuilder.replaceColumns(remapping, 0); + + // Fix (b). + std::vector<StateType> newInitialStateIndices(this->internalStateInformation.initialStateIndices.size()); + std::transform(this->internalStateInformation.initialStateIndices.begin(), this->internalStateInformation.initialStateIndices.end(), newInitialStateIndices.begin(), [&remapping] (StateType const& state) { return remapping[state]; } ); + std::sort(newInitialStateIndices.begin(), newInitialStateIndices.end()); + this->internalStateInformation.initialStateIndices = std::move(newInitialStateIndices); + + // Fix (c). + std::unordered_map<StateType, StateType> valueRemapping; + for (StateType index = 0; index < remapping.size(); ++index) { + if (remapping[index] != index) { + valueRemapping.emplace(index, static_cast<StateType>(remapping[index])); + } + } + this->internalStateInformation.stateStorage.remap(valueRemapping); + } + return choiceLabels; } diff --git a/src/builder/ExplicitPrismModelBuilder.h b/src/builder/ExplicitPrismModelBuilder.h index 3e5614854..ab2eb9815 100644 --- a/src/builder/ExplicitPrismModelBuilder.h +++ b/src/builder/ExplicitPrismModelBuilder.h @@ -1,5 +1,5 @@ -#ifndef STORM_ADAPTERS_EXPLICITPRISMMODELBUILDER_H -#define STORM_ADAPTERS_EXPLICITPRISMMODELBUILDER_H +#ifndef STORM_BUILDER_EXPLICITPRISMMODELBUILDER_H +#define STORM_BUILDER_EXPLICITPRISMMODELBUILDER_H #include <memory> #include <utility> @@ -24,6 +24,8 @@ #include "src/utility/prism.h" +#include "src/builder/ExplorationOrder.h" + #include "src/generator/CompressedState.h" #include "src/generator/VariableInformation.h" @@ -102,6 +104,11 @@ namespace storm { */ Options(); + /*! + * Copies the given set of options. + */ + Options(Options const& other) = default; + /*! Creates an object representing the suggested building options assuming that the given formula is the * only one to check. Additional formulas may be preserved by calling <code>preserveFormula</code>. * @@ -144,6 +151,9 @@ namespace storm { */ void setTerminalStatesFromFormula(storm::logic::Formula const& formula); + // The order in which to explore the model. + ExplorationOrder explorationOrder; + // A flag that indicates whether or not command labels are to be built. bool buildCommandLabels; @@ -286,13 +296,13 @@ namespace storm { // A set of states that still need to be explored. std::deque<CompressedState> statesToExplore; - // An optional mapping from row groups to the indices of the states that they reflect. This needs to be built - // in case the exploration order is not BFS. -// boost::optional<std::vector<StateType, StateType>> rowGroupToIndexMapping; + // An optional mapping from state indices to the row groups in which they actually reside. This needs to be + // built in case the exploration order is not BFS. + boost::optional<std::vector<uint_fast64_t>> stateRemapping; }; } // namespace adapters } // namespace storm -#endif /* STORM_ADAPTERS_EXPLICITPRISMMODELBUILDER_H */ +#endif /* STORM_BUILDER_EXPLICITPRISMMODELBUILDER_H */ diff --git a/src/builder/ExplorationOrder.cpp b/src/builder/ExplorationOrder.cpp new file mode 100644 index 000000000..fb0b82766 --- /dev/null +++ b/src/builder/ExplorationOrder.cpp @@ -0,0 +1,22 @@ +#include "src/builder/ExplorationOrder.h" + +namespace storm { + namespace builder { + + std::ostream& operator<<(std::ostream& out, ExplorationOrder const& order) { + switch (order) { + case ExplorationOrder::Dfs: + out << "depth-first"; + break; + case ExplorationOrder::Bfs: + out << "breadth-first"; + break; + default: + out << "undefined"; + break; + } + return out; + } + + } +} \ No newline at end of file diff --git a/src/builder/ExplorationOrder.h b/src/builder/ExplorationOrder.h new file mode 100644 index 000000000..94c6363b3 --- /dev/null +++ b/src/builder/ExplorationOrder.h @@ -0,0 +1,17 @@ +#ifndef STORM_BUILDER_EXPLORATIONORDER_H_ +#define STORM_BUILDER_EXPLORATIONORDER_H_ + +#include <ostream> + +namespace storm { + namespace builder { + + // An enum that contains all currently supported exploration orders. + enum class ExplorationOrder { Dfs, Bfs }; + + std::ostream& operator<<(std::ostream& out, ExplorationOrder const& order); + + } +} + +#endif /* STORM_BUILDER_EXPLORATIONORDER_H_ */ \ No newline at end of file diff --git a/src/settings/modules/GeneralSettings.cpp b/src/settings/modules/GeneralSettings.cpp index 95a48931b..171b2a886 100644 --- a/src/settings/modules/GeneralSettings.cpp +++ b/src/settings/modules/GeneralSettings.cpp @@ -32,6 +32,8 @@ namespace storm { const std::string GeneralSettings::explicitOptionShortName = "exp"; const std::string GeneralSettings::symbolicOptionName = "symbolic"; const std::string GeneralSettings::symbolicOptionShortName = "s"; + const std::string GeneralSettings::explorationOrderOptionName = "explorder"; + const std::string GeneralSettings::explorationOrderOptionShortName = "eo"; const std::string GeneralSettings::propertyOptionName = "prop"; const std::string GeneralSettings::propertyOptionShortName = "prop"; const std::string GeneralSettings::transitionRewardsOptionName = "transrew"; @@ -83,6 +85,11 @@ namespace storm { .addArgument(storm::settings::ArgumentBuilder::createStringArgument("labeling filename", "The name of the file from which to read the state labeling.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, symbolicOptionName, false, "Parses the model given in a symbolic representation.").setShortName(symbolicOptionShortName) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the symbolic model.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); + + std::vector<std::string> explorationOrders = {"dfs", "bfs"}; + this->addOption(storm::settings::OptionBuilder(moduleName, explorationOrderOptionName, false, "Sets which exploration order to use.").setShortName(explorationOrderOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the exploration order to choose. Available are: dfs and bfs.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(explorationOrders)).setDefaultValueString("dfs").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, propertyOptionName, false, "Specifies the formulas to be checked on the model.").setShortName(propertyOptionShortName) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("formula or filename", "The formula or the file containing the formulas.").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, counterexampleOptionName, false, "Generates a counterexample for the given PRCTL formulas if not satisfied by the model") @@ -186,6 +193,20 @@ namespace storm { return this->getOption(symbolicOptionName).getArgumentByName("filename").getValueAsString(); } + bool GeneralSettings::isExplorationOrderSet() const { + return this->getOption(explorationOrderOptionName).getHasOptionBeenSet(); + } + + storm::builder::ExplorationOrder GeneralSettings::getExplorationOrder() const { + std::string explorationOrderAsString = this->getOption(explorationOrderOptionName).getArgumentByName("name").getValueAsString(); + if (explorationOrderAsString == "dfs") { + return storm::builder::ExplorationOrder::Dfs; + } else if (explorationOrderAsString == "bfs") { + return storm::builder::ExplorationOrder::Bfs; + } + STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown exploration order '" << explorationOrderAsString << "'."); + } + bool GeneralSettings::isPropertySet() const { return this->getOption(propertyOptionName).getHasOptionBeenSet(); } diff --git a/src/settings/modules/GeneralSettings.h b/src/settings/modules/GeneralSettings.h index 8144c0a1f..ad268db93 100644 --- a/src/settings/modules/GeneralSettings.h +++ b/src/settings/modules/GeneralSettings.h @@ -4,6 +4,8 @@ #include "storm-config.h" #include "src/settings/modules/ModuleSettings.h" +#include "src/builder/ExplorationOrder.h" + namespace storm { namespace solver { enum class EquationSolverType; @@ -25,7 +27,6 @@ namespace storm { class GeneralSettings : public ModuleSettings { public: // An enumeration of all engines. - enum class Engine { Sparse, Hybrid, Dd, AbstractionRefinement }; @@ -138,6 +139,20 @@ namespace storm { * @return The name of the file that contains the symbolic model specification. */ std::string getSymbolicModelFilename() const; + + /*! + * Retrieves whether the model exploration order was set. + * + * @return True if the model exploration option was set. + */ + bool isExplorationOrderSet() const; + + /*! + * Retrieves the exploration order if it was set. + * + * @return The chosen exploration order. + */ + storm::builder::ExplorationOrder getExplorationOrder() const; /*! * Retrieves whether the property option was set. @@ -389,6 +404,8 @@ namespace storm { static const std::string explicitOptionShortName; static const std::string symbolicOptionName; static const std::string symbolicOptionShortName; + static const std::string explorationOrderOptionName; + static const std::string explorationOrderOptionShortName; static const std::string propertyOptionName; static const std::string propertyOptionShortName; static const std::string transitionRewardsOptionName; diff --git a/src/storage/BitVectorHashMap.cpp b/src/storage/BitVectorHashMap.cpp index 881fc016a..fd4304eaa 100644 --- a/src/storage/BitVectorHashMap.cpp +++ b/src/storage/BitVectorHashMap.cpp @@ -233,6 +233,16 @@ namespace storm { return std::make_pair(buckets.get(bucket * bucketSize, bucketSize), values[bucket]); } + template<class ValueType, class Hash1, class Hash2> + void BitVectorHashMap<ValueType, Hash1, Hash2>::remap(std::unordered_map<ValueType, ValueType> const& remapping) { + for (auto pos : occupied) { + auto it = remapping.find(values[pos]); + if (it != remapping.end()) { + values[pos] = it->second; + } + } + } + template class BitVectorHashMap<uint_fast64_t>; template class BitVectorHashMap<uint32_t>; } diff --git a/src/storage/BitVectorHashMap.h b/src/storage/BitVectorHashMap.h index c53928b09..7e8a1dc0e 100644 --- a/src/storage/BitVectorHashMap.h +++ b/src/storage/BitVectorHashMap.h @@ -3,6 +3,7 @@ #include <cstdint> #include <functional> +#include <unordered_map> #include "src/storage/BitVector.h" @@ -123,6 +124,13 @@ namespace storm { */ std::size_t capacity() const; + /*! + * Performs a remapping of all values stored by applying the given remapping. + * + * @param remapping The remapping to apply. + */ + void remap(std::unordered_map<ValueType, ValueType> const& remapping); + private: /*! * Retrieves whether the given bucket holds a value. diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 3dbabf854..08138d48c 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -143,12 +143,12 @@ namespace storm { STORM_LOG_THROW(startingRow >= lastRow, storm::exceptions::InvalidStateException, "Illegal row group with negative size."); rowGroupIndices.push_back(startingRow); ++currentRowGroup; - + // Close all rows from the most recent one to the starting row. for (index_type i = lastRow + 1; i <= startingRow; ++i) { rowIndications.push_back(currentEntryCount); } - + // Reset the most recently seen row/column to allow for proper insertion of the following elements. lastRow = startingRow; lastColumn = 0; @@ -162,7 +162,7 @@ namespace storm { rowCount = std::max(rowCount, initialRowCount); } rowCount = std::max(rowCount, overriddenRowCount); - + // If the current row count was overridden, we may need to add empty rows. for (index_type i = lastRow + 1; i < rowCount; ++i) { rowIndications.push_back(currentEntryCount); @@ -202,7 +202,7 @@ namespace storm { rowGroupIndices.push_back(rowCount); } } - + return SparseMatrix<ValueType>(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), hasCustomRowGrouping); } @@ -216,6 +216,72 @@ namespace storm { return lastColumn; } + template<typename ValueType> + bool SparseMatrixBuilder<ValueType>::replaceColumns(std::vector<index_type> const& replacements, index_type offset) { + bool changed = false; + + // Sort columns per row. + typename SparseMatrix<ValueType>::index_type endGroups; + typename SparseMatrix<ValueType>::index_type endRows; + for (index_type group = 0; group < rowGroupIndices.size(); ++group) { + endGroups = group < rowGroupIndices.size()-1 ? rowGroupIndices[group+1] : rowIndications.size(); + for (index_type i = rowGroupIndices[group]; i < endGroups; ++i) { + bool changedRow = false; + endRows = i < rowIndications.size()-1 ? rowIndications[i+1] : columnsAndValues.size(); + + for (auto it = columnsAndValues.begin() + rowIndications[i], ite = columnsAndValues.begin() + endRows; it != ite; ++it) { + if (it->getColumn() >= offset) { + it->setColumn(replacements[it->getColumn() - offset]); + changedRow = true; + } + // Update highest column in a way that only works if the highest appearing index does not become + // lower during performing the replacement. + highestColumn = std::max(highestColumn, it->getColumn()); + } + + if (changedRow) { + changed = true; + + // Sort the row. + std::sort(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { + return a.getColumn() < b.getColumn(); + }); + // Assert no equal elements + STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { + return a.getColumn() <= b.getColumn(); + }), "Must not have different elements with the same column in a row."); + } + } + } + + return changed; + } + + template<typename ValueType> + void SparseMatrixBuilder<ValueType>::fixColumns() { + // Sort columns per row. + typename SparseMatrix<ValueType>::index_type endGroups; + typename SparseMatrix<ValueType>::index_type endRows; + for (index_type group = 0; group < rowGroupIndices.size(); ++group) { + endGroups = group < rowGroupIndices.size()-1 ? rowGroupIndices[group+1] : rowIndications.size(); + for (index_type i = rowGroupIndices[group]; i < endGroups; ++i) { + endRows = i < rowIndications.size()-1 ? rowIndications[i+1] : columnsAndValues.size(); + // Sort the row. + std::sort(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { + return a.getColumn() < b.getColumn(); + }); + // Assert no equal elements + STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { + return a.getColumn() <= b.getColumn(); + }), "Must not have different elements with the same column in a row."); + } + } + } + template<typename ValueType> SparseMatrix<ValueType>::rows::rows(iterator begin, index_type entryCount) : beginIterator(begin), entryCount(entryCount) { // Intentionally left empty. @@ -532,12 +598,12 @@ namespace storm { *it = i; } auto res = getSubmatrix(rowConstraint, columnConstraint, fakeRowGroupIndices, insertDiagonalElements); - + // Create a new row grouping that reflects the new sizes of the row groups. std::vector<uint_fast64_t> newRowGroupIndices; newRowGroupIndices.push_back(0); auto selectedRowIt = rowConstraint.begin(); - + // For this, we need to count how many rows were preserved in every group. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { uint_fast64_t newRowCount = 0; @@ -1172,7 +1238,7 @@ namespace storm { assert(this->getRowGroupSize(group) == 1); for (typename SparseMatrix<ValueType>::index_type i = this->getRowGroupIndices()[group]; i < this->getRowGroupIndices()[group + 1]; ++i) { typename SparseMatrix<ValueType>::index_type nextIndex = this->rowIndications[i]; - + // Print the actual row. out << i << "\t("; typename SparseMatrix<ValueType>::index_type currentRealIndex = 0; @@ -1226,7 +1292,7 @@ namespace storm { template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix); template std::vector<double> SparseMatrix<double>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<double> const& otherMatrix) const; template bool SparseMatrix<double>::isSubmatrixOf(SparseMatrix<double> const& matrix) const; - + // float template class MatrixEntry<typename SparseMatrix<float>::index_type, float>; template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<float>::index_type, float> const& entry); @@ -1235,7 +1301,7 @@ namespace storm { template std::ostream& operator<<(std::ostream& out, SparseMatrix<float> const& matrix); template std::vector<float> SparseMatrix<float>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<float> const& otherMatrix) const; template bool SparseMatrix<float>::isSubmatrixOf(SparseMatrix<float> const& matrix) const; - + // int template class MatrixEntry<typename SparseMatrix<int>::index_type, int>; template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<int>::index_type, int> const& entry); @@ -1243,7 +1309,7 @@ namespace storm { template class SparseMatrix<int>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix); template bool SparseMatrix<int>::isSubmatrixOf(SparseMatrix<int> const& matrix) const; - + // state_type template class MatrixEntry<typename SparseMatrix<storm::storage::sparse::state_type>::index_type, storm::storage::sparse::state_type>; template std::ostream& operator<<(std::ostream& out, MatrixEntry<typename SparseMatrix<storm::storage::sparse::state_type>::index_type, storm::storage::sparse::state_type> const& entry); @@ -1251,7 +1317,7 @@ namespace storm { template class SparseMatrix<storm::storage::sparse::state_type>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<storm::storage::sparse::state_type> const& matrix); template bool SparseMatrix<int>::isSubmatrixOf(SparseMatrix<storm::storage::sparse::state_type> const& matrix) const; - + #ifdef STORM_HAVE_CARL // Rat Function template class MatrixEntry<typename SparseMatrix<RationalFunction>::index_type, RationalFunction>; @@ -1264,7 +1330,7 @@ namespace storm { template std::vector<storm::RationalFunction> SparseMatrix<float>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<storm::RationalFunction> const& otherMatrix) const; template std::vector<storm::RationalFunction> SparseMatrix<int>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<storm::RationalFunction> const& otherMatrix) const; template bool SparseMatrix<storm::RationalFunction>::isSubmatrixOf(SparseMatrix<storm::RationalFunction> const& matrix) const; - + // Intervals template std::vector<storm::Interval> SparseMatrix<double>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<storm::Interval> const& otherMatrix) const; template class MatrixEntry<typename SparseMatrix<Interval>::index_type, Interval>; @@ -1274,7 +1340,7 @@ namespace storm { template std::ostream& operator<<(std::ostream& out, SparseMatrix<Interval> const& matrix); template std::vector<storm::Interval> SparseMatrix<Interval>::getPointwiseProductRowSumVector(storm::storage::SparseMatrix<storm::Interval> const& otherMatrix) const; template bool SparseMatrix<storm::Interval>::isSubmatrixOf(SparseMatrix<storm::Interval> const& matrix) const; - + template bool SparseMatrix<storm::Interval>::isSubmatrixOf(SparseMatrix<double> const& matrix) const; #endif diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index f9d9916c4..4892dbe32 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -217,6 +217,17 @@ namespace storm { */ index_type getLastColumn() const; + /*! + * Replaces all columns with id > offset according to replacements. + * Every state with id offset+i is replaced by the id in replacements[i]. + * Afterwards the columns are sorted. + * + * @param replacements Mapping indicating the replacements from offset+i -> value of i. + * @param offset Offset to add to each id in vector index. + * @return True if replacement took place, False if nothing changed. + */ + bool replaceColumns(std::vector<index_type> const& replacements, index_type offset); + private: // A flag indicating whether a row count was set upon construction. bool initialRowCountSet; @@ -277,6 +288,11 @@ namespace storm { // Stores the currently active row group. This is used for correctly constructing the row grouping of the // matrix. index_type currentRowGroup; + + /*! + * Fixes the matrix by sorting the columns to gain increasing order again. + */ + void fixColumns(); }; /*! diff --git a/src/storage/prism/RewardModel.cpp b/src/storage/prism/RewardModel.cpp index 3f38f7bd8..30c43728c 100644 --- a/src/storage/prism/RewardModel.cpp +++ b/src/storage/prism/RewardModel.cpp @@ -84,7 +84,7 @@ namespace storm { std::ostream& operator<<(std::ostream& stream, RewardModel const& rewardModel) { stream << "rewards"; if (rewardModel.getName() != "") { - std::cout << " \"" << rewardModel.getName() << "\""; + stream << " \"" << rewardModel.getName() << "\""; } stream << std::endl; for (auto const& reward : rewardModel.getStateRewards()) { diff --git a/src/utility/constants.cpp b/src/utility/constants.cpp index 900482a72..941f07f7e 100644 --- a/src/utility/constants.cpp +++ b/src/utility/constants.cpp @@ -192,6 +192,10 @@ namespace storm { template bool isZero(storm::storage::sparse::state_type const& value); template bool isConstant(storm::storage::sparse::state_type const& value); + template uint32_t one(); + template uint32_t zero(); + template uint32_t infinity(); + template storm::storage::sparse::state_type one(); template storm::storage::sparse::state_type zero(); template storm::storage::sparse::state_type infinity(); From ffe63ea95d63b2bdcbf49573b0217acefcd28ba7 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 29 Feb 2016 22:10:30 +0100 Subject: [PATCH 10/17] made dfs as exploration order available Former-commit-id: 46ea31af78b5819690fcc0a47ef9c1565c109c2b --- src/builder/ExplicitPrismModelBuilder.cpp | 12 +--- src/parser/PrismParser.cpp | 4 +- src/settings/ArgumentValidators.h | 16 ++++-- src/storage/BitVectorHashMap.cpp | 7 +-- src/storage/BitVectorHashMap.h | 3 +- src/storage/SparseMatrix.cpp | 70 +++++++---------------- src/storage/SparseMatrix.h | 5 -- 7 files changed, 40 insertions(+), 77 deletions(-) diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 1aeab458d..1ef7f3ad8 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -231,8 +231,8 @@ namespace storm { } // Now that the program is fixed, we we need to substitute all constants with their concrete value. - this->program = program.substituteConstants(); - + this->program = this->program.substituteConstants(); + // Create the variable information for the transformed program. this->variableInformation = VariableInformation(this->program); @@ -481,13 +481,7 @@ namespace storm { this->internalStateInformation.initialStateIndices = std::move(newInitialStateIndices); // Fix (c). - std::unordered_map<StateType, StateType> valueRemapping; - for (StateType index = 0; index < remapping.size(); ++index) { - if (remapping[index] != index) { - valueRemapping.emplace(index, static_cast<StateType>(remapping[index])); - } - } - this->internalStateInformation.stateStorage.remap(valueRemapping); + this->internalStateInformation.stateStorage.remap([&remapping] (StateType const& state) { return remapping[state]; } ); } return choiceLabels; diff --git a/src/parser/PrismParser.cpp b/src/parser/PrismParser.cpp index 9040f3cf4..b2a153f99 100644 --- a/src/parser/PrismParser.cpp +++ b/src/parser/PrismParser.cpp @@ -11,7 +11,7 @@ namespace storm { namespace parser { storm::prism::Program PrismParser::parse(std::string const& filename) { // Open file and initialize result. - std::ifstream inputFileStream(filename, std::ios::in); + std::ifstream inputFileStream(filename); STORM_LOG_THROW(inputFileStream.good(), storm::exceptions::WrongFormatException, "Unable to read from file '" << filename << "'."); storm::prism::Program result; @@ -35,7 +35,7 @@ namespace storm { PositionIteratorType first(input.begin()); PositionIteratorType iter = first; PositionIteratorType last(input.end()); - assert(first != last); + STORM_LOG_ASSERT(first != last, "Illegal input to PRISM parser."); // Create empty result; storm::prism::Program result; diff --git a/src/settings/ArgumentValidators.h b/src/settings/ArgumentValidators.h index 235ddd765..6e1b4aa02 100644 --- a/src/settings/ArgumentValidators.h +++ b/src/settings/ArgumentValidators.h @@ -18,6 +18,7 @@ #include "src/exceptions/IllegalArgumentValueException.h" #include "src/exceptions/IllegalFunctionCallException.h" +#include <sys/stat.h> namespace storm { namespace settings { @@ -97,11 +98,16 @@ namespace storm { */ static std::function<bool (std::string const&)> existingReadableFileValidator() { return [] (std::string const fileName) -> bool { - std::ifstream targetFile(fileName); - bool isFileGood = targetFile.good(); - - STORM_LOG_THROW(isFileGood, storm::exceptions::IllegalArgumentValueException, "The file " << fileName << " does not exist or is not readable."); - return isFileGood; + // First check existence as ifstream::good apparently als returns true for directories. + struct stat info; + stat(fileName.c_str(), &info); + STORM_LOG_THROW(info.st_mode & S_IFREG, storm::exceptions::IllegalArgumentValueException, "Unable to read from non-existing file '" << fileName << "'."); + + // Now that we know it's a file, we can check its readability. + std::ifstream istream(fileName); + STORM_LOG_THROW(istream.good(), storm::exceptions::IllegalArgumentValueException, "Unable to read from file '" << fileName << "'."); + + return true; }; } diff --git a/src/storage/BitVectorHashMap.cpp b/src/storage/BitVectorHashMap.cpp index fd4304eaa..66ff43f96 100644 --- a/src/storage/BitVectorHashMap.cpp +++ b/src/storage/BitVectorHashMap.cpp @@ -234,12 +234,9 @@ namespace storm { } template<class ValueType, class Hash1, class Hash2> - void BitVectorHashMap<ValueType, Hash1, Hash2>::remap(std::unordered_map<ValueType, ValueType> const& remapping) { + void BitVectorHashMap<ValueType, Hash1, Hash2>::remap(std::function<ValueType(ValueType const&)> const& remapping) { for (auto pos : occupied) { - auto it = remapping.find(values[pos]); - if (it != remapping.end()) { - values[pos] = it->second; - } + values[pos] = remapping(values[pos]); } } diff --git a/src/storage/BitVectorHashMap.h b/src/storage/BitVectorHashMap.h index 7e8a1dc0e..746467203 100644 --- a/src/storage/BitVectorHashMap.h +++ b/src/storage/BitVectorHashMap.h @@ -3,7 +3,6 @@ #include <cstdint> #include <functional> -#include <unordered_map> #include "src/storage/BitVector.h" @@ -129,7 +128,7 @@ namespace storm { * * @param remapping The remapping to apply. */ - void remap(std::unordered_map<ValueType, ValueType> const& remapping); + void remap(std::function<ValueType(ValueType const&)> const& remapping); private: /*! diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 08138d48c..9d390185f 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -218,68 +218,40 @@ namespace storm { template<typename ValueType> bool SparseMatrixBuilder<ValueType>::replaceColumns(std::vector<index_type> const& replacements, index_type offset) { - bool changed = false; + bool matrixChanged = false; - // Sort columns per row. - typename SparseMatrix<ValueType>::index_type endGroups; - typename SparseMatrix<ValueType>::index_type endRows; - for (index_type group = 0; group < rowGroupIndices.size(); ++group) { - endGroups = group < rowGroupIndices.size()-1 ? rowGroupIndices[group+1] : rowIndications.size(); - for (index_type i = rowGroupIndices[group]; i < endGroups; ++i) { - bool changedRow = false; - endRows = i < rowIndications.size()-1 ? rowIndications[i+1] : columnsAndValues.size(); - - for (auto it = columnsAndValues.begin() + rowIndications[i], ite = columnsAndValues.begin() + endRows; it != ite; ++it) { - if (it->getColumn() >= offset) { - it->setColumn(replacements[it->getColumn() - offset]); - changedRow = true; - } - // Update highest column in a way that only works if the highest appearing index does not become - // lower during performing the replacement. - highestColumn = std::max(highestColumn, it->getColumn()); - } - - if (changedRow) { - changed = true; - - // Sort the row. - std::sort(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, - [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { - return a.getColumn() < b.getColumn(); - }); - // Assert no equal elements - STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, - [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { - return a.getColumn() <= b.getColumn(); - }), "Must not have different elements with the same column in a row."); + // Walk through all rows. + for (index_type row = 0; row < rowIndications.size(); ++row) { + bool rowChanged = false; + index_type rowEnd = row < rowIndications.size()-1 ? rowIndications[row+1] : columnsAndValues.size(); + + for (auto it = columnsAndValues.begin() + rowIndications[row], ite = columnsAndValues.begin() + rowEnd; it != ite; ++it) { + if (it->getColumn() >= offset && it->getColumn() != replacements[it->getColumn() - offset]) { + it->setColumn(replacements[it->getColumn() - offset]); + rowChanged = true; } + // Update highest column in a way that only works if the highest appearing index does not become + // lower during performing the replacement. + highestColumn = std::max(highestColumn, it->getColumn()); } - } - - return changed; - } - - template<typename ValueType> - void SparseMatrixBuilder<ValueType>::fixColumns() { - // Sort columns per row. - typename SparseMatrix<ValueType>::index_type endGroups; - typename SparseMatrix<ValueType>::index_type endRows; - for (index_type group = 0; group < rowGroupIndices.size(); ++group) { - endGroups = group < rowGroupIndices.size()-1 ? rowGroupIndices[group+1] : rowIndications.size(); - for (index_type i = rowGroupIndices[group]; i < endGroups; ++i) { - endRows = i < rowIndications.size()-1 ? rowIndications[i+1] : columnsAndValues.size(); + + if (rowChanged) { + matrixChanged = true; + // Sort the row. - std::sort(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + std::sort(columnsAndValues.begin() + rowIndications[row], columnsAndValues.begin() + rowEnd, [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { return a.getColumn() < b.getColumn(); }); // Assert no equal elements - STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[row], columnsAndValues.begin() + rowEnd, [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { return a.getColumn() <= b.getColumn(); }), "Must not have different elements with the same column in a row."); } } + + return matrixChanged; } template<typename ValueType> diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index 4892dbe32..1ecee566f 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -288,11 +288,6 @@ namespace storm { // Stores the currently active row group. This is used for correctly constructing the row grouping of the // matrix. index_type currentRowGroup; - - /*! - * Fixes the matrix by sorting the columns to gain increasing order again. - */ - void fixColumns(); }; /*! From c45812c66ae44a62ef41b2921915c2d0f5544164 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 1 Mar 2016 19:22:08 +0100 Subject: [PATCH 11/17] made bfs the default exploration order again Former-commit-id: 6476c48a6798c7a48343da7626e27d28fe57ace5 --- src/settings/modules/GeneralSettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/modules/GeneralSettings.cpp b/src/settings/modules/GeneralSettings.cpp index 171b2a886..9979c6824 100644 --- a/src/settings/modules/GeneralSettings.cpp +++ b/src/settings/modules/GeneralSettings.cpp @@ -88,7 +88,7 @@ namespace storm { std::vector<std::string> explorationOrders = {"dfs", "bfs"}; this->addOption(storm::settings::OptionBuilder(moduleName, explorationOrderOptionName, false, "Sets which exploration order to use.").setShortName(explorationOrderOptionShortName) - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the exploration order to choose. Available are: dfs and bfs.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(explorationOrders)).setDefaultValueString("dfs").build()).build()); + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the exploration order to choose. Available are: dfs and bfs.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(explorationOrders)).setDefaultValueString("bfs").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propertyOptionName, false, "Specifies the formulas to be checked on the model.").setShortName(propertyOptionShortName) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("formula or filename", "The formula or the file containing the formulas.").build()).build()); From 1f5439e270fda649a63f6be8f4fdf75cad829eda Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sun, 6 Mar 2016 20:08:53 +0100 Subject: [PATCH 12/17] added state labeling generator interface Former-commit-id: eb7668741fa1561dbb2bd3c043a7b4b5674327fe --- src/builder/ExplicitPrismModelBuilder.cpp | 30 ++--------- src/generator/NextStateGenerator.h | 2 +- src/generator/PrismNextStateGenerator.h | 2 +- src/generator/PrismStateLabelingGenerator.cpp | 50 +++++++++++++++++++ src/generator/PrismStateLabelingGenerator.h | 31 ++++++++++++ src/generator/StateLabelingGenerator.h | 20 ++++++++ 6 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 src/generator/PrismStateLabelingGenerator.cpp create mode 100644 src/generator/PrismStateLabelingGenerator.h create mode 100644 src/generator/StateLabelingGenerator.h diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index 1ef7f3ad8..ef53b9e43 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -12,6 +12,7 @@ #include "src/settings/modules/GeneralSettings.h" #include "src/generator/PrismNextStateGenerator.h" +#include "src/generator/PrismStateLabelingGenerator.h" #include "src/utility/prism.h" #include "src/utility/constants.h" @@ -558,33 +559,8 @@ namespace storm { template <typename ValueType, typename RewardModelType, typename StateType> storm::models::sparse::StateLabeling ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildStateLabeling() { - std::vector<storm::prism::Label> const& labels = program.getLabels(); - - storm::expressions::ExpressionEvaluator<ValueType> evaluator(program.getManager()); - storm::models::sparse::StateLabeling result(internalStateInformation.numberOfStates); - - // Initialize labeling. - for (auto const& label : labels) { - result.addLabel(label.getName()); - } - for (auto const& stateIndexPair : internalStateInformation.stateStorage) { - unpackStateIntoEvaluator(stateIndexPair.first, variableInformation, evaluator); - - for (auto const& label : labels) { - // Add label to state, if the corresponding expression is true. - if (evaluator.asBool(label.getStatePredicateExpression())) { - result.addLabelToState(label.getName(), stateIndexPair.second); - } - } - } - - // Also label the initial state with the special label "init". - result.addLabel("init"); - for (auto index : internalStateInformation.initialStateIndices) { - result.addLabelToState("init", index); - } - - return result; + storm::generator::PrismStateLabelingGenerator<ValueType, StateType> generator(program, variableInformation); + return generator.generate(internalStateInformation.stateStorage, internalStateInformation.initialStateIndices); } // Explicitly instantiate the class. diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index f70709779..779a49ef3 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -21,4 +21,4 @@ namespace storm { } } -#endif \ No newline at end of file +#endif /* STORM_GENERATOR_NEXTSTATEGENERATOR_H_ */ \ No newline at end of file diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index e9c63502e..d8eddc0ac 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -92,7 +92,7 @@ namespace storm { // An optional expression that governs which states must not be explored. boost::optional<storm::expressions::Expression> terminalExpression; - // Information about how the variables are packed + // Information about how the variables are packed. VariableInformation const& variableInformation; // An evaluator used to evaluate expressions. diff --git a/src/generator/PrismStateLabelingGenerator.cpp b/src/generator/PrismStateLabelingGenerator.cpp new file mode 100644 index 000000000..6022ce063 --- /dev/null +++ b/src/generator/PrismStateLabelingGenerator.cpp @@ -0,0 +1,50 @@ +#include "src/generator/PrismStateLabelingGenerator.h" + +#include "src/generator/CompressedState.h" + +#include "src/storage/expressions/ExpressionEvaluator.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType> + PrismStateLabelingGenerator<ValueType, StateType>::PrismStateLabelingGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation) : program(program), variableInformation(variableInformation) { + // Intentionally left empty. + } + + template<typename ValueType, typename StateType> + storm::models::sparse::StateLabeling PrismStateLabelingGenerator<ValueType, StateType>::generate(storm::storage::BitVectorHashMap<StateType> const& states, std::vector<StateType> const& initialStateIndices) { + std::vector<storm::prism::Label> const& labels = program.getLabels(); + + storm::expressions::ExpressionEvaluator<ValueType> evaluator(program.getManager()); + storm::models::sparse::StateLabeling result(states.size()); + + // Initialize labeling. + for (auto const& label : labels) { + result.addLabel(label.getName()); + } + for (auto const& stateIndexPair : states) { + unpackStateIntoEvaluator(stateIndexPair.first, variableInformation, evaluator); + + for (auto const& label : labels) { + // Add label to state, if the corresponding expression is true. + if (evaluator.asBool(label.getStatePredicateExpression())) { + result.addLabelToState(label.getName(), stateIndexPair.second); + } + } + } + + // Also label the initial state with the special label "init". + result.addLabel("init"); + for (auto index : initialStateIndices) { + result.addLabelToState("init", index); + } + + return result; + } + + template class PrismStateLabelingGenerator<double, uint32_t>; + template class PrismStateLabelingGenerator<storm::RationalFunction, uint32_t>; + + } +} \ No newline at end of file diff --git a/src/generator/PrismStateLabelingGenerator.h b/src/generator/PrismStateLabelingGenerator.h new file mode 100644 index 000000000..9859453ba --- /dev/null +++ b/src/generator/PrismStateLabelingGenerator.h @@ -0,0 +1,31 @@ +#ifndef STORM_GENERATOR_PRISMSTATELABELINGGENERATOR_H_ +#define STORM_GENERATOR_PRISMSTATELABELINGGENERATOR_H_ + +#include "src/generator/StateLabelingGenerator.h" + +#include "src/generator/VariableInformation.h" + +#include "src/storage/prism/Program.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType = uint32_t> + class PrismStateLabelingGenerator : public StateLabelingGenerator<StateType> { + public: + PrismStateLabelingGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation); + + virtual storm::models::sparse::StateLabeling generate(storm::storage::BitVectorHashMap<StateType> const& states, std::vector<StateType> const& initialStateIndices = {}) override; + + private: + // The program for which to generate the labels. + storm::prism::Program const& program; + + // Information about how the variables are packed. + VariableInformation const& variableInformation; + }; + + } +} + +#endif /* STORM_GENERATOR_PRISMSTATELABELINGGENERATOR_H_ */ \ No newline at end of file diff --git a/src/generator/StateLabelingGenerator.h b/src/generator/StateLabelingGenerator.h new file mode 100644 index 000000000..17401c703 --- /dev/null +++ b/src/generator/StateLabelingGenerator.h @@ -0,0 +1,20 @@ +#ifndef STORM_GENERATOR_STATELABELINGGENERATOR_H_ +#define STORM_GENERATOR_STATELABELINGGENERATOR_H_ + +#include "src/models/sparse/StateLabeling.h" + +#include "src/storage/BitVectorHashMap.h" + +namespace storm { + namespace generator { + + template<typename StateType = uint32_t> + class StateLabelingGenerator { + public: + virtual storm::models::sparse::StateLabeling generate(storm::storage::BitVectorHashMap<StateType> const& states, std::vector<StateType> const& initialStateIndices = {}) = 0; + }; + + } +} + +#endif /* STORM_GENERATOR_STATELABELINGGENERATOR_H_ */ \ No newline at end of file From f81ce1cac193a01fab313dd4a7a6ce4cf6abd9e2 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 7 Mar 2016 16:07:40 +0100 Subject: [PATCH 13/17] started making row grouping optional Former-commit-id: b90ae91e754087bc55f35993b66713ab60edc7e5 --- src/storage/SparseMatrix.cpp | 198 ++++++++++++++++++++++------------- src/storage/SparseMatrix.h | 46 ++++---- 2 files changed, 148 insertions(+), 96 deletions(-) diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 9d390185f..83bc7dc83 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -80,8 +80,9 @@ namespace storm { if (initialEntryCountSet) { columnsAndValues.reserve(initialEntryCount); } - if (initialRowGroupCountSet) { - rowGroupIndices.reserve(initialRowGroupCount + 1); + if (initialRowGroupCountSet && hasCustomRowGrouping) { + rowGroupIndices = std::vector<index_type>(); + rowGroupIndices.get().reserve(initialRowGroupCount + 1); } rowIndications.push_back(0); } @@ -92,8 +93,8 @@ namespace storm { // If the matrix has a custom row grouping, we move it and remove the last element to make it 'open' again. if (hasCustomRowGrouping) { rowGroupIndices = std::move(matrix.rowGroupIndices); - rowGroupIndices.pop_back(); - currentRowGroup = rowGroupIndices.size() - 1; + rowGroupIndices.get().pop_back(); + currentRowGroup = rowGroupIndices.get().size() - 1; } // Likewise, we need to 'open' the row indications again. @@ -141,7 +142,7 @@ namespace storm { void SparseMatrixBuilder<ValueType>::newRowGroup(index_type startingRow) { STORM_LOG_THROW(hasCustomRowGrouping, storm::exceptions::InvalidStateException, "Matrix was not created to have a custom row grouping."); STORM_LOG_THROW(startingRow >= lastRow, storm::exceptions::InvalidStateException, "Illegal row group with negative size."); - rowGroupIndices.push_back(startingRow); + rowGroupIndices.get().push_back(startingRow); ++currentRowGroup; // Close all rows from the most recent one to the starting row. @@ -186,11 +187,7 @@ namespace storm { } // Check whether row groups are missing some entries. - if (!hasCustomRowGrouping) { - for (index_type i = 0; i <= rowCount; ++i) { - rowGroupIndices.push_back(i); - } - } else { + if (hasCustomRowGrouping) { uint_fast64_t rowGroupCount = currentRowGroup; if (initialRowGroupCountSet && forceInitialDimensions) { STORM_LOG_THROW(rowGroupCount <= initialRowGroupCount, storm::exceptions::InvalidStateException, "Expected not more than " << initialRowGroupCount << " row groups, but got " << rowGroupCount << "."); @@ -199,7 +196,7 @@ namespace storm { rowGroupCount = std::max(rowGroupCount, overriddenRowGroupCount); for (index_type i = currentRowGroup; i <= rowGroupCount; ++i) { - rowGroupIndices.push_back(rowCount); + rowGroupIndices.get().push_back(rowCount); } } @@ -295,12 +292,12 @@ namespace storm { } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), nontrivialRowGrouping(false), rowGroupIndices() { + SparseMatrix<ValueType>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { // Intentionally left empty. } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), nontrivialRowGrouping(other.nontrivialRowGrouping), rowGroupIndices(other.rowGroupIndices) { + SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { // Intentionally left empty. } @@ -312,7 +309,7 @@ namespace storm { } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), nontrivialRowGrouping(other.nontrivialRowGrouping), rowGroupIndices(std::move(other.rowGroupIndices)) { + SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { // Now update the source matrix other.rowCount = 0; other.columnCount = 0; @@ -320,12 +317,12 @@ namespace storm { } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type> const& rowIndications, std::vector<MatrixEntry<index_type, ValueType>> const& columnsAndValues, std::vector<index_type> const& rowGroupIndices, bool nontrivialRowGrouping) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), nontrivialRowGrouping(nontrivialRowGrouping), rowGroupIndices(rowGroupIndices) { + SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type> const& rowIndications, std::vector<MatrixEntry<index_type, ValueType>> const& columnsAndValues, boost::optional<std::vector<index_type>> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), trivialRowGrouping(!rowGroupIndices), rowGroupIndices(rowGroupIndices) { this->updateNonzeroEntryCount(); } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, ValueType>>&& columnsAndValues, std::vector<index_type>&& rowGroupIndices, bool nontrivialRowGrouping) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), nontrivialRowGrouping(nontrivialRowGrouping), rowGroupIndices(std::move(rowGroupIndices)) { + SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, ValueType>>&& columnsAndValues, boost::optional<std::vector<index_type>>&& rowGroupIndices, bool nontrivialRowGrouping) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), trivialRowGrouping(!rowGroupIndices), rowGroupIndices(std::move(rowGroupIndices)) { this->updateNonzeroEntryCount(); } @@ -341,7 +338,7 @@ namespace storm { columnsAndValues = other.columnsAndValues; rowIndications = other.rowIndications; rowGroupIndices = other.rowGroupIndices; - nontrivialRowGrouping = other.nontrivialRowGrouping; + trivialRowGrouping = other.trivialRowGrouping; } return *this; @@ -359,7 +356,7 @@ namespace storm { columnsAndValues = std::move(other.columnsAndValues); rowIndications = std::move(other.rowIndications); rowGroupIndices = std::move(other.rowGroupIndices); - nontrivialRowGrouping = other.nontrivialRowGrouping; + trivialRowGrouping = other.trivialRowGrouping; } return *this; @@ -381,7 +378,11 @@ namespace storm { if (!equalityResult) { return false; } - equalityResult &= this->getRowGroupIndices() == other.getRowGroupIndices(); + if (!this->hasTrivialRowGrouping() && !other.hasTrivialRowGrouping()) { + equalityResult &= this->getRowGroupIndices() == other.getRowGroupIndices(); + } else { + equalityResult &= this->hasTrivialRowGrouping() && other.hasTrivialRowGrouping(); + } if (!equalityResult) { return false; } @@ -433,8 +434,12 @@ namespace storm { template<typename T> uint_fast64_t SparseMatrix<T>::getRowGroupEntryCount(uint_fast64_t const group) const { uint_fast64_t result = 0; - for (uint_fast64_t row = this->getRowGroupIndices()[group]; row < this->getRowGroupIndices()[group + 1]; ++row) { - result += (this->rowIndications[row + 1] - this->rowIndications[row]); + if (!this->hasTrivialRowGrouping()) { + for (uint_fast64_t row = this->getRowGroupIndices()[group]; row < this->getRowGroupIndices()[group + 1]; ++row) { + result += (this->rowIndications[row + 1] - this->rowIndications[row]); + } + } else { + result += (this->rowIndications[group + 1] - this->rowIndications[group]); } return result; } @@ -473,7 +478,11 @@ namespace storm { template<typename ValueType> typename SparseMatrix<ValueType>::index_type SparseMatrix<ValueType>::getRowGroupCount() const { - return rowGroupIndices.size() - 1; + if (!this->hasTrivialRowGrouping()) { + return rowGroupIndices.get().size() - 1; + } else { + return rowCount; + } } template<typename ValueType> @@ -483,7 +492,15 @@ namespace storm { template<typename ValueType> std::vector<typename SparseMatrix<ValueType>::index_type> const& SparseMatrix<ValueType>::getRowGroupIndices() const { - return rowGroupIndices; + // If there is no current row grouping, we need to create it. + if (!this->rowGroupIndices) { + STORM_LOG_ASSERT(trivialRowGrouping, "Only trivial row-groupings can be constructed on-the-fly."); + this->rowGroupIndices = std::vector<index_type>(this->getRowCount() + 1); + for (uint_fast64_t group = 0; group < this->getRowCount(); ++group) { + this->rowGroupIndices.get()[group] = group; + } + } + return rowGroupIndices.get(); } template<typename ValueType> @@ -495,9 +512,15 @@ namespace storm { template<typename ValueType> void SparseMatrix<ValueType>::makeRowGroupsAbsorbing(storm::storage::BitVector const& rowGroupConstraint) { - for (auto rowGroup : rowGroupConstraint) { - for (index_type row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) { - makeRowDirac(row, rowGroup); + if (!this->hasTrivialRowGrouping()) { + for (auto rowGroup : rowGroupConstraint) { + for (index_type row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) { + makeRowDirac(row, rowGroup); + } + } + } else { + for (auto rowGroup : rowGroupConstraint) { + makeRowDirac(rowGroup, rowGroup); } } } @@ -550,9 +573,15 @@ namespace storm { std::vector<ValueType> SparseMatrix<ValueType>::getConstrainedRowGroupSumVector(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint) const { std::vector<ValueType> result; result.reserve(rowGroupConstraint.getNumberOfSetBits()); - for (auto rowGroup : rowGroupConstraint) { - for (index_type row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) { - result.push_back(getConstrainedRowSum(row, columnConstraint)); + if (!this->hasTrivialRowGrouping()) { + for (auto rowGroup : rowGroupConstraint) { + for (index_type row = this->getRowGroupIndices()[rowGroup]; row < this->getRowGroupIndices()[rowGroup + 1]; ++row) { + result.push_back(getConstrainedRowSum(row, columnConstraint)); + } + } + } else { + for (auto rowGroup : rowGroupConstraint) { + result.push_back(getConstrainedRowSum(rowGroup, columnConstraint)); } } return result; @@ -571,24 +600,29 @@ namespace storm { } auto res = getSubmatrix(rowConstraint, columnConstraint, fakeRowGroupIndices, insertDiagonalElements); - // Create a new row grouping that reflects the new sizes of the row groups. - std::vector<uint_fast64_t> newRowGroupIndices; - newRowGroupIndices.push_back(0); - auto selectedRowIt = rowConstraint.begin(); - - // For this, we need to count how many rows were preserved in every group. - for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { - uint_fast64_t newRowCount = 0; - while (*selectedRowIt < this->getRowGroupIndices()[group + 1]) { - ++selectedRowIt; - ++newRowCount; - } - if (newRowCount > 0) { - newRowGroupIndices.push_back(newRowGroupIndices.back() + newRowCount); + // Create a new row grouping that reflects the new sizes of the row groups if the current matrix has a + // non trivial row-grouping. + if (!this->hasTrivialRowGrouping()) { + std::vector<uint_fast64_t> newRowGroupIndices; + newRowGroupIndices.push_back(0); + auto selectedRowIt = rowConstraint.begin(); + + // For this, we need to count how many rows were preserved in every group. + for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { + uint_fast64_t newRowCount = 0; + while (*selectedRowIt < this->getRowGroupIndices()[group + 1]) { + ++selectedRowIt; + ++newRowCount; + } + if (newRowCount > 0) { + newRowGroupIndices.push_back(newRowGroupIndices.back() + newRowCount); + } } + + res.trivialRowGrouping = false; + res.rowGroupIndices = newRowGroupIndices; } - res.rowGroupIndices = newRowGroupIndices; return res; } } @@ -635,13 +669,13 @@ namespace storm { } // Create and initialize resulting matrix. - SparseMatrixBuilder<ValueType> matrixBuilder(subRows, submatrixColumnCount, subEntries, true, this->hasNontrivialRowGrouping()); + SparseMatrixBuilder<ValueType> matrixBuilder(subRows, submatrixColumnCount, subEntries, true, !this->hasTrivialRowGrouping()); // Copy over selected entries. rowGroupCount = 0; index_type rowCount = 0; for (auto index : rowGroupConstraint) { - if (this->hasNontrivialRowGrouping()) { + if (!this->hasTrivialRowGrouping()) { matrixBuilder.newRowGroup(rowCount); } for (index_type i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) { @@ -677,12 +711,12 @@ namespace storm { template<typename ValueType> SparseMatrix<ValueType> SparseMatrix<ValueType>::restrictRows(storm::storage::BitVector const& rowsToKeep) const { // For now, we use the expensive call to submatrix. - assert(rowsToKeep.size() == getRowCount()); - assert(rowsToKeep.getNumberOfSetBits() >= getRowGroupCount()); + STORM_LOG_ASSERT(rowsToKeep.size() == this->getRowCount(), "Dimensions mismatch."); + STORM_LOG_ASSERT(rowsToKeep.getNumberOfSetBits() >= this->getRowGroupCount(), "Invalid dimensions."); SparseMatrix<ValueType> res(getSubmatrix(false, rowsToKeep, storm::storage::BitVector(getColumnCount(), true), false)); - assert(res.getRowCount() == rowsToKeep.getNumberOfSetBits()); - assert(res.getColumnCount() == getColumnCount()); - assert(getRowGroupCount() == res.getRowGroupCount()); + STORM_LOG_ASSERT(res.getRowCount() == rowsToKeep.getNumberOfSetBits(), "Invalid dimensions"); + STORM_LOG_ASSERT(res.getColumnCount() == this->getColumnCount(), "Invalid dimensions"); + STORM_LOG_ASSERT(this->getRowGroupCount() == res.getRowGroupCount(), "Invalid dimensions"); return res; } @@ -693,7 +727,7 @@ namespace storm { index_type subEntries = 0; for (index_type rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) { // Determine which row we need to select from the current row group. - index_type rowToCopy = rowGroupIndices[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex]; + index_type rowToCopy = this->getRowGroupIndices()[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex]; // Iterate through that row and count the number of slots we have to reserve for copying. bool foundDiagonalElement = false; @@ -709,12 +743,12 @@ namespace storm { } // Now create the matrix to be returned with the appropriate size. - SparseMatrixBuilder<ValueType> matrixBuilder(rowGroupIndices.size() - 1, columnCount, subEntries); + SparseMatrixBuilder<ValueType> matrixBuilder(rowGroupIndices.get().size() - 1, columnCount, subEntries); // Copy over the selected lines from the source matrix. for (index_type rowGroupIndex = 0, rowGroupIndexEnd = rowGroupToRowIndexMapping.size(); rowGroupIndex < rowGroupIndexEnd; ++rowGroupIndex) { // Determine which row we need to select from the current row group. - index_type rowToCopy = rowGroupIndices[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex]; + index_type rowToCopy = this->getRowGroupIndices()[rowGroupIndex] + rowGroupToRowIndexMapping[rowGroupIndex]; // Iterate through that row and copy the entries. This also inserts a zero element on the diagonal if // there is no entry yet. @@ -781,12 +815,7 @@ namespace storm { } } - std::vector<index_type> rowGroupIndices(rowCount + 1); - for (index_type i = 0; i <= rowCount; ++i) { - rowGroupIndices[i] = i; - } - - storm::storage::SparseMatrix<ValueType> transposedMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), false); + storm::storage::SparseMatrix<ValueType> transposedMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues), boost::none); return transposedMatrix; } @@ -1056,30 +1085,46 @@ namespace storm { template<typename ValueType> typename SparseMatrix<ValueType>::const_rows SparseMatrix<ValueType>::getRow(index_type rowGroup, index_type offset) const { - assert(rowGroup < this->getRowGroupCount()); - assert(offset < this->getRowGroupEntryCount(rowGroup)); - return getRow(rowGroupIndices[rowGroup] + offset); + STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); + STORM_LOG_ASSERT(offset < this->getRowGroupEntryCount(rowGroup), "Row offset in row-group is out-of-bounds."); + if (!this->hasTrivialRowGrouping()) { + return getRow(this->getRowGroupIndices()[rowGroup] + offset); + } else { + return getRow(this->getRowGroupIndices()[rowGroup] + offset); + } } template<typename ValueType> typename SparseMatrix<ValueType>::rows SparseMatrix<ValueType>::getRow(index_type rowGroup, index_type offset) { - assert(rowGroup < this->getRowGroupCount()); - assert(offset < this->getRowGroupEntryCount(rowGroup)); - return getRow(rowGroupIndices[rowGroup] + offset); + STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); + STORM_LOG_ASSERT(offset < this->getRowGroupEntryCount(rowGroup), "Row offset in row-group is out-of-bounds."); + if (!this->hasTrivialRowGrouping()) { + return getRow(this->getRowGroupIndices()[rowGroup] + offset); + } else { + return getRow(rowGroup + offset); + } } template<typename ValueType> typename SparseMatrix<ValueType>::const_rows SparseMatrix<ValueType>::getRowGroup(index_type rowGroup) const { - assert(rowGroup < this->getRowGroupCount()); - return getRows(rowGroupIndices[rowGroup], rowGroupIndices[rowGroup + 1] - 1); + STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); + if (!this->hasTrivialRowGrouping()) { + return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1] - 1); + } else { + return getRows(rowGroup, rowGroup + 1); + } } template<typename ValueType> typename SparseMatrix<ValueType>::rows SparseMatrix<ValueType>::getRowGroup(index_type rowGroup) { - assert(rowGroup < this->getRowGroupCount()); - return getRows(rowGroupIndices[rowGroup], rowGroupIndices[rowGroup + 1] - 1); + STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); + if (!this->hasTrivialRowGrouping()) { + return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1] - 1); + } else { + return getRows(rowGroup, rowGroup + 1); + } } template<typename ValueType> @@ -1113,8 +1158,8 @@ namespace storm { } template<typename ValueType> - bool SparseMatrix<ValueType>::hasNontrivialRowGrouping() const { - return nontrivialRowGrouping; + bool SparseMatrix<ValueType>::hasTrivialRowGrouping() const { + return trivialRowGrouping; } template<typename ValueType> @@ -1143,6 +1188,9 @@ namespace storm { // Check for matching sizes. if (this->getRowCount() != matrix.getRowCount()) return false; if (this->getColumnCount() != matrix.getColumnCount()) return false; + if (this->hasTrivialRowGrouping() && !matrix.hasTrivialRowGrouping()) return false; + if (!this->hasTrivialRowGrouping() && matrix.hasTrivialRowGrouping()) return false; + if (!this->hasTrivialRowGrouping() && !matrix.hasTrivialRowGrouping() && this->getRowGroupIndices() != matrix.getRowGroupIndices()) return false; if (this->getRowGroupIndices() != matrix.getRowGroupIndices()) return false; // Check the subset property for all rows individually. @@ -1174,7 +1222,7 @@ namespace storm { // Iterate over all row groups. for (typename SparseMatrix<ValueType>::index_type group = 0; group < matrix.getRowGroupCount(); ++group) { out << "\t---- group " << group << "/" << (matrix.getRowGroupCount() - 1) << " ---- " << std::endl; - for (typename SparseMatrix<ValueType>::index_type i = matrix.getRowGroupIndices()[group]; i < matrix.getRowGroupIndices()[group + 1]; ++i) { + for (typename SparseMatrix<ValueType>::index_type i = matrix.hasTrivialRowGrouping() ? group : matrix.getRowGroupIndices()[group]; i < matrix.hasTrivialRowGrouping() ? group + 1 : matrix.getRowGroupIndices()[group + 1]; ++i) { typename SparseMatrix<ValueType>::index_type nextIndex = matrix.rowIndications[i]; // Print the actual row. @@ -1237,7 +1285,9 @@ namespace storm { boost::hash_combine(result, this->getEntryCount()); boost::hash_combine(result, boost::hash_range(columnsAndValues.begin(), columnsAndValues.end())); boost::hash_combine(result, boost::hash_range(rowIndications.begin(), rowIndications.end())); - boost::hash_combine(result, boost::hash_range(rowGroupIndices.begin(), rowGroupIndices.end())); + if (!this->hasTrivialRowGrouping()) { + boost::hash_combine(result, boost::hash_range(rowGroupIndices.get().begin(), rowGroupIndices.get().end())); + } return result; } diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index 1ecee566f..730e018f2 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -7,9 +7,11 @@ #include <vector> #include <iterator> +#include <boost/functional/hash.hpp> +#include <boost/optional.hpp> + #include "src/utility/OsDetection.h" #include "src/adapters/CarlAdapter.h" -#include <boost/functional/hash.hpp> // Forward declaration for adapter classes. namespace storm { @@ -259,7 +261,8 @@ namespace storm { // The number of row groups in the matrix. index_type initialRowGroupCount; - std::vector<index_type> rowGroupIndices; + // The vector that stores the row-group indices (if they are non-trivial). + boost::optional<std::vector<index_type>> rowGroupIndices; // The storage for the columns and values of all entries in the matrix. std::vector<MatrixEntry<index_type, value_type>> columnsAndValues; @@ -447,9 +450,8 @@ namespace storm { * @param rowIndications The row indications vector of the matrix to be constructed. * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. - * @param hasNontrivialRowGrouping If set to true, this indicates that the row grouping is non-trivial. */ - SparseMatrix(index_type columnCount, std::vector<index_type> const& rowIndications, std::vector<MatrixEntry<index_type, value_type>> const& columnsAndValues, std::vector<index_type> const& rowGroupIndices, bool hasNontrivialRowGrouping); + SparseMatrix(index_type columnCount, std::vector<index_type> const& rowIndications, std::vector<MatrixEntry<index_type, value_type>> const& columnsAndValues, boost::optional<std::vector<index_type>> const& rowGroupIndices); /*! * Constructs a sparse matrix by moving the given contents. @@ -458,9 +460,8 @@ namespace storm { * @param rowIndications The row indications vector of the matrix to be constructed. * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. - * @param hasNontrivialRowGrouping If set to true, this indicates that the row grouping is non-trivial. */ - SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, value_type>>&& columnsAndValues, std::vector<index_type>&& rowGroupIndices, bool hasNontrivialRowGrouping); + SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, value_type>>&& columnsAndValues, boost::optional<std::vector<index_type>>&& rowGroupIndices); /*! * Assigns the contents of the given matrix to the current one by deep-copying its contents. @@ -913,27 +914,28 @@ namespace storm { iterator end(); /*! - * Retrieves whether the matrix has a (possibly) non-trivial row grouping. + * Retrieves whether the matrix has a trivial row grouping. * - * @return True iff the matrix has a (possibly) non-trivial row grouping. + * @return True iff the matrix has a trivial row grouping. */ - bool hasNontrivialRowGrouping() const; + bool hasTrivialRowGrouping() const; /*! * Returns a copy of the matrix with the chosen internal data type */ template<typename NewValueType> SparseMatrix<NewValueType> toValueType() const { - std::vector<MatrixEntry<SparseMatrix::index_type, NewValueType>> new_columnsAndValues; - std::vector<SparseMatrix::index_type> new_rowIndications(rowIndications); - std::vector<SparseMatrix::index_type> new_rowGroupIndices(rowGroupIndices); + std::vector<MatrixEntry<SparseMatrix::index_type, NewValueType>> newColumnsAndValues; + std::vector<SparseMatrix::index_type> newRowIndications(rowIndications); + boost::optional<std::vector<SparseMatrix::index_type>> newRowGroupIndices(rowGroupIndices); - new_columnsAndValues.resize(columnsAndValues.size()); - for (size_t i = 0, size = columnsAndValues.size(); i < size; ++i) { - new_columnsAndValues.at(i) = MatrixEntry<SparseMatrix::index_type, NewValueType>(columnsAndValues.at(i).getColumn(), static_cast<NewValueType>(columnsAndValues.at(i).getValue())); - } + newColumnsAndValues.resize(columnsAndValues.size()); + std::transform(columnsAndValues.begin(), columnsAndValues.end(), newColumnsAndValues.begin(), + [] (MatrixEntry<SparseMatrix::index_type, ValueType> const& a) { + return MatrixEntry<SparseMatrix::index_type, NewValueType>(a.getColumn(), static_cast<NewValueType>(a.getValue())); + }); - return SparseMatrix<NewValueType>(columnCount, std::move(new_rowIndications), std::move(new_columnsAndValues), std::move(new_rowGroupIndices), nontrivialRowGrouping); + return SparseMatrix<NewValueType>(columnCount, std::move(newRowIndications), std::move(newColumnsAndValues), std::move(newRowGroupIndices)); } private: @@ -972,12 +974,12 @@ namespace storm { // entry is not included anymore. std::vector<index_type> rowIndications; - // A flag that indicates whether the matrix has a non-trivial row-grouping, i.e. (possibly) more than one - // row per row group. - bool nontrivialRowGrouping; + // A flag indicating whether the matrix has a trivial row grouping. Note that this may be true and yet + // there may be row group indices, because they were requested from the outside. + bool trivialRowGrouping; - // A vector indicating the row groups of the matrix. - std::vector<index_type> rowGroupIndices; + // A vector indicating the row groups of the matrix. This needs to be mutible in case we create it on-the-fly. + mutable boost::optional<std::vector<index_type>> rowGroupIndices; }; From 0b98412bb43bf998c2397e3e6add2f88ac340e35 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 7 Mar 2016 22:45:42 +0100 Subject: [PATCH 14/17] further work on making row-grouping optional Former-commit-id: bae568660f238424524b14694d39929165801aa5 --- src/storage/SparseMatrix.cpp | 14 ++++++++------ src/storage/dd/Add.cpp | 6 +++--- test/functional/storage/SparseMatrixTest.cpp | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 83bc7dc83..df78e4e2d 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -80,15 +80,17 @@ namespace storm { if (initialEntryCountSet) { columnsAndValues.reserve(initialEntryCount); } - if (initialRowGroupCountSet && hasCustomRowGrouping) { + if (hasCustomRowGrouping) { rowGroupIndices = std::vector<index_type>(); + } + if (initialRowGroupCountSet && hasCustomRowGrouping) { rowGroupIndices.get().reserve(initialRowGroupCount + 1); } rowIndications.push_back(0); } template<typename ValueType> - SparseMatrixBuilder<ValueType>::SparseMatrixBuilder(SparseMatrix<ValueType>&& matrix) : initialRowCountSet(false), initialRowCount(0), initialColumnCountSet(false), initialColumnCount(0), initialEntryCountSet(false), initialEntryCount(0), forceInitialDimensions(false), hasCustomRowGrouping(matrix.nontrivialRowGrouping), initialRowGroupCountSet(false), initialRowGroupCount(0), rowGroupIndices(), columnsAndValues(std::move(matrix.columnsAndValues)), rowIndications(std::move(matrix.rowIndications)), currentEntryCount(matrix.entryCount), lastRow(matrix.rowCount - 1), lastColumn(columnsAndValues.back().getColumn()), highestColumn(matrix.getColumnCount() - 1), currentRowGroup() { + SparseMatrixBuilder<ValueType>::SparseMatrixBuilder(SparseMatrix<ValueType>&& matrix) : initialRowCountSet(false), initialRowCount(0), initialColumnCountSet(false), initialColumnCount(0), initialEntryCountSet(false), initialEntryCount(0), forceInitialDimensions(false), hasCustomRowGrouping(!matrix.trivialRowGrouping), initialRowGroupCountSet(false), initialRowGroupCount(0), rowGroupIndices(), columnsAndValues(std::move(matrix.columnsAndValues)), rowIndications(std::move(matrix.rowIndications)), currentEntryCount(matrix.entryCount), lastRow(matrix.rowCount - 1), lastColumn(columnsAndValues.back().getColumn()), highestColumn(matrix.getColumnCount() - 1), currentRowGroup() { // If the matrix has a custom row grouping, we move it and remove the last element to make it 'open' again. if (hasCustomRowGrouping) { @@ -200,7 +202,7 @@ namespace storm { } } - return SparseMatrix<ValueType>(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), hasCustomRowGrouping); + return SparseMatrix<ValueType>(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); } template<typename ValueType> @@ -297,7 +299,7 @@ namespace storm { } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { + SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), trivialRowGrouping(other.trivialRowGrouping), rowGroupIndices(other.rowGroupIndices) { // Intentionally left empty. } @@ -309,7 +311,7 @@ namespace storm { } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { + SparseMatrix<ValueType>::SparseMatrix(SparseMatrix<ValueType>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), trivialRowGrouping(other.trivialRowGrouping), rowGroupIndices(std::move(other.rowGroupIndices)) { // Now update the source matrix other.rowCount = 0; other.columnCount = 0; @@ -322,7 +324,7 @@ namespace storm { } template<typename ValueType> - SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, ValueType>>&& columnsAndValues, boost::optional<std::vector<index_type>>&& rowGroupIndices, bool nontrivialRowGrouping) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), trivialRowGrouping(!rowGroupIndices), rowGroupIndices(std::move(rowGroupIndices)) { + SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, ValueType>>&& columnsAndValues, boost::optional<std::vector<index_type>>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), trivialRowGrouping(!rowGroupIndices), rowGroupIndices(std::move(rowGroupIndices)) { this->updateNonzeroEntryCount(); } diff --git a/src/storage/dd/Add.cpp b/src/storage/dd/Add.cpp index ecd98c2d9..40223da75 100644 --- a/src/storage/dd/Add.cpp +++ b/src/storage/dd/Add.cpp @@ -459,7 +459,7 @@ namespace storm { rowIndications[0] = 0; // Construct matrix and return result. - return storm::storage::SparseMatrix<ValueType>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(trivialRowGroupIndices), false); + return storm::storage::SparseMatrix<ValueType>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), boost::none); } template<DdType LibraryType, typename ValueType> @@ -583,7 +583,7 @@ namespace storm { } rowIndications[0] = 0; - return storm::storage::SparseMatrix<ValueType>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), true); + return storm::storage::SparseMatrix<ValueType>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); } template<DdType LibraryType, typename ValueType> @@ -708,7 +708,7 @@ namespace storm { } rowIndications[0] = 0; - return std::make_pair(storm::storage::SparseMatrix<ValueType>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), true), std::move(explicitVector)); + return std::make_pair(storm::storage::SparseMatrix<ValueType>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)), std::move(explicitVector)); } template<DdType LibraryType, typename ValueType> diff --git a/test/functional/storage/SparseMatrixTest.cpp b/test/functional/storage/SparseMatrixTest.cpp index 1fe0041f7..321fc7b91 100644 --- a/test/functional/storage/SparseMatrixTest.cpp +++ b/test/functional/storage/SparseMatrixTest.cpp @@ -155,8 +155,8 @@ TEST(SparseMatrix, CreationWithMovingContents) { columnsAndValues.emplace_back(1, 0.7); columnsAndValues.emplace_back(3, 0.2); - ASSERT_NO_THROW(storm::storage::SparseMatrix<double> matrix(4, {0, 2, 5, 5}, columnsAndValues, {0, 1, 2, 3}, false)); - storm::storage::SparseMatrix<double> matrix(4, {0, 2, 5, 5}, columnsAndValues, {0, 1, 2, 3}, false); + ASSERT_NO_THROW(storm::storage::SparseMatrix<double> matrix(4, {0, 2, 5, 5}, columnsAndValues, boost::optional<std::vector<uint_fast64_t>>({0, 1, 2, 3}))); + storm::storage::SparseMatrix<double> matrix(4, {0, 2, 5, 5}, columnsAndValues, boost::optional<std::vector<uint_fast64_t>>({0, 1, 2, 3})); ASSERT_EQ(3ul, matrix.getRowCount()); ASSERT_EQ(4ul, matrix.getColumnCount()); ASSERT_EQ(5ul, matrix.getEntryCount()); From a40d12f9153bd8e0c0917120b8966819db20791c Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 8 Mar 2016 08:48:41 +0100 Subject: [PATCH 15/17] made getRowGroup more consistent and fixed some introduced bugs Former-commit-id: 99b6c0e3a5b5d263c185fbd4bdb08d435c5b688b --- src/storage/SparseMatrix.cpp | 14 +++++++------- src/storage/SparseMatrix.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index df78e4e2d..99e21ee0d 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -1067,22 +1067,22 @@ namespace storm { template<typename ValueType> typename SparseMatrix<ValueType>::const_rows SparseMatrix<ValueType>::getRows(index_type startRow, index_type endRow) const { - return const_rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]); + return const_rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow] - this->rowIndications[startRow]); } template<typename ValueType> typename SparseMatrix<ValueType>::rows SparseMatrix<ValueType>::getRows(index_type startRow, index_type endRow) { - return rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]); + return rows(this->columnsAndValues.begin() + this->rowIndications[startRow], this->rowIndications[endRow] - this->rowIndications[startRow]); } template<typename ValueType> typename SparseMatrix<ValueType>::const_rows SparseMatrix<ValueType>::getRow(index_type row) const { - return getRows(row, row); + return getRows(row, row + 1); } template<typename ValueType> typename SparseMatrix<ValueType>::rows SparseMatrix<ValueType>::getRow(index_type row) { - return getRows(row, row); + return getRows(row, row + 1); } template<typename ValueType> @@ -1096,7 +1096,6 @@ namespace storm { } } - template<typename ValueType> typename SparseMatrix<ValueType>::rows SparseMatrix<ValueType>::getRow(index_type rowGroup, index_type offset) { STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); @@ -1104,6 +1103,7 @@ namespace storm { if (!this->hasTrivialRowGrouping()) { return getRow(this->getRowGroupIndices()[rowGroup] + offset); } else { + STORM_LOG_ASSERT(offset == 0, "Invalid offset."); return getRow(rowGroup + offset); } } @@ -1113,7 +1113,7 @@ namespace storm { typename SparseMatrix<ValueType>::const_rows SparseMatrix<ValueType>::getRowGroup(index_type rowGroup) const { STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); if (!this->hasTrivialRowGrouping()) { - return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1] - 1); + return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1]); } else { return getRows(rowGroup, rowGroup + 1); } @@ -1123,7 +1123,7 @@ namespace storm { typename SparseMatrix<ValueType>::rows SparseMatrix<ValueType>::getRowGroup(index_type rowGroup) { STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Row group is out-of-bounds."); if (!this->hasTrivialRowGrouping()) { - return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1] - 1); + return getRows(this->getRowGroupIndices()[rowGroup], this->getRowGroupIndices()[rowGroup + 1]); } else { return getRows(rowGroup, rowGroup + 1); } diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index 730e018f2..c5c7133c6 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -805,7 +805,7 @@ namespace storm { * Returns an object representing the consecutive rows given by the parameters. * * @param startRow The starting row. - * @param endRow The ending row (which is included in the result). + * @param endRow The ending row (which is *not* included in the result). * @return An object representing the consecutive rows given by the parameters. */ const_rows getRows(index_type startRow, index_type endRow) const; @@ -814,7 +814,7 @@ namespace storm { * Returns an object representing the consecutive rows given by the parameters. * * @param startRow The starting row. - * @param endRow The ending row (which is included in the result). + * @param endRow The ending row (which is *not* included in the result). * @return An object representing the consecutive rows given by the parameters. */ rows getRows(index_type startRow, index_type endRow); From f54c2fb8e7dc20df6024c853e11cd1ea3ee4a5c7 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 8 Mar 2016 10:00:17 +0100 Subject: [PATCH 16/17] tests passing again Former-commit-id: 8e3311f4c7afd7dfc456e22ad7998b929aee76ed --- src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp | 2 +- src/storage/SparseMatrix.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp b/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp index 5043c8296..a4724da46 100644 --- a/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp +++ b/src/modelchecker/csl/helper/SparseCtmcCslHelper.cpp @@ -354,7 +354,7 @@ namespace storm { STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); storm::storage::SparseMatrix<ValueType> uniformizedMatrix = computeUniformizedMatrix(rateMatrix, storm::storage::BitVector(numberOfStates, true), uniformizationRate, exitRateVector); - + // Compute the total state reward vector. std::vector<ValueType> totalRewardVector = rewardModel.getTotalRewardVector(rateMatrix, exitRateVector); diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 99e21ee0d..9795dbe2d 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -498,7 +498,7 @@ namespace storm { if (!this->rowGroupIndices) { STORM_LOG_ASSERT(trivialRowGrouping, "Only trivial row-groupings can be constructed on-the-fly."); this->rowGroupIndices = std::vector<index_type>(this->getRowCount() + 1); - for (uint_fast64_t group = 0; group < this->getRowCount(); ++group) { + for (uint_fast64_t group = 0; group <= this->getRowCount(); ++group) { this->rowGroupIndices.get()[group] = group; } } @@ -1224,7 +1224,10 @@ namespace storm { // Iterate over all row groups. for (typename SparseMatrix<ValueType>::index_type group = 0; group < matrix.getRowGroupCount(); ++group) { out << "\t---- group " << group << "/" << (matrix.getRowGroupCount() - 1) << " ---- " << std::endl; - for (typename SparseMatrix<ValueType>::index_type i = matrix.hasTrivialRowGrouping() ? group : matrix.getRowGroupIndices()[group]; i < matrix.hasTrivialRowGrouping() ? group + 1 : matrix.getRowGroupIndices()[group + 1]; ++i) { + typename SparseMatrix<ValueType>::index_type start = matrix.hasTrivialRowGrouping() ? group : matrix.getRowGroupIndices()[group]; + typename SparseMatrix<ValueType>::index_type end = matrix.hasTrivialRowGrouping() ? group + 1 : matrix.getRowGroupIndices()[group + 1]; + + for (typename SparseMatrix<ValueType>::index_type i = start; i < end; ++i) { typename SparseMatrix<ValueType>::index_type nextIndex = matrix.rowIndications[i]; // Print the actual row. From adb42b3ac08edb72a9ed86a5fa45cf8fdf8a1cb0 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Fri, 11 Mar 2016 16:37:59 +0100 Subject: [PATCH 17/17] fixed minor things related to merge Former-commit-id: f428c2808b1db2a0a5ac458118b546554ff6a22a --- .../utility/ModelInstantiatorTest.cpp | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/test/functional/utility/ModelInstantiatorTest.cpp b/test/functional/utility/ModelInstantiatorTest.cpp index af8f5b8d2..fe2ee4a7c 100644 --- a/test/functional/utility/ModelInstantiatorTest.cpp +++ b/test/functional/utility/ModelInstantiatorTest.cpp @@ -17,12 +17,12 @@ #include "src/models/sparse/Dtmc.h" #include "src/models/sparse/Mdp.h" -TEST(ModelInstantiatorTest, Brp_Prob) { +TEST(ModelInstantiatorTest, BrpProb) { carl::VariablePool::getInstance().clear(); - std::string const& programFile = STORM_CPP_TESTS_BASE_PATH "/functional/utility/brp16_2.pm"; - std::string const& formulaAsString = "P=? [F s=5 ]"; - std::string const& constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 + std::string programFile = STORM_CPP_TESTS_BASE_PATH "/functional/utility/brp16_2.pm"; + std::string formulaAsString = "P=? [F s=5 ]"; + std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 // Program and formula storm::prism::Program program = storm::parseProgram(programFile); @@ -33,7 +33,7 @@ TEST(ModelInstantiatorTest, Brp_Prob) { typename storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>::Options options = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>::Options(*formulas[0]); options.addConstantDefinitionsFromString(program, constantsAsString); options.preserveFormula(*formulas[0]); - std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>().translateProgram(program, options)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); + std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>(program, options).translate()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); storm::utility::ModelInstantiator<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::models::sparse::Dtmc<double>> modelInstantiator(*dtmc); EXPECT_FALSE(dtmc->hasRewardModel()); @@ -141,9 +141,9 @@ TEST(ModelInstantiatorTest, Brp_Prob) { TEST(ModelInstantiatorTest, Brp_Rew) { carl::VariablePool::getInstance().clear(); - std::string const& programFile = STORM_CPP_TESTS_BASE_PATH "/functional/utility/brp16_2.pm"; - std::string const& formulaAsString = "R=? [F ((s=5) | (s=0&srep=3)) ]"; - std::string const& constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 + std::string programFile = STORM_CPP_TESTS_BASE_PATH "/functional/utility/brp16_2.pm"; + std::string formulaAsString = "R=? [F ((s=5) | (s=0&srep=3)) ]"; + std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 // Program and formula storm::prism::Program program = storm::parseProgram(programFile); @@ -154,7 +154,7 @@ TEST(ModelInstantiatorTest, Brp_Rew) { typename storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>::Options options = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>::Options(*formulas[0]); options.addConstantDefinitionsFromString(program, constantsAsString); options.preserveFormula(*formulas[0]); - std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>().translateProgram(program, options)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); + std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>(program, options).translate()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>(); storm::utility::ModelInstantiator<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::models::sparse::Dtmc<double>> modelInstantiator(*dtmc); @@ -211,12 +211,12 @@ TEST(ModelInstantiatorTest, Brp_Rew) { } -TEST(ModelInstantiatorTest, consensus) { +TEST(ModelInstantiatorTest, Consensus) { carl::VariablePool::getInstance().clear(); - std::string const& programFile = STORM_CPP_TESTS_BASE_PATH "/functional/utility/coin2_2.pm"; - std::string const& formulaAsString = "Pmin=? [F \"finished\"&\"all_coins_equal_1\" ]"; - std::string const& constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 + std::string programFile = STORM_CPP_TESTS_BASE_PATH "/functional/utility/coin2_2.pm"; + std::string formulaAsString = "Pmin=? [F \"finished\"&\"all_coins_equal_1\" ]"; + std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 // Program and formula storm::prism::Program program = storm::parseProgram(programFile); @@ -227,7 +227,7 @@ TEST(ModelInstantiatorTest, consensus) { typename storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>::Options options = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>::Options(*formulas[0]); options.addConstantDefinitionsFromString(program, constantsAsString); options.preserveFormula(*formulas[0]); - std::shared_ptr<storm::models::sparse::Mdp<storm::RationalFunction>> mdp = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>().translateProgram(program, options)->as<storm::models::sparse::Mdp<storm::RationalFunction>>(); + std::shared_ptr<storm::models::sparse::Mdp<storm::RationalFunction>> mdp = storm::builder::ExplicitPrismModelBuilder<storm::RationalFunction>(program, options).translate()->as<storm::models::sparse::Mdp<storm::RationalFunction>>(); storm::utility::ModelInstantiator<storm::models::sparse::Mdp<storm::RationalFunction>, storm::models::sparse::Mdp<double>> modelInstantiator(*mdp); @@ -260,7 +260,6 @@ TEST(ModelInstantiatorTest, consensus) { std::unique_ptr<storm::modelchecker::CheckResult> chkResult = modelchecker.check(*formulas[0]); storm::modelchecker::ExplicitQuantitativeCheckResult<double>& quantitativeChkResult = chkResult->asExplicitQuantitativeCheckResult<double>(); EXPECT_NEAR(0.3526577219, quantitativeChkResult[*instantiated.getInitialStates().begin()], storm::settings::generalSettings().getPrecision()); - } #endif \ No newline at end of file