diff --git a/src/storm/builder/BuilderOptions.cpp b/src/storm/builder/BuilderOptions.cpp index d028bca8d..1d291af02 100644 --- a/src/storm/builder/BuilderOptions.cpp +++ b/src/storm/builder/BuilderOptions.cpp @@ -35,7 +35,7 @@ namespace storm { return boost::get(labelOrExpression); } - BuilderOptions::BuilderOptions(bool buildAllRewardModels, bool buildAllLabels) : buildAllRewardModels(buildAllRewardModels), buildAllLabels(buildAllLabels), buildChoiceLabels(false), explorationChecks(false) { + BuilderOptions::BuilderOptions(bool buildAllRewardModels, bool buildAllLabels) : buildAllRewardModels(buildAllRewardModels), buildAllLabels(buildAllLabels), buildChoiceLabels(false), buildChoiceOrigins(false), explorationChecks(false) { // Intentionally left empty. } @@ -139,6 +139,10 @@ namespace storm { return buildChoiceLabels; } + bool BuilderOptions::isBuildChoiceOriginsSet() const { + return buildChoiceOrigins; + } + bool BuilderOptions::isBuildAllRewardModelsSet() const { return buildAllRewardModels; } @@ -197,6 +201,11 @@ namespace storm { buildChoiceLabels = newValue; return *this; } + + BuilderOptions& BuilderOptions::setBuildChoiceOrigins(bool newValue) { + buildChoiceOrigins = newValue; + return *this; + } } } diff --git a/src/storm/builder/BuilderOptions.h b/src/storm/builder/BuilderOptions.h index 4be1bbc19..1485c1113 100644 --- a/src/storm/builder/BuilderOptions.h +++ b/src/storm/builder/BuilderOptions.h @@ -89,6 +89,7 @@ namespace storm { bool hasTerminalStates() const; void clearTerminalStates(); bool isBuildChoiceLabelsSet() const; + bool isBuildChoiceOriginsSet() const; bool isBuildAllRewardModelsSet() const; bool isBuildAllLabelsSet() const; bool isExplorationChecksSet() const; @@ -101,6 +102,7 @@ namespace storm { BuilderOptions& addTerminalExpression(storm::expressions::Expression const& expression, bool value); BuilderOptions& addTerminalLabel(std::string const& label, bool value); BuilderOptions& setBuildChoiceLabels(bool newValue); + BuilderOptions& setBuildChoiceOrigins(bool newValue); BuilderOptions& setExplorationChecks(bool newValue); private: @@ -125,6 +127,10 @@ namespace storm { /// A flag indicating whether or not to build choice labels. bool buildChoiceLabels; + + // A flag that indicates whether or not to generate the information from which parts of the model specification + // each choice originates. + bool buildChoiceOrigins; /// A flag that stores whether exploration checks are to be performed. bool explorationChecks; diff --git a/src/storm/builder/ChoiceInformationBuilder.cpp b/src/storm/builder/ChoiceInformationBuilder.cpp new file mode 100644 index 000000000..b51c68b15 --- /dev/null +++ b/src/storm/builder/ChoiceInformationBuilder.cpp @@ -0,0 +1,34 @@ +#include "storm/builder/ChoiceInformationBuilder.h" + +namespace storm { + namespace builder { + + void ChoiceInformationBuilder::addLabel(std::string const& label, uint_fast64_t choiceIndex) { + storm::storage::BitVector& labeledChoices = labels[label]; + labeledChoices.grow(choiceIndex + 1); + labeledChoices.set(choiceIndex, true); + } + + void ChoiceInformationBuilder::addOriginData(boost::any const& originData, uint_fast64_t choiceIndex) { + if(dataOfOrigins.size() != choiceIndex) { + dataOfOrigins.resize(choiceIndex); + } + dataOfOrigins.push_back(originData); + } + + storm::models::sparse::ChoiceLabeling ChoiceInformationBuilder::buildChoiceLabeling(uint_fast64_t totalNumberOfChoices) { + storm::models::sparse::ChoiceLabeling result(totalNumberOfChoices); + for (auto& label : labels) { + label.second.resize(totalNumberOfChoices, false); + result.addLabel(label.first, std::move(label.second)); + } + return result; + } + + std::vector ChoiceInformationBuilder::buildDataOfChoiceOrigins(uint_fast64_t totalNumberOfChoices) { + dataOfOrigins.resize(totalNumberOfChoices); + return std::move(dataOfOrigins); + } + + } +} \ No newline at end of file diff --git a/src/storm/builder/ChoiceInformationBuilder.h b/src/storm/builder/ChoiceInformationBuilder.h new file mode 100644 index 000000000..ca29e3fe1 --- /dev/null +++ b/src/storm/builder/ChoiceInformationBuilder.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "storm/models/sparse/ChoiceLabeling.h" +#include "storm/storage/BitVector.h" +#include "storm/storage/sparse/PrismChoiceOrigins.h" +#include "storm/storage/prism/Program.h" + + +namespace storm { + namespace builder { + + /*! + * This class collects information regarding the choices + */ + class ChoiceInformationBuilder { + public: + + ChoiceInformationBuilder() = default; + + void addLabel(std::string const& label, uint_fast64_t choiceIndex); + + void addOriginData(boost::any const& originData, uint_fast64_t choiceIndex); + + storm::models::sparse::ChoiceLabeling buildChoiceLabeling(uint_fast64_t totalNumberOfChoices); + + std::vector buildDataOfChoiceOrigins(uint_fast64_t totalNumberOfChoices); + + + private: + std::unordered_map labels; + std::vector dataOfOrigins; + }; + } +} + \ No newline at end of file diff --git a/src/storm/builder/ExplicitModelBuilder.cpp b/src/storm/builder/ExplicitModelBuilder.cpp index b207816bb..02d286dd4 100644 --- a/src/storm/builder/ExplicitModelBuilder.cpp +++ b/src/storm/builder/ExplicitModelBuilder.cpp @@ -14,6 +14,7 @@ #include "storm/settings/modules/IOSettings.h" #include "storm/builder/RewardModelBuilder.h" +#include "storm/builder/ChoiceInformationBuilder.h" #include "storm/generator/PrismNextStateGenerator.h" #include "storm/generator/JaniNextStateGenerator.h" @@ -66,36 +67,30 @@ namespace storm { } template - storm::storage::sparse::StateValuations const& ExplicitModelBuilder::getStateValuations() const { - STORM_LOG_THROW(static_cast(stateValuations), storm::exceptions::InvalidOperationException, "The state information was not properly build."); - return stateValuations.get(); - } - - template - std::shared_ptr> ExplicitModelBuilder::build() { + ExplicitModelBuilderResult ExplicitModelBuilder::build() { STORM_LOG_DEBUG("Exploration order is: " << options.explorationOrder); ModelComponents modelComponents = buildModelComponents(); - std::shared_ptr> result; + std::shared_ptr> resultModel; switch (generator->getModelType()) { case storm::generator::ModelType::DTMC: - result = std::shared_ptr>(new storm::models::sparse::Dtmc(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.rewardModels), std::move(modelComponents.choiceLabeling))); + resultModel = std::shared_ptr>(new storm::models::sparse::Dtmc(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.rewardModels) )); //, std::move(modelComponents.choiceLabeling))); break; case storm::generator::ModelType::CTMC: - result = std::shared_ptr>(new storm::models::sparse::Ctmc(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.rewardModels), std::move(modelComponents.choiceLabeling))); + resultModel = std::shared_ptr>(new storm::models::sparse::Ctmc(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.rewardModels) )); //, std::move(modelComponents.choiceLabeling))); break; case storm::generator::ModelType::MDP: - result = std::shared_ptr>(new storm::models::sparse::Mdp(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.rewardModels), std::move(modelComponents.choiceLabeling))); + resultModel = std::shared_ptr>(new storm::models::sparse::Mdp(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.rewardModels) )); //, std::move(modelComponents.choiceLabeling))); break; case storm::generator::ModelType::MA: - result = std::shared_ptr>(new storm::models::sparse::MarkovAutomaton(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), *std::move(modelComponents.markovianStates), std::move(modelComponents.rewardModels), std::move(modelComponents.choiceLabeling))); + resultModel = std::shared_ptr>(new storm::models::sparse::MarkovAutomaton(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), *std::move(modelComponents.markovianStates), std::move(modelComponents.rewardModels) )); //, std::move(modelComponents.choiceLabeling))); break; default: STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Error while creating model: cannot handle this model type."); break; } - return result; + return ExplicitModelBuilderResult(resultModel, modelComponents.stateValuations, modelComponents.choiceOrigins); } template @@ -122,11 +117,7 @@ namespace storm { } template - void ExplicitModelBuilder::buildMatrices(storm::storage::SparseMatrixBuilder& transitionMatrixBuilder, std::vector>& rewardModelBuilders, boost::optional>>& choiceLabels, boost::optional& markovianStates) { - // Create choice labels, if requested, - if (generator->getOptions().isBuildChoiceLabelsSet()) { - choiceLabels = std::vector>(); - } + void ExplicitModelBuilder::buildMatrices(storm::storage::SparseMatrixBuilder& transitionMatrixBuilder, std::vector>& rewardModelBuilders, ChoiceInformationBuilder& choiceInformationBuilder, boost::optional& markovianStates) { // Create markovian states bit vector, if required. if (generator->getModelType() == storm::generator::ModelType::MA) { @@ -177,11 +168,6 @@ namespace storm { this->stateStorage.deadlockStateIndices.push_back(currentIndex); } - if (generator->getOptions().isBuildChoiceLabelsSet()) { - // Insert empty choice labeling for added self-loop transitions. - choiceLabels.get().push_back(boost::container::flat_set()); - } - if (markovianStates) { markovianStates.get().grow(currentRowGroup + 1, false); markovianStates.get().set(currentRowGroup); @@ -225,9 +211,15 @@ namespace storm { // Now add all choices. for (auto const& choice : behavior) { - // Add command labels if requested. - if (generator->getOptions().isBuildChoiceLabelsSet()) { - choiceLabels.get().push_back(choice.getLabels()); + + // add the generated choice information + if (choice.hasLabels()) { + for (auto const& label : choice.getLabels()) { + choiceInformationBuilder.addLabel(label, currentRow); + } + } + if (choice.hasOriginData()) { + choiceInformationBuilder.addOriginData(choice.getOriginData(), currentRow); } // If we keep track of the Markovian choices, store whether the current one is Markovian. @@ -300,8 +292,8 @@ namespace storm { rewardModelBuilders.emplace_back(generator->getRewardModelInformation(i)); } - boost::optional markovianChoices; - buildMatrices(transitionMatrixBuilder, rewardModelBuilders, modelComponents.choiceLabeling, modelComponents.markovianStates); + ChoiceInformationBuilder choiceInformationBuilder; + buildMatrices(transitionMatrixBuilder, rewardModelBuilders, choiceInformationBuilder, modelComponents.markovianStates); modelComponents.transitionMatrix = transitionMatrixBuilder.build(); // Now finalize all reward models. @@ -312,13 +304,18 @@ namespace storm { // Build the state labeling. modelComponents.stateLabeling = buildStateLabeling(); - // Finally -- if requested -- build the state information that can be retrieved from the outside. + // Build the choice labeling + modelComponents.choiceLabeling = choiceInformationBuilder.buildChoiceLabeling(modelComponents.transitionMatrix.getRowCount()); + + // if requested, build the state valuations and choice origins if (options.buildStateValuations) { - stateValuations = storm::storage::sparse::StateValuations(stateStorage.getNumberOfStates()); + modelComponents.stateValuations = std::make_shared(stateStorage.getNumberOfStates()); for (auto const& bitVectorIndexPair : stateStorage.stateToId) { - stateValuations.get().valuations[bitVectorIndexPair.second] = generator->toValuation(bitVectorIndexPair.first); + modelComponents.stateValuations->valuations[bitVectorIndexPair.second] = generator->toValuation(bitVectorIndexPair.first); } } + auto originData = choiceInformationBuilder.buildDataOfChoiceOrigins(modelComponents.transitionMatrix.getRowCount()); + modelComponents.choiceOrigins = generator->generateChoiceOrigins(originData); return modelComponents; } diff --git a/src/storm/builder/ExplicitModelBuilder.h b/src/storm/builder/ExplicitModelBuilder.h index 25acb684c..d1cea0e7e 100644 --- a/src/storm/builder/ExplicitModelBuilder.h +++ b/src/storm/builder/ExplicitModelBuilder.h @@ -19,14 +19,16 @@ #include "storm/models/sparse/StateAnnotation.h" #include "storm/models/sparse/Model.h" #include "storm/models/sparse/StateLabeling.h" +#include "storm/models/sparse/ChoiceLabeling.h" #include "storm/storage/SparseMatrix.h" -#include "storm/storage/sparse/StateValuations.h" +#include "storm/storage/sparse/ChoiceOrigins.h" #include "storm/storage/sparse/StateStorage.h" #include "storm/settings/SettingsManager.h" #include "storm/utility/prism.h" #include "storm/builder/ExplorationOrder.h" +#include "storm/builder/ExplicitModelBuilderResult.h" #include "storm/generator/NextStateGenerator.h" #include "storm/generator/CompressedState.h" @@ -44,6 +46,7 @@ namespace storm { // Forward-declare classes. template struct RewardModelBuilder; + class ChoiceInformationBuilder; template, typename StateType = uint32_t> class ExplicitModelBuilder { @@ -62,10 +65,17 @@ namespace storm { std::unordered_map> rewardModels; // A vector that stores a labeling for each choice. - boost::optional>> choiceLabeling; + storm::models::sparse::ChoiceLabeling choiceLabeling; // A vector that stores which states are markovian. boost::optional markovianStates; + + // Stores for each state to which variable valuation it belongs (is nullptr if not generated) + std::shared_ptr stateValuations; + + // Stores for each choice from which parts of the input model description it originates (is nullptr if not generated) + std::shared_ptr choiceOrigins; + }; struct Options { @@ -78,8 +88,7 @@ namespace storm { ExplorationOrder explorationOrder; // A flag that indicates whether or not to store the state information after successfully building the - // model. If it is to be preserved, it can be retrieved via the appropriate methods after a successful - // call to translateProgram. + // model. If it is to be preserved, it is contained in the result obtained from build bool buildStateValuations; }; @@ -108,21 +117,10 @@ namespace storm { * 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. * - * @param program The program to translate. - * @param constantDefinitionString A string that contains a comma-separated definition of all undefined - * constants in the model. - * @param rewardModel The reward model that is to be built. - * @return The explicit model that was given by the probabilistic program. - */ - std::shared_ptr> build(); - - /*! - * If requested in the options, information about the variable valuations in the reachable states can be - * retrieved via this function. - * - * @return A structure that stores information about all reachable states. + * @return The explicit model that was given by the probabilistic program as well as additional + * information (if requested). */ - storm::storage::sparse::StateValuations const& getStateValuations() const; + ExplicitModelBuilderResult build(); private: /*! @@ -141,10 +139,10 @@ namespace storm { * * @param transitionMatrixBuilder The builder of the transition matrix. * @param rewardModelBuilders The builders for the selected reward models. - * @param choiceLabels is set to a vector containing the labels associated with each choice (is only set if choice labels are requested). + * @param choiceInformationBuilder The builder for the requested information of the choices * @param markovianChoices is set to a bit vector storing whether a choice is markovian (is only set if the model type requires this information). */ - void buildMatrices(storm::storage::SparseMatrixBuilder& transitionMatrixBuilder, std::vector>& rewardModelBuilders, boost::optional>>& choiceLabels , boost::optional& markovianChoices); + void buildMatrices(storm::storage::SparseMatrixBuilder& transitionMatrixBuilder, std::vector>& rewardModelBuilders, ChoiceInformationBuilder& choiceInformationBuilder, boost::optional& markovianChoices); /*! * Explores the state space of the given program and returns the components of the model as a result. @@ -169,10 +167,6 @@ namespace storm { /// Internal information about the states that were explored. storm::storage::sparse::StateStorage stateStorage; - /// This member holds information about reachable states that can be retrieved from the outside after a - /// successful build. - boost::optional stateValuations; - /// A set of states that still need to be explored. std::deque statesToExplore; diff --git a/src/storm/builder/ExplicitModelBuilderResult.cpp b/src/storm/builder/ExplicitModelBuilderResult.cpp new file mode 100644 index 000000000..b2d52522e --- /dev/null +++ b/src/storm/builder/ExplicitModelBuilderResult.cpp @@ -0,0 +1,73 @@ +#include "storm/builder/ExplicitModelBuilderResult.h" + +#include "storm/utility/macros.h" +#include "storm/exceptions/InvalidOperationException.h" + +#include "storm/adapters/CarlAdapter.h" +#include "storm/models/sparse/StandardRewardModel.h" + +namespace storm { + namespace builder { + + template + ExplicitModelBuilderResult::ExplicitModelBuilderResult(std::shared_ptr> model, std::shared_ptr stateValuations,std::shared_ptr choiceOrigins) : model(model), stateValuations(stateValuations), choiceOrigins(choiceOrigins) { + // Intentionally left empty + } + + template + std::shared_ptr>& ExplicitModelBuilderResult::getModel() { + STORM_LOG_THROW(model, storm::exceptions::InvalidOperationException, "Retrieving the model failed since it is not set."); + return model; + } + + template + std::shared_ptr> const& ExplicitModelBuilderResult::getModel() const { + STORM_LOG_THROW(model, storm::exceptions::InvalidOperationException, "Retrieving the model failed since it is not set."); + return model; + } + + template + bool ExplicitModelBuilderResult::hasStateValuations() { + return static_cast(stateValuations); + } + + template + std::shared_ptr& ExplicitModelBuilderResult::getStateValuations() { + STORM_LOG_THROW(stateValuations, storm::exceptions::InvalidOperationException, "Retrieving the state valuations failed since they are not set."); + return stateValuations; + } + + template + std::shared_ptr const& ExplicitModelBuilderResult::getStateValuations() const { + STORM_LOG_THROW(stateValuations, storm::exceptions::InvalidOperationException, "Retrieving the state valuations failed since they are not set."); + return stateValuations; + } + + template + bool ExplicitModelBuilderResult::hasChoiceOrigins() { + return static_cast(choiceOrigins); + } + + template + std::shared_ptr& ExplicitModelBuilderResult::getChoiceOrigins() { + STORM_LOG_THROW(choiceOrigins, storm::exceptions::InvalidOperationException, "Retrieving the choice origins failed since they are not set."); + return choiceOrigins; + } + + template + std::shared_ptr const& ExplicitModelBuilderResult::getChoiceOrigins() const { + STORM_LOG_THROW(choiceOrigins, storm::exceptions::InvalidOperationException, "Retrieving the choice origins failed since they are not set."); + return choiceOrigins; + } + + // Explicitly instantiate the class. + template class ExplicitModelBuilderResult>; + +#ifdef STORM_HAVE_CARL + template class ExplicitModelBuilderResult>; + template class ExplicitModelBuilderResult>; + template class ExplicitModelBuilderResult>; +#endif + + } +} \ No newline at end of file diff --git a/src/storm/builder/ExplicitModelBuilderResult.h b/src/storm/builder/ExplicitModelBuilderResult.h new file mode 100644 index 000000000..76a8e1161 --- /dev/null +++ b/src/storm/builder/ExplicitModelBuilderResult.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include "storm/models/sparse/Model.h" +#include "storm/storage/sparse/StateValuations.h" +#include "storm/storage/sparse/ChoiceOrigins.h" + +namespace storm { + namespace builder { + + template> + class ExplicitModelBuilderResult { + public: + ExplicitModelBuilderResult(std::shared_ptr> model, std::shared_ptr stateValuations,std::shared_ptr choiceOrigins); + + std::shared_ptr>& getModel(); + std::shared_ptr> const& getModel() const; + + bool hasStateValuations(); + std::shared_ptr& getStateValuations(); + std::shared_ptr const& getStateValuations() const; + + bool hasChoiceOrigins(); + std::shared_ptr& getChoiceOrigins(); + std::shared_ptr const& getChoiceOrigins() const; + + private: + std::shared_ptr> model; + std::shared_ptr stateValuations; + std::shared_ptr choiceOrigins; + }; + } +} \ No newline at end of file diff --git a/src/storm/generator/Choice.cpp b/src/storm/generator/Choice.cpp index cddf12f0f..020198fc6 100644 --- a/src/storm/generator/Choice.cpp +++ b/src/storm/generator/Choice.cpp @@ -1,11 +1,15 @@ #include "storm/generator/Choice.h" +#include + #include "storm/adapters/CarlAdapter.h" #include "storm/utility/constants.h" +#include "storm/builder/ChoiceInformationBuilder.h" #include "storm/utility/macros.h" #include "storm/exceptions/InvalidOperationException.h" +#include "storm/exceptions/NotImplementedException.h" namespace storm { namespace generator { @@ -33,17 +37,12 @@ namespace storm { rewardValue += *otherRewIt; } - // Join label sets. - if (this->labels) { - if (other.labels) { - LabelSet newLabelSet; - std::set_union(this->labels.get().begin(), this->labels.get().end(), other.labels.get().begin(), other.labels.get().end(), std::inserter(newLabelSet, newLabelSet.begin())); - this->labels = std::move(newLabelSet); - } - } else { - if (other.labels) { - this->labels = std::move(other.labels); - } + // Join label sets and origin data if given. + if (other.labels) { + this->addLabels(other.labels.get()); + } + if (other.originData) { + this->addOriginData(other.originData.get()); } } @@ -68,24 +67,60 @@ namespace storm { } template - void Choice::addLabel(uint_fast64_t label) { + void Choice::addLabel(std::string const& newLabel) { if (!labels) { - labels = LabelSet(); + labels = std::set(); } - labels->insert(label); + labels->insert(newLabel); } template - void Choice::addLabels(LabelSet const& labelSet) { - if (!labels) { - labels = LabelSet(); + void Choice::addLabels(std::set const& newLabels) { + if (labels) { + labels->insert(newLabels.begin(), newLabels.end()); + } else { + labels = newLabels; } - labels->insert(labelSet.begin(), labelSet.end()); } - + + template + bool Choice::hasLabels() const { + return labels.is_initialized(); + } + + template + std::set const& Choice::getLabels() const { + return labels.get(); + } + + template + void Choice::addOriginData(boost::any const& data) { + if (!this->originData || this->originData->empty()) { + this->originData = data; + } else { + if (!data.empty()) { + // Reaching this point means that the both the existing and the given data are non-empty + + auto existingDataAsIndexSet = boost::any_cast>(&this->originData.get()); + if (existingDataAsIndexSet != nullptr) { + auto givenDataAsIndexSet = boost::any_cast>(&data); + STORM_LOG_THROW(givenDataAsIndexSet != nullptr, storm::exceptions::InvalidOperationException, "Types of existing and given choice origin data do not match."); + existingDataAsIndexSet->insert(givenDataAsIndexSet->begin(), givenDataAsIndexSet->end()); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Type of choice origin data (aka " << data.type().name() << ") is not implemented."); + } + } + } + } + + template + bool Choice::hasOriginData() const { + return originData.is_initialized(); + } + template - boost::container::flat_set const& Choice::getLabels() const { - return *labels; + boost::any const& Choice::getOriginData() const { + return originData.get(); } template diff --git a/src/storm/generator/Choice.h b/src/storm/generator/Choice.h index d6abe51ed..04e0fd311 100644 --- a/src/storm/generator/Choice.h +++ b/src/storm/generator/Choice.h @@ -3,9 +3,10 @@ #include #include +#include #include -#include +#include #include "storm/storage/Distribution.h" @@ -16,8 +17,6 @@ namespace storm { template struct Choice { public: - typedef boost::container::flat_set LabelSet; - Choice(uint_fast64_t actionIndex = 0, bool markovian = false); Choice(Choice const& other) = default; @@ -71,21 +70,41 @@ namespace storm { * * @param label The label to associate with this choice. */ - void addLabel(uint_fast64_t label); + void addLabel(std::string const& label); /*! * Adds the given label set to the labels associated with this choice. * * @param labelSet The label set to associate with this choice. */ - void addLabels(LabelSet const& labelSet); + void addLabels(std::set const& labels); + + /*! + * Returns whether there are labels defined for this choice + */ + bool hasLabels() const; /*! * Retrieves the set of labels associated with this choice. * * @return The set of labels associated with this choice. */ - LabelSet const& getLabels() const; + std::set const& getLabels() const; + + /*! + * Adds the given data that specifies the origin of this choice w.r.t. the model specification + */ + void addOriginData(boost::any const& data); + + /*! + * Returns whether there is origin data defined for this choice + */ + bool hasOriginData() const; + + /*! + * Returns the origin data associated with this choice. + */ + boost::any const& getOriginData() const; /*! * Retrieves the index of the action of this choice. @@ -147,8 +166,11 @@ namespace storm { // The reward values associated with this choice. std::vector rewards; - // The labels that are associated with this choice. - boost::optional labels; + // The data that stores what part of the model specification induced this choice + boost::optional originData; + + // The labels of this choice + boost::optional> labels; }; template diff --git a/src/storm/generator/JaniNextStateGenerator.cpp b/src/storm/generator/JaniNextStateGenerator.cpp index 9ff5974c3..b7e8cd315 100644 --- a/src/storm/generator/JaniNextStateGenerator.cpp +++ b/src/storm/generator/JaniNextStateGenerator.cpp @@ -342,10 +342,6 @@ namespace storm { if (hasStateActionRewards && !this->isDiscreteTimeModel()) { totalExitRate += choice.getTotalMass(); } - - if (this->options.isBuildChoiceLabelsSet()) { - globalChoice.addLabels(choice.getLabels()); - } } std::vector stateActionRewards(rewardVariables.size(), storm::utility::zero()); diff --git a/src/storm/generator/NextStateGenerator.cpp b/src/storm/generator/NextStateGenerator.cpp index 7f931051f..2b4b64a88 100644 --- a/src/storm/generator/NextStateGenerator.cpp +++ b/src/storm/generator/NextStateGenerator.cpp @@ -147,6 +147,12 @@ namespace storm { storm::expressions::SimpleValuation NextStateGenerator::toValuation(CompressedState const& state) const { return unpackStateIntoValuation(state, variableInformation, *expressionManager); } + + template + std::shared_ptr NextStateGenerator::generateChoiceOrigins(std::vector& dataForChoiceOrigins) const { + STORM_LOG_ERROR_COND(!options.isBuildChoiceOriginsSet(), "Generating choice origins is not supported for the considered model format."); + return nullptr; + } template class NextStateGenerator; diff --git a/src/storm/generator/NextStateGenerator.h b/src/storm/generator/NextStateGenerator.h index ea442d278..f894d84a3 100644 --- a/src/storm/generator/NextStateGenerator.h +++ b/src/storm/generator/NextStateGenerator.h @@ -9,6 +9,7 @@ #include "storm/storage/expressions/Expression.h" #include "storm/storage/BitVectorHashMap.h" #include "storm/storage/expressions/ExpressionEvaluator.h" +#include "storm/storage/sparse/ChoiceOrigins.h" #include "storm/builder/BuilderOptions.h" #include "storm/builder/RewardModelInformation.h" @@ -62,6 +63,8 @@ namespace storm { NextStateGeneratorOptions const& getOptions() const; + virtual std::shared_ptr generateChoiceOrigins(std::vector& dataForChoiceOrigins) const; + protected: /*! * Creates the state labeling for the given states using the provided labels and expressions. diff --git a/src/storm/generator/PrismNextStateGenerator.cpp b/src/storm/generator/PrismNextStateGenerator.cpp index 62eeae361..015d64509 100644 --- a/src/storm/generator/PrismNextStateGenerator.cpp +++ b/src/storm/generator/PrismNextStateGenerator.cpp @@ -1,10 +1,12 @@ #include "storm/generator/PrismNextStateGenerator.h" #include +#include #include "storm/models/sparse/StateLabeling.h" #include "storm/storage/expressions/SimpleValuation.h" +#include "storm/storage/sparse/PrismChoiceOrigins.h" #include "storm/solver/SmtSolver.h" @@ -244,9 +246,13 @@ namespace storm { totalExitRate += choice.getTotalMass(); } - if (this->options.isBuildChoiceLabelsSet()) { + if (this->options.isBuildChoiceLabelsSet() && choice.hasLabels()) { globalChoice.addLabels(choice.getLabels()); } + + if (this->options.isBuildChoiceOriginsSet() && choice.hasOriginData()) { + globalChoice.addOriginData(choice.getOriginData()); + } } // Now construct the state-action reward for all selected reward models. @@ -391,9 +397,10 @@ namespace storm { result.push_back(Choice(command.getActionIndex(), command.isMarkovian())); Choice& choice = result.back(); - // Remember the command labels only if we were asked to. - if (this->options.isBuildChoiceLabelsSet()) { - choice.addLabel(command.getGlobalIndex()); + // Remember the choice origin only if we were asked to. + if (this->options.isBuildChoiceOriginsSet()) { + CommandSet commandIndex { command.getGlobalIndex() }; + choice.addOriginData(boost::any(std::move(commandIndex))); } // Iterate over all updates of the current command. @@ -503,12 +510,16 @@ namespace storm { // Now create the actual distribution. Choice& choice = result.back(); - // Remember the command labels only if we were asked to. + // Remember the choice label and origins only if we were asked to. if (this->options.isBuildChoiceLabelsSet()) { - // Add the labels of all commands to this choice. + choice.addLabel(program.getActionName(actionIndex)); + } + if (this->options.isBuildChoiceOriginsSet()) { + CommandSet commandIndices; for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) { - choice.addLabel(iteratorList[i]->get().getGlobalIndex()); + commandIndices.insert(iteratorList[i]->get().getGlobalIndex()); } + choice.addOriginData(boost::any(std::move(commandIndices))); } // Add the probabilities/rates to the newly created choice. @@ -594,6 +605,41 @@ namespace storm { storm::prism::RewardModel const& rewardModel = rewardModels[index].get(); return storm::builder::RewardModelInformation(rewardModel.getName(), rewardModel.hasStateRewards(), rewardModel.hasStateActionRewards(), rewardModel.hasTransitionRewards()); } + + template + std::shared_ptr PrismNextStateGenerator::generateChoiceOrigins(std::vector& dataForChoiceOrigins) const { + + if (!this->getOptions().isBuildChoiceOriginsSet()) { + return nullptr; + } + + std::vector identifiers; + identifiers.reserve(dataForChoiceOrigins.size()); + + std::map commandSetToIdentifierMap; + uint_fast64_t currentIdentifier = 0; + for (boost::any& originData : dataForChoiceOrigins) { + STORM_LOG_ASSERT(originData.empty() || boost::any_cast(&originData) != nullptr, "Origin data has unexpected type: " << originData.type().name() << "."); + + CommandSet currentCommandSet = originData.empty() ? CommandSet() : boost::any_cast(std::move(originData)); + auto insertionRes = commandSetToIdentifierMap.insert(std::make_pair(std::move(currentCommandSet), currentIdentifier)); + identifiers.push_back(insertionRes.first->second); + if (insertionRes.second) { + ++currentIdentifier; + } + } + + std::vector identifierToCommandSetMapping(currentIdentifier); + std::vector identifierToInfoMapping(currentIdentifier); + for (auto const& setIdPair : commandSetToIdentifierMap) { + identifierToCommandSetMapping[setIdPair.second] = setIdPair.first; + // Todo: Make these identifiers unique + identifierToInfoMapping[setIdPair.second] = "Choice made from " + std::to_string(setIdPair.first.size()) + " commands"; + } + + return std::make_shared(std::make_shared(program), std::move(identifiers), std::move(identifierToInfoMapping), std::move(identifierToCommandSetMapping)); + } + template class PrismNextStateGenerator; diff --git a/src/storm/generator/PrismNextStateGenerator.h b/src/storm/generator/PrismNextStateGenerator.h index ee12130e1..0097e7055 100644 --- a/src/storm/generator/PrismNextStateGenerator.h +++ b/src/storm/generator/PrismNextStateGenerator.h @@ -1,6 +1,8 @@ #ifndef STORM_GENERATOR_PRISMNEXTSTATEGENERATOR_H_ #define STORM_GENERATOR_PRISMNEXTSTATEGENERATOR_H_ +#include + #include "storm/generator/NextStateGenerator.h" #include "storm/storage/prism/Program.h" @@ -12,9 +14,10 @@ namespace storm { class PrismNextStateGenerator : public NextStateGenerator { public: typedef typename NextStateGenerator::StateToIdCallback StateToIdCallback; + typedef boost::container::flat_set CommandSet; PrismNextStateGenerator(storm::prism::Program const& program, NextStateGeneratorOptions const& options = NextStateGeneratorOptions()); - + virtual ModelType getModelType() const override; virtual bool isDeterministicModel() const override; virtual bool isDiscreteTimeModel() const override; @@ -27,6 +30,8 @@ namespace storm { virtual storm::models::sparse::StateLabeling label(storm::storage::BitVectorHashMap const& states, std::vector const& initialStateIndices = {}, std::vector const& deadlockStateIndices = {}) override; + virtual std::shared_ptr generateChoiceOrigins(std::vector& dataForChoiceOrigins) const override; + private: void checkValid() const; diff --git a/src/storm/models/sparse/ChoiceLabeling.cpp b/src/storm/models/sparse/ChoiceLabeling.cpp index 96c6aeec0..fe1117027 100644 --- a/src/storm/models/sparse/ChoiceLabeling.cpp +++ b/src/storm/models/sparse/ChoiceLabeling.cpp @@ -54,7 +54,7 @@ namespace storm { void ChoiceLabeling::addLabelToChoice(std::string const& label,uint64_t choice) { - this->addLabelToChoice(label, choice); + return ItemLabeling::addLabelToItem(label, choice); } bool ChoiceLabeling::getChoiceHasLabel(std::string const& label, uint64_t choice) const { diff --git a/src/storm/storage/sparse/ChoiceOrigins.cpp b/src/storm/storage/sparse/ChoiceOrigins.cpp new file mode 100644 index 000000000..d9a045482 --- /dev/null +++ b/src/storm/storage/sparse/ChoiceOrigins.cpp @@ -0,0 +1,69 @@ +#include "storm/storage/sparse/ChoiceOrigins.h" + +#include "storm/storage/sparse/PrismChoiceOrigins.h" +#include "storm/storage/sparse/JaniChoiceOrigins.h" +#include "storm/utility/vector.h" + + +namespace storm { + namespace storage { + namespace sparse { + + ChoiceOrigins::ChoiceOrigins(std::vector const& indexToIdentifierMapping, std::vector const& identifierToInfoMapping) : indexToIdentifier(indexToIdentifierMapping), identifierToInfo(identifierToInfoMapping) { + // Intentionally left empty + } + + ChoiceOrigins::ChoiceOrigins(std::vector&& indexToIdentifierMapping, std::vector&& identifierToInfoMapping) : indexToIdentifier(std::move(indexToIdentifierMapping)), identifierToInfo(std::move(identifierToInfoMapping)) { + // Intentionally left empty + } + + bool ChoiceOrigins::isPrismChoiceOrigins() const { + return false; + } + + bool ChoiceOrigins::isJaniChoiceOrigins() const { + return false; + } + + PrismChoiceOrigins& ChoiceOrigins::asPrismChoiceOrigins() { + return dynamic_cast(*this); + } + + PrismChoiceOrigins const& ChoiceOrigins::asPrismChoiceOrigins() const { + return dynamic_cast(*this); + } + + JaniChoiceOrigins& ChoiceOrigins::asJaniChoiceOrigins() { + return dynamic_cast(*this); + } + + JaniChoiceOrigins const& ChoiceOrigins::asJaniChoiceOrigins() const { + return dynamic_cast(*this); + } + + uint_fast64_t ChoiceOrigins::getIdentifier(uint_fast64_t choiceIndex) const { + return indexToIdentifier[choiceIndex]; + } + + std::string const& ChoiceOrigins::getIdentifierInfo(uint_fast64_t identifier) const { + return identifierToInfo[identifier]; + } + + std::string const& ChoiceOrigins::getChoiceInfo(uint_fast64_t choiceIndex) const { + return getIdentifierInfo(getIdentifier(choiceIndex)); + } + + std::shared_ptr ChoiceOrigins::selectChoices(storm::storage::BitVector const& selectedChoices) const { + std::vector indexToIdentifierMapping(selectedChoices.getNumberOfSetBits()); + storm::utility::vector::selectVectorValues(indexToIdentifierMapping, selectedChoices, indexToIdentifier); + return cloneWithNewIndexToIdentifierMapping(std::move(indexToIdentifierMapping)); + } + + std::shared_ptr ChoiceOrigins::selectChoices(std::vector const& selectedChoices) const { + std::vector indexToIdentifierMapping(selectedChoices.size()); + storm::utility::vector::selectVectorValues(indexToIdentifierMapping, selectedChoices, indexToIdentifier); + return cloneWithNewIndexToIdentifierMapping(std::move(indexToIdentifierMapping)); + } + } + } +} \ No newline at end of file diff --git a/src/storm/storage/sparse/ChoiceOrigins.h b/src/storm/storage/sparse/ChoiceOrigins.h new file mode 100644 index 000000000..e869658ea --- /dev/null +++ b/src/storm/storage/sparse/ChoiceOrigins.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include "storm/storage/BitVector.h" + +namespace storm { + namespace storage { + namespace sparse { + + class PrismChoiceOrigins; + class JaniChoiceOrigins; + + /*! + * This class represents the origin of the choices of a model in terms of the input model specification + * (e.g., the Prism commands that induced the choice). + */ + class ChoiceOrigins { + public: + + virtual ~ChoiceOrigins() = default; + + virtual bool isPrismChoiceOrigins() const; + virtual bool isJaniChoiceOrigins() const; + + PrismChoiceOrigins& asPrismChoiceOrigins(); + PrismChoiceOrigins const& asPrismChoiceOrigins() const; + + JaniChoiceOrigins& asJaniChoiceOrigins(); + JaniChoiceOrigins const& asJaniChoiceOrigins() const; + + /* + * Returns a unique identifier of the origin of the given choice which can be used to e.g. check whether two choices have the same origin + */ + uint_fast64_t getIdentifier(uint_fast64_t choiceIndex) const; + + /* + * Returns the information for the given choice origin identifier as a (human readable) string + */ + std::string const& getIdentifierInfo(uint_fast64_t identifier) const; + + /* + * Returns the choice origin information as a (human readable) string. + */ + std::string const& getChoiceInfo(uint_fast64_t choiceIndex) const; + + + std::shared_ptr selectChoices(storm::storage::BitVector const& selectedChoices) const; + std::shared_ptr selectChoices(std::vector const& selectedChoiceIndices) const; + + protected: + ChoiceOrigins(std::vector const& indexToIdentifierMapping, std::vector const& identifierToInfoMapping); + ChoiceOrigins(std::vector&& indexToIdentifierMapping, std::vector&& identifierToInfoMapping); + + /* + * Returns a copy of this object where the mapping of choice indices to origin identifiers is replaced by the given one. + */ + virtual std::shared_ptr cloneWithNewIndexToIdentifierMapping(std::vector&& indexToIdentifierMapping) const = 0; + + std::vector indexToIdentifier; + std::vector identifierToInfo; + }; + } + } +} \ No newline at end of file diff --git a/src/storm/storage/sparse/JaniChoiceOrigins.cpp b/src/storm/storage/sparse/JaniChoiceOrigins.cpp new file mode 100644 index 000000000..3548469f8 --- /dev/null +++ b/src/storm/storage/sparse/JaniChoiceOrigins.cpp @@ -0,0 +1,23 @@ +#include "storm/storage/sparse/JaniChoiceOrigins.h" + + +namespace storm { + namespace storage { + namespace sparse { + + JaniChoiceOrigins::JaniChoiceOrigins(std::vector const& indexToIdentifierMapping, std::vector const& identifierToInfoMapping) : ChoiceOrigins(indexToIdentifierMapping, identifierToInfoMapping) { + // Intentionally left empty + } + + bool JaniChoiceOrigins::isJaniChoiceOrigins() const { + return true; + } + + std::shared_ptr JaniChoiceOrigins::cloneWithNewIndexToIdentifierMapping(std::vector&& indexToIdentifierMapping) const { + std::vector identifierToInfoMapping = this->identifierToInfo; + return std::make_shared(std::move(indexToIdentifierMapping), std::move(identifierToInfoMapping)); + } + + } + } +} \ No newline at end of file diff --git a/src/storm/storage/sparse/JaniChoiceOrigins.h b/src/storm/storage/sparse/JaniChoiceOrigins.h new file mode 100644 index 000000000..58518c561 --- /dev/null +++ b/src/storm/storage/sparse/JaniChoiceOrigins.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "storm/storage/sparse/ChoiceOrigins.h" + + +namespace storm { + namespace storage { + namespace sparse { + + + /*! + * This class represents for each choice the origin in the jani specification + */ + class JaniChoiceOrigins : public ChoiceOrigins { + public: + + /*! + * Creates a new representation of the choice indices to their origin in the Jani specification + * // TODO complete this + */ + JaniChoiceOrigins(std::vector const& indexToIdentifierMapping, std::vector const& identifierToInfoMapping); + + virtual ~JaniChoiceOrigins() = default; + + virtual bool isJaniChoiceOrigins() const override ; + + /* + * Returns a copy of this object where the mapping of choice indices to origin identifiers is replaced by the given one. + */ + virtual std::shared_ptr cloneWithNewIndexToIdentifierMapping(std::vector&& indexToIdentifierMapping) const override; + + private: + + }; + } + } +} \ No newline at end of file diff --git a/src/storm/storage/sparse/PrismChoiceOrigins.cpp b/src/storm/storage/sparse/PrismChoiceOrigins.cpp new file mode 100644 index 000000000..ec417bdff --- /dev/null +++ b/src/storm/storage/sparse/PrismChoiceOrigins.cpp @@ -0,0 +1,37 @@ +#include "storm/storage/sparse/PrismChoiceOrigins.h" + + +namespace storm { + namespace storage { + namespace sparse { + + PrismChoiceOrigins::PrismChoiceOrigins(std::shared_ptr const& prismProgram, std::vector const& indexToIdentifierMapping, std::vector const& identifierToInfoMapping, std::vector const& identifierToCommandSetMapping) : ChoiceOrigins(indexToIdentifierMapping, identifierToInfoMapping), program(prismProgram), identifierToCommandSet(identifierToCommandSetMapping) { + // Intentionally left empty + } + + PrismChoiceOrigins::PrismChoiceOrigins(std::shared_ptr const& prismProgram, std::vector&& indexToIdentifierMapping, std::vector&& identifierToInfoMapping, std::vector&& identifierToCommandSetMapping) : ChoiceOrigins(std::move(indexToIdentifierMapping), std::move(identifierToInfoMapping)), program(prismProgram), identifierToCommandSet(std::move(identifierToCommandSetMapping)) { + // Intentionally left empty + } + + bool PrismChoiceOrigins::isPrismChoiceOrigins() const { + return true; + } + + storm::prism::Program const& PrismChoiceOrigins::getProgram() const { + return *program; + } + + PrismChoiceOrigins::CommandSet const& PrismChoiceOrigins::getCommandSet(uint_fast64_t choiceIndex) const { + return identifierToCommandSet[this->getIdentifier(choiceIndex)]; + } + + std::shared_ptr PrismChoiceOrigins::cloneWithNewIndexToIdentifierMapping(std::vector&& indexToIdentifierMapping) const { + std::vector identifierToCommandSetMapping = this->identifierToCommandSet; + std::vector identifierToInfoMapping = this->identifierToInfo; + return std::make_shared(this->program, std::move(indexToIdentifierMapping), std::move(identifierToInfoMapping), std::move(identifierToCommandSetMapping)); + } + + + } + } +} \ No newline at end of file diff --git a/src/storm/storage/sparse/PrismChoiceOrigins.h b/src/storm/storage/sparse/PrismChoiceOrigins.h new file mode 100644 index 000000000..0d65728a2 --- /dev/null +++ b/src/storm/storage/sparse/PrismChoiceOrigins.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +#include "storm/storage/sparse/ChoiceOrigins.h" +#include "storm/storage/prism/Program.h" + + +namespace storm { + namespace storage { + namespace sparse { + + + /*! + * This class represents for each choice the set of prism commands that induced the choice + */ + class PrismChoiceOrigins : public ChoiceOrigins { + public: + + typedef boost::container::flat_set CommandSet; + + /*! + * Creates a new representation of the choice indices to their origin in the prism program + * @param prismProgram The associated prism program + * @param indexToIdentifierMapping maps a choice index to the internally used identifier of the choice origin + * @param identifierToInfoMapping maps an origin identifier to a string representation of the origin + * @param identifierToCommandSetMapping maps an origin identifier to the set of global indices of the corresponding prism commands + */ + PrismChoiceOrigins(std::shared_ptr const& prismProgram, std::vector const& indexToIdentifierMapping, std::vector const& identifierToInfoMapping, std::vector const& identifierToCommandSetMapping); + PrismChoiceOrigins(std::shared_ptr const& prismProgram, std::vector&& indexToIdentifierMapping, std::vector&& identifierToInfoMapping, std::vector&& identifierToCommandSetMapping); + + virtual ~PrismChoiceOrigins() = default; + + virtual bool isPrismChoiceOrigins() const override ; + + /* + * Returns the prism program associated with this + */ + storm::prism::Program const& getProgram() const; + + /* + * Returns the set of prism commands that induced the choice with the given index. + * The command set is represented by a set of global command indices + */ + CommandSet const& getCommandSet(uint_fast64_t choiceIndex) const; + + /* + * Returns a copy of this object where the mapping of choice indices to origin identifiers is replaced by the given one. + */ + virtual std::shared_ptr cloneWithNewIndexToIdentifierMapping(std::vector&& indexToIdentifierMapping) const override; + + private: + + std::shared_ptr program; + std::vector identifierToCommandSet; + + }; + } + } +} \ No newline at end of file diff --git a/src/storm/utility/storm.h b/src/storm/utility/storm.h index 368a09618..c45c013b4 100644 --- a/src/storm/utility/storm.h +++ b/src/storm/utility/storm.h @@ -168,7 +168,7 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Cannot build sparse model from this symbolic model description."); } storm::builder::ExplicitModelBuilder builder(generator); - return builder.build(); + return builder.build().getModel(); } }