From ebb47aaa132c97ea4bde0e71caf3e2e9cae03966 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 18 May 2016 17:53:43 +0200 Subject: [PATCH 1/4] working on making model composition in PRISM work again Former-commit-id: 080a6d80cd6676faf1d213abbd2b3cd3f1442626 --- src/builder/DdPrismModelBuilder.cpp | 498 +++++++++++------- src/builder/DdPrismModelBuilder.h | 12 + .../prism/InterleavingParallelComposition.cpp | 2 +- .../prism/InterleavingParallelComposition.h | 4 - src/storage/prism/Program.cpp | 27 +- src/storage/prism/Program.h | 7 + .../prism/RestrictedParallelComposition.cpp | 2 +- .../prism/RestrictedParallelComposition.h | 2 - .../SynchronizingParallelComposition.cpp | 2 +- .../prism/SynchronizingParallelComposition.h | 4 - 10 files changed, 343 insertions(+), 217 deletions(-) diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index 2bb3328cd..d34ed80f3 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -1,5 +1,7 @@ #include "src/builder/DdPrismModelBuilder.h" +#include <boost/algorithm/string/join.hpp> + #include "src/models/symbolic/Dtmc.h" #include "src/models/symbolic/Ctmc.h" #include "src/models/symbolic/Mdp.h" @@ -9,12 +11,13 @@ #include "src/settings/SettingsManager.h" #include "src/exceptions/InvalidStateException.h" - +#include "src/exceptions/NotSupportedException.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/utility/prism.h" #include "src/utility/math.h" #include "src/storage/prism/Program.h" +#include "src/storage/prism/Compositions.h" #include "src/storage/dd/Add.h" #include "src/storage/dd/cudd/CuddAddIterator.h" @@ -27,107 +30,148 @@ namespace storm { template <storm::dd::DdType Type, typename ValueType> class DdPrismModelBuilder<Type, ValueType>::GenerationInformation { - public: - GenerationInformation(storm::prism::Program const& program) : program(program), manager(std::make_shared<storm::dd::DdManager<Type>>()), rowMetaVariables(), variableToRowMetaVariableMap(), rowExpressionAdapter(nullptr), columnMetaVariables(), variableToColumnMetaVariableMap(), columnExpressionAdapter(nullptr), rowColumnMetaVariablePairs(), nondeterminismMetaVariables(), variableToIdentityMap(), allGlobalVariables(), moduleToIdentityMap() { - // Initializes variables and identity DDs. - createMetaVariablesAndIdentities(); - - rowExpressionAdapter = std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>>(new storm::adapters::AddExpressionAdapter<Type>(manager, variableToRowMetaVariableMap)); - columnExpressionAdapter = std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>>(new storm::adapters::AddExpressionAdapter<Type>(manager, variableToColumnMetaVariableMap)); - } - - // The program that is currently translated. - storm::prism::Program const& program; - - // The manager used to build the decision diagrams. - std::shared_ptr<storm::dd::DdManager<Type>> manager; - - // The meta variables for the row encoding. - std::set<storm::expressions::Variable> rowMetaVariables; - std::map<storm::expressions::Variable, storm::expressions::Variable> variableToRowMetaVariableMap; - std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>> rowExpressionAdapter; - - // The meta variables for the column encoding. - std::set<storm::expressions::Variable> columnMetaVariables; - std::map<storm::expressions::Variable, storm::expressions::Variable> variableToColumnMetaVariableMap; - std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>> columnExpressionAdapter; - - // All pairs of row/column meta variables. - std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> rowColumnMetaVariablePairs; - - // The meta variables used to encode the nondeterminism. - std::vector<storm::expressions::Variable> nondeterminismMetaVariables; - - // The meta variables used to encode the synchronization. - std::vector<storm::expressions::Variable> synchronizationMetaVariables; + public: + GenerationInformation(storm::prism::Program const& program) : program(program), manager(std::make_shared<storm::dd::DdManager<Type>>()), rowMetaVariables(), variableToRowMetaVariableMap(), rowExpressionAdapter(nullptr), columnMetaVariables(), variableToColumnMetaVariableMap(), columnExpressionAdapter(nullptr), rowColumnMetaVariablePairs(), nondeterminismMetaVariables(), variableToIdentityMap(), allGlobalVariables(), moduleToIdentityMap() { + // Initializes variables and identity DDs. + createMetaVariablesAndIdentities(); - // A set of all variables used for encoding the nondeterminism (i.e. nondetermism + synchronization - // variables). This is handy to abstract from this variable set. - std::set<storm::expressions::Variable> allNondeterminismVariables; + rowExpressionAdapter = std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>>(new storm::adapters::AddExpressionAdapter<Type>(manager, variableToRowMetaVariableMap)); + columnExpressionAdapter = std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>>(new storm::adapters::AddExpressionAdapter<Type>(manager, variableToColumnMetaVariableMap)); + } + + // The program that is currently translated. + storm::prism::Program const& program; + + // The manager used to build the decision diagrams. + std::shared_ptr<storm::dd::DdManager<Type>> manager; + + // The meta variables for the row encoding. + std::set<storm::expressions::Variable> rowMetaVariables; + std::map<storm::expressions::Variable, storm::expressions::Variable> variableToRowMetaVariableMap; + std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>> rowExpressionAdapter; + + // The meta variables for the column encoding. + std::set<storm::expressions::Variable> columnMetaVariables; + std::map<storm::expressions::Variable, storm::expressions::Variable> variableToColumnMetaVariableMap; + std::shared_ptr<storm::adapters::AddExpressionAdapter<Type>> columnExpressionAdapter; + + // All pairs of row/column meta variables. + std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> rowColumnMetaVariablePairs; + + // The meta variables used to encode the nondeterminism. + std::vector<storm::expressions::Variable> nondeterminismMetaVariables; + + // The meta variables used to encode the synchronization. + std::vector<storm::expressions::Variable> synchronizationMetaVariables; + + // A set of all variables used for encoding the nondeterminism (i.e. nondetermism + synchronization + // variables). This is handy to abstract from this variable set. + std::set<storm::expressions::Variable> allNondeterminismVariables; + + // As set of all variables used for encoding the synchronization. + std::set<storm::expressions::Variable> allSynchronizationMetaVariables; - // As set of all variables used for encoding the synchronization. - std::set<storm::expressions::Variable> allSynchronizationMetaVariables; + // DDs representing the identity for each variable. + std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> variableToIdentityMap; - // DDs representing the identity for each variable. - std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> variableToIdentityMap; + // A set of all meta variables that correspond to global variables. + std::set<storm::expressions::Variable> allGlobalVariables; - // A set of all meta variables that correspond to global variables. - std::set<storm::expressions::Variable> allGlobalVariables; + // DDs representing the identity for each module. + std::map<std::string, storm::dd::Add<Type, ValueType>> moduleToIdentityMap; - // DDs representing the identity for each module. - std::map<std::string, storm::dd::Add<Type, ValueType>> moduleToIdentityMap; + // DDs representing the valid ranges of the variables of each module. + std::map<std::string, storm::dd::Add<Type, ValueType>> moduleToRangeMap; + + private: + /*! + * Creates the required meta variables and variable/module identities. + */ + void createMetaVariablesAndIdentities() { + // Add synchronization variables. + for (auto const& actionIndex : program.getSynchronizingActionIndices()) { + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(program.getActionName(actionIndex)); + synchronizationMetaVariables.push_back(variablePair.first); + allSynchronizationMetaVariables.insert(variablePair.first); + allNondeterminismVariables.insert(variablePair.first); + } - // DDs representing the valid ranges of the variables of each module. - std::map<std::string, storm::dd::Add<Type, ValueType>> moduleToRangeMap; + // Add nondeterminism variables (number of modules + number of commands). + uint_fast64_t numberOfNondeterminismVariables = program.getModules().size(); + for (auto const& module : program.getModules()) { + numberOfNondeterminismVariables += module.getNumberOfCommands(); + } + for (uint_fast64_t i = 0; i < numberOfNondeterminismVariables; ++i) { + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable("nondet" + std::to_string(i)); + nondeterminismMetaVariables.push_back(variablePair.first); + allNondeterminismVariables.insert(variablePair.first); + } - private: - /*! - * Creates the required meta variables and variable/module identities. - */ - void createMetaVariablesAndIdentities() { - // Add synchronization variables. - for (auto const& actionIndex : program.getSynchronizingActionIndices()) { - std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(program.getActionName(actionIndex)); - synchronizationMetaVariables.push_back(variablePair.first); - allSynchronizationMetaVariables.insert(variablePair.first); - allNondeterminismVariables.insert(variablePair.first); - } + // Create meta variables for global program variables. + for (storm::prism::IntegerVariable const& integerVariable : program.getGlobalIntegerVariables()) { + int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt(); + int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt(); + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(integerVariable.getName(), low, high); - // Add nondeterminism variables (number of modules + number of commands). - uint_fast64_t numberOfNondeterminismVariables = program.getModules().size(); - for (auto const& module : program.getModules()) { - numberOfNondeterminismVariables += module.getNumberOfCommands(); - } - for (uint_fast64_t i = 0; i < numberOfNondeterminismVariables; ++i) { - std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable("nondet" + std::to_string(i)); - nondeterminismMetaVariables.push_back(variablePair.first); - allNondeterminismVariables.insert(variablePair.first); - } + STORM_LOG_TRACE("Created meta variables for global integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); + + rowMetaVariables.insert(variablePair.first); + variableToRowMetaVariableMap.emplace(integerVariable.getExpressionVariable(), variablePair.first); + + columnMetaVariables.insert(variablePair.second); + variableToColumnMetaVariableMap.emplace(integerVariable.getExpressionVariable(), variablePair.second); - // Create meta variables for global program variables. - for (storm::prism::IntegerVariable const& integerVariable : program.getGlobalIntegerVariables()) { + storm::dd::Add<Type, ValueType> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>() * manager->getRange(variablePair.first).template toAdd<ValueType>() * manager->getRange(variablePair.second).template toAdd<ValueType>(); + variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity); + rowColumnMetaVariablePairs.push_back(variablePair); + + allGlobalVariables.insert(integerVariable.getExpressionVariable()); + } + for (storm::prism::BooleanVariable const& booleanVariable : program.getGlobalBooleanVariables()) { + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(booleanVariable.getName()); + + STORM_LOG_TRACE("Created meta variables for global boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); + + rowMetaVariables.insert(variablePair.first); + variableToRowMetaVariableMap.emplace(booleanVariable.getExpressionVariable(), variablePair.first); + + columnMetaVariables.insert(variablePair.second); + variableToColumnMetaVariableMap.emplace(booleanVariable.getExpressionVariable(), variablePair.second); + + storm::dd::Add<Type, ValueType> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>(); + variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity); + + rowColumnMetaVariablePairs.push_back(variablePair); + allGlobalVariables.insert(booleanVariable.getExpressionVariable()); + } + + // Create meta variables for each of the modules' variables. + for (storm::prism::Module const& module : program.getModules()) { + storm::dd::Bdd<Type> moduleIdentity = manager->getBddOne(); + storm::dd::Bdd<Type> moduleRange = manager->getBddOne(); + + for (storm::prism::IntegerVariable const& integerVariable : module.getIntegerVariables()) { int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt(); int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt(); std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(integerVariable.getName(), low, high); + STORM_LOG_TRACE("Created meta variables for integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - STORM_LOG_TRACE("Created meta variables for global integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - rowMetaVariables.insert(variablePair.first); variableToRowMetaVariableMap.emplace(integerVariable.getExpressionVariable(), variablePair.first); columnMetaVariables.insert(variablePair.second); variableToColumnMetaVariableMap.emplace(integerVariable.getExpressionVariable(), variablePair.second); - storm::dd::Add<Type, ValueType> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>() * manager->getRange(variablePair.first).template toAdd<ValueType>() * manager->getRange(variablePair.second).template toAdd<ValueType>(); - variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity); - rowColumnMetaVariablePairs.push_back(variablePair); + storm::dd::Bdd<Type> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)) && manager->getRange(variablePair.first) && manager->getRange(variablePair.second); + variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>()); + moduleIdentity &= variableIdentity; + moduleRange &= manager->getRange(variablePair.first); - allGlobalVariables.insert(integerVariable.getExpressionVariable()); + rowColumnMetaVariablePairs.push_back(variablePair); } - for (storm::prism::BooleanVariable const& booleanVariable : program.getGlobalBooleanVariables()) { + for (storm::prism::BooleanVariable const& booleanVariable : module.getBooleanVariables()) { std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(booleanVariable.getName()); - - STORM_LOG_TRACE("Created meta variables for global boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); + STORM_LOG_TRACE("Created meta variables for boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); rowMetaVariables.insert(variablePair.first); variableToRowMetaVariableMap.emplace(booleanVariable.getExpressionVariable(), variablePair.first); @@ -135,60 +179,163 @@ namespace storm { columnMetaVariables.insert(variablePair.second); variableToColumnMetaVariableMap.emplace(booleanVariable.getExpressionVariable(), variablePair.second); - storm::dd::Add<Type, ValueType> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>(); - variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity); + storm::dd::Bdd<Type> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)) && manager->getRange(variablePair.first) && manager->getRange(variablePair.second); + variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>()); + moduleIdentity &= variableIdentity; + moduleRange &= manager->getRange(variablePair.first); rowColumnMetaVariablePairs.push_back(variablePair); - allGlobalVariables.insert(booleanVariable.getExpressionVariable()); } + moduleToIdentityMap[module.getName()] = moduleIdentity.template toAdd<ValueType>(); + moduleToRangeMap[module.getName()] = moduleRange.template toAdd<ValueType>(); + } + } + }; + + template <storm::dd::DdType Type, typename ValueType> + class ModuleComposer : public storm::prism::CompositionVisitor { + public: + ModuleComposer(typename DdPrismModelBuilder<Type, ValueType>::GenerationInformation& generationInfo) : generationInfo(generationInfo) { + for (auto const& actionIndex : generationInfo.program.getSynchronizingActionIndices()) { + synchronizingActionToOffsetMap[actionIndex] = 0; + } + } + + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram compose(storm::prism::Composition const& composition) { + std::cout << "composing the system " << composition << std::endl; + return boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.accept(*this)); + } + + virtual boost::any visit(storm::prism::ModuleComposition const& composition) override { + STORM_LOG_TRACE("Translating module '" << composition.getModuleName() << "'."); + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram result = DdPrismModelBuilder<Type, ValueType>::createModuleDecisionDiagram(generationInfo, generationInfo.program.getModule(composition.getModuleName()), synchronizingActionToOffsetMap); + + // Update the offset indices. + for (auto const& actionIndex : generationInfo.program.getSynchronizingActionIndices()) { + if (result.hasSynchronizingAction(actionIndex)) { + synchronizingActionToOffsetMap[actionIndex] = result.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables; + } + } + + return result; + } + + virtual boost::any visit(storm::prism::RenamingComposition const& composition) override { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Renaming is currently not supported in symbolic model building."); + } + + virtual boost::any visit(storm::prism::HidingComposition const& composition) override { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Hiding is currently not supported in symbolic model building."); + } + + virtual boost::any visit(storm::prism::SynchronizingParallelComposition const& composition) override { + // First, we translate the subcompositions. + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this)); + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this)); + + // Then, determine the action indices on which we need to synchronize. + std::set<uint_fast64_t> leftSynchronizationActionIndices = left.getSynchronizingActionIndices(); + for (auto const& entry : leftSynchronizationActionIndices) { + std::cout << "entry 1: " << entry << std::endl; + } + std::set<uint_fast64_t> rightSynchronizationActionIndices = right.getSynchronizingActionIndices(); + for (auto const& entry : rightSynchronizationActionIndices) { + std::cout << "entry 2: " << entry << std::endl; + } + std::set<uint_fast64_t> synchronizationActionIndices; + std::set_intersection(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(), rightSynchronizationActionIndices.begin(), rightSynchronizationActionIndices.end(), std::inserter(synchronizationActionIndices, synchronizationActionIndices.begin())); + + // Finally, we compose the subcompositions to create the result. For this, we modify the left + // subcomposition in place and later return it. + composeInParallel(left, right, synchronizationActionIndices); + return left; + } + + virtual boost::any visit(storm::prism::InterleavingParallelComposition const& composition) override { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Interleaving is currently not supported in symbolic model building."); + } + + virtual boost::any visit(storm::prism::RestrictedParallelComposition const& composition) override { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Restricted parallel composition is currently not supported in symbolic model building."); + } - // Create meta variables for each of the modules' variables. - for (storm::prism::Module const& module : program.getModules()) { - storm::dd::Bdd<Type> moduleIdentity = manager->getBddOne(); - storm::dd::Bdd<Type> moduleRange = manager->getBddOne(); - - for (storm::prism::IntegerVariable const& integerVariable : module.getIntegerVariables()) { - int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt(); - int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt(); - std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(integerVariable.getName(), low, high); - STORM_LOG_TRACE("Created meta variables for integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - - rowMetaVariables.insert(variablePair.first); - variableToRowMetaVariableMap.emplace(integerVariable.getExpressionVariable(), variablePair.first); - - columnMetaVariables.insert(variablePair.second); - variableToColumnMetaVariableMap.emplace(integerVariable.getExpressionVariable(), variablePair.second); - - storm::dd::Bdd<Type> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)) && manager->getRange(variablePair.first) && manager->getRange(variablePair.second); - variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>()); - moduleIdentity &= variableIdentity; - moduleRange &= manager->getRange(variablePair.first); - - rowColumnMetaVariablePairs.push_back(variablePair); - } - for (storm::prism::BooleanVariable const& booleanVariable : module.getBooleanVariables()) { - std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(booleanVariable.getName()); - STORM_LOG_TRACE("Created meta variables for boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); + private: + /*! + * Composes the given modules while synchronizing over the provided action indices. As a result, the first + * module is modified in place and will contain the composition after a call to this method. + */ + void composeInParallel(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& left, typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& right, std::set<uint_fast64_t> const& synchronizationActionIndices) { + + std::vector<std::string> actionNames; + for (auto const& actionIndex : synchronizationActionIndices) { + actionNames.push_back(generationInfo.program.getActionName(actionIndex)); + } + STORM_LOG_TRACE("Composing two modules over the actions '" << boost::join(actionNames, ", ") << "'."); + + // Combine the tau action. + uint_fast64_t numberOfUsedNondeterminismVariables = right.independentAction.numberOfUsedNondeterminismVariables; + left.independentAction = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, left.independentAction, right.independentAction, left.identity, right.identity); + numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, left.independentAction.numberOfUsedNondeterminismVariables); - rowMetaVariables.insert(variablePair.first); - variableToRowMetaVariableMap.emplace(booleanVariable.getExpressionVariable(), variablePair.first); - - columnMetaVariables.insert(variablePair.second); - variableToColumnMetaVariableMap.emplace(booleanVariable.getExpressionVariable(), variablePair.second); - - storm::dd::Bdd<Type> variableIdentity = manager->template getIdentity<ValueType>(variablePair.first).equals(manager->template getIdentity<ValueType>(variablePair.second)) && manager->getRange(variablePair.first) && manager->getRange(variablePair.second); - variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>()); - moduleIdentity &= variableIdentity; - moduleRange &= manager->getRange(variablePair.first); + // Create an empty action for the case where one of the modules does not have a certain action. + typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram emptyAction(*generationInfo.manager); - rowColumnMetaVariablePairs.push_back(variablePair); + // Treat all non-tau actions of the left module. + for (auto& action : left.synchronizingActionToDecisionDiagramMap) { + // If we need to synchronize over this action index, we try to do so now. + if (synchronizationActionIndices.find(action.first) != synchronizationActionIndices.end()) { + // If we are to synchronize over an action that does not exist in the second module, the result + // is that the synchronization is the empty action. + if (right.hasSynchronizingAction(action.first)) { + action.second = emptyAction; + } else { + // Otherwise, the actions of the modules are synchronized. + action.second = DdPrismModelBuilder<Type, ValueType>::combineSynchronizingActions(generationInfo, action.second, right.synchronizingActionToDecisionDiagramMap[action.first]); + } + } else { + // If we don't synchronize over this action, we need to construct the interleaving. + + // If both modules contain the action, we need to mutually multiply the other identity. + if (right.hasSynchronizingAction(action.first)) { + action.second = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, right.synchronizingActionToDecisionDiagramMap[action.first], left.identity, right.identity); + } else { + // If only the first module has this action, we need to use a dummy action decision diagram + // for the second module. + action.second = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, emptyAction, left.identity, right.identity); } - moduleToIdentityMap[module.getName()] = moduleIdentity.template toAdd<ValueType>(); - moduleToRangeMap[module.getName()] = moduleRange.template toAdd<ValueType>(); } + numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, action.second.numberOfUsedNondeterminismVariables); } - }; + + // Treat all non-tau actions of the right module. + for (auto const& actionIndex : right.getSynchronizingActionIndices()) { + // Here, we only need to treat actions that the first module does not have, because we have handled + // this case earlier. + if (!left.hasSynchronizingAction(actionIndex)) { + if (synchronizationActionIndices.find(actionIndex) != synchronizationActionIndices.end()) { + // If we are to synchronize over this action that does not exist in the first module, the + // result is that the synchronization is the empty action. + left.synchronizingActionToDecisionDiagramMap[actionIndex] = emptyAction; + } else { + // If only the second module has this action, we need to use a dummy action decision diagram + // for the first module. + left.synchronizingActionToDecisionDiagramMap[actionIndex] = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, emptyAction, right.synchronizingActionToDecisionDiagramMap[actionIndex], left.identity, right.identity); + } + } + numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, left.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables); + } + + // Combine identity matrices. + left.identity = left.identity * right.identity; + + // Keep track of the number of nondeterminism variables used. + left.numberOfUsedNondeterminismVariables = std::max(left.numberOfUsedNondeterminismVariables, numberOfUsedNondeterminismVariables); + } + typename DdPrismModelBuilder<Type, ValueType>::GenerationInformation& generationInfo; + std::map<uint_fast64_t, uint_fast64_t> synchronizingActionToOffsetMap; + }; + template <storm::dd::DdType Type, typename ValueType> DdPrismModelBuilder<Type, ValueType>::Options::Options() : buildAllRewardModels(true), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), terminalStates(), negatedTerminalStates() { // Intentionally left empty. @@ -224,7 +371,7 @@ namespace storm { if (negatedTerminalStates) { negatedTerminalStates.reset(); } - + // If we are not required to build all reward models, we determine the reward models we need to build. if (!buildAllRewardModels) { std::set<std::string> referencedRewardModels = formula.getReferencedRewardModels(); @@ -369,7 +516,7 @@ namespace storm { STORM_LOG_WARN_COND(!updateResults.back().updateDd.isZero(), "Update '" << update << "' does not have any effect."); } - + // Start by gathering all variables that were written in at least one update. std::set<storm::expressions::Variable> globalVariablesInSomeUpdate; @@ -386,7 +533,7 @@ namespace storm { for (auto& updateResult : updateResults) { std::set<storm::expressions::Variable> missingIdentities; std::set_difference(globalVariablesInSomeUpdate.begin(), globalVariablesInSomeUpdate.end(), updateResult.assignedGlobalVariables.begin(), updateResult.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - + for (auto const& variable : missingIdentities) { STORM_LOG_TRACE("Multiplying identity for variable " << variable.getName() << "[" << variable.getIndex() << "] to update."); updateResult.updateDd *= generationInfo.variableToIdentityMap.at(variable); @@ -448,7 +595,7 @@ namespace storm { // Start by gathering all variables that were written in at least one action DD. std::set<storm::expressions::Variable> globalVariablesInActionDd; std::set_union(action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), action2.assignedGlobalVariables.begin(), action2.assignedGlobalVariables.end(), std::inserter(globalVariablesInActionDd, globalVariablesInActionDd.begin())); - + std::set<storm::expressions::Variable> missingIdentitiesInAction1; std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), std::inserter(missingIdentitiesInAction1, missingIdentitiesInAction1.begin())); for (auto const& variable : missingIdentitiesInAction1) { @@ -460,7 +607,7 @@ namespace storm { for (auto const& variable : missingIdentitiesInAction2) { action2.transitionsDd *= generationInfo.variableToIdentityMap.at(variable); } - + return globalVariablesInActionDd; } @@ -473,7 +620,7 @@ namespace storm { } STORM_LOG_TRACE("Equalizing assigned global variables."); - + // Then multiply the transitions of each action with the missing identities. for (auto& actionDd : actionDds) { STORM_LOG_TRACE("Equalizing next action."); @@ -545,7 +692,7 @@ namespace storm { allGuards |= commandDd.guardDd.toBdd(); } uint_fast64_t maxChoices = static_cast<uint_fast64_t>(sumOfGuards.getMax()); - + STORM_LOG_TRACE("Found " << maxChoices << " local choices."); // Depending on the maximal number of nondeterminstic choices, we need to use some variables to encode the nondeterminism. @@ -626,7 +773,7 @@ namespace storm { return ActionDecisionDiagram(allGuards.template toAdd<ValueType>(), allCommands, assignedGlobalVariables, nondeterminismVariableOffset + numberOfBinaryVariables); } } - + template <storm::dd::DdType Type, typename ValueType> typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineSynchronizingActions(GenerationInformation const& generationInfo, ActionDecisionDiagram const& action1, ActionDecisionDiagram const& action2) { std::set<storm::expressions::Variable> assignedGlobalVariables; @@ -638,7 +785,7 @@ namespace storm { typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2, storm::dd::Add<Type, ValueType> const& identityDd1, storm::dd::Add<Type, ValueType> const& identityDd2) { storm::dd::Add<Type, ValueType> action1Extended = action1.transitionsDd * identityDd2; storm::dd::Add<Type, ValueType> action2Extended = action2.transitionsDd * identityDd1; - + STORM_LOG_TRACE("Combining unsynchronized actions."); // Make both action DDs write to the same global variables. @@ -679,7 +826,7 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Illegal model type."); } } - + template <storm::dd::DdType Type, typename ValueType> typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram DdPrismModelBuilder<Type, ValueType>::createModuleDecisionDiagram(GenerationInformation& generationInfo, storm::prism::Module const& module, std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap) { // Start by creating the action DD for the independent action. @@ -726,7 +873,7 @@ namespace storm { // First, determine the highest number of nondeterminism variables that is used in any action and make // all actions use the same amout of nondeterminism variables. uint_fast64_t numberOfUsedNondeterminismVariables = module.numberOfUsedNondeterminismVariables; - + // Compute missing global variable identities in independent action. std::set<storm::expressions::Variable> missingIdentities; std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(), module.independentAction.assignedGlobalVariables.begin(), module.independentAction.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); @@ -742,7 +889,7 @@ namespace storm { nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>(); } result = identityEncoding * module.independentAction.transitionsDd * nondeterminismEncoding; - + // Add variables to synchronized action DDs. std::map<uint_fast64_t, storm::dd::Add<Type, ValueType>> synchronizingActionToDdMap; for (auto const& synchronizingAction : module.synchronizingActionToDecisionDiagramMap) { @@ -773,7 +920,7 @@ namespace storm { for (auto const& synchronizingAction : synchronizingActionToDdMap) { result += synchronizingAction.second; } - + return result; } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) { // Simply add all actions. @@ -789,67 +936,14 @@ namespace storm { template <storm::dd::DdType Type, typename ValueType> typename DdPrismModelBuilder<Type, ValueType>::SystemResult DdPrismModelBuilder<Type, ValueType>::createSystemDecisionDiagram(GenerationInformation& generationInfo) { - // Create the initial offset mapping. - std::map<uint_fast64_t, uint_fast64_t> synchronizingActionToOffsetMap; - for (auto const& actionIndex : generationInfo.program.getSynchronizingActionIndices()) { - synchronizingActionToOffsetMap[actionIndex] = 0; - } - - // Start by creating DDs for the first module. - STORM_LOG_TRACE("Translating (first) module '" << generationInfo.program.getModule(0).getName() << "'."); - ModuleDecisionDiagram system = createModuleDecisionDiagram(generationInfo, generationInfo.program.getModule(0), synchronizingActionToOffsetMap); - - // Now translate module by module and combine it with the system created thus far. - for (uint_fast64_t i = 1; i < generationInfo.program.getNumberOfModules(); ++i) { - storm::prism::Module const& currentModule = generationInfo.program.getModule(i); - STORM_LOG_TRACE("Translating module '" << currentModule.getName() << "'."); - - // Update the offset index. - for (auto const& actionIndex : generationInfo.program.getSynchronizingActionIndices()) { - if (system.hasSynchronizingAction(actionIndex)) { - synchronizingActionToOffsetMap[actionIndex] = system.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables; - } - } - - ModuleDecisionDiagram currentModuleDd = createModuleDecisionDiagram(generationInfo, currentModule, synchronizingActionToOffsetMap); - - // Combine the non-synchronizing action. - uint_fast64_t numberOfUsedNondeterminismVariables = currentModuleDd.numberOfUsedNondeterminismVariables; - system.independentAction = combineUnsynchronizedActions(generationInfo, system.independentAction, currentModuleDd.independentAction, system.identity, currentModuleDd.identity); - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, system.independentAction.numberOfUsedNondeterminismVariables); - - ActionDecisionDiagram emptyAction(*generationInfo.manager); - - // For all synchronizing actions that the next module does not have, we need to multiply the identity of the next module. - for (auto& action : system.synchronizingActionToDecisionDiagramMap) { - if (!currentModuleDd.hasSynchronizingAction(action.first)) { - action.second = combineUnsynchronizedActions(generationInfo, action.second, emptyAction, system.identity, currentModuleDd.identity); - } - } - - // Combine synchronizing actions. - for (auto const& actionIndex : currentModule.getSynchronizingActionIndices()) { - if (system.hasSynchronizingAction(actionIndex)) { - system.synchronizingActionToDecisionDiagramMap[actionIndex] = combineSynchronizingActions(generationInfo, system.synchronizingActionToDecisionDiagramMap[actionIndex], currentModuleDd.synchronizingActionToDecisionDiagramMap[actionIndex]); - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, system.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables); - } else { - system.synchronizingActionToDecisionDiagramMap[actionIndex] = combineUnsynchronizedActions(generationInfo, emptyAction, currentModuleDd.synchronizingActionToDecisionDiagramMap[actionIndex], system.identity, currentModuleDd.identity); - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, system.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables); - } - } - - // Combine identity matrices. - system.identity = system.identity * currentModuleDd.identity; - - // Keep track of the number of nondeterminism variables used. - system.numberOfUsedNondeterminismVariables = std::max(system.numberOfUsedNondeterminismVariables, numberOfUsedNondeterminismVariables); - } + ModuleComposer<Type, ValueType> composer(generationInfo); + ModuleDecisionDiagram system = composer.compose(generationInfo.program.specifiesSystemComposition() ? generationInfo.program.getSystemCompositionConstruct().getSystemComposition() : *generationInfo.program.getDefaultSystemComposition()); storm::dd::Add<Type, ValueType> result = createSystemFromModule(generationInfo, system); - + // Create an auxiliary DD that is used later during the construction of reward models. storm::dd::Add<Type, ValueType> stateActionDd = result.sumAbstract(generationInfo.columnMetaVariables); - + // For DTMCs, we normalize each row to 1 (to account for non-determinism). if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) { result = result / stateActionDd; @@ -892,7 +986,7 @@ namespace storm { // Next, build the state-action reward vector. boost::optional<storm::dd::Add<Type, ValueType>> stateActionRewards; if (rewardModel.hasStateActionRewards()) { - stateActionRewards = generationInfo.manager->template getAddZero<ValueType>(); + stateActionRewards = generationInfo.manager->template getAddZero<ValueType>(); for (auto const& stateActionReward : rewardModel.getStateActionRewards()) { storm::dd::Add<Type, ValueType> states = generationInfo.rowExpressionAdapter->translateExpression(stateActionReward.getStatePredicateExpression()); @@ -980,7 +1074,7 @@ namespace storm { return storm::models::symbolic::StandardRewardModel<Type, double>(stateRewards, stateActionRewards, transitionRewards); } - + template <storm::dd::DdType Type, typename ValueType> storm::prism::Program const& DdPrismModelBuilder<Type, ValueType>::getTranslatedProgram() const { return preparedProgram.get(); @@ -1017,7 +1111,7 @@ namespace storm { // Start by initializing the structure used for storing all information needed during the model generation. // In particular, this creates the meta variables used to encode the model. GenerationInformation generationInfo(*preparedProgram); - + SystemResult system = createSystemDecisionDiagram(generationInfo); storm::dd::Add<Type, ValueType> transitionMatrix = system.allTransitionsDd; @@ -1037,7 +1131,7 @@ namespace storm { std::string const& labelName = boost::get<std::string>(options.terminalStates.get()); terminalExpression = preparedProgram->getLabelExpression(labelName); } - + // If the expression refers to constants of the model, we need to substitute them. terminalExpression = terminalExpression.substitute(constantsSubstitution); @@ -1078,7 +1172,7 @@ namespace storm { // Detect deadlocks and 1) fix them if requested 2) throw an error otherwise. storm::dd::Bdd<Type> statesWithTransition = transitionMatrixBdd.existsAbstract(generationInfo.columnMetaVariables); storm::dd::Add<Type, ValueType> deadlockStates = (reachableStates && !statesWithTransition).template toAdd<ValueType>(); - + if (!deadlockStates.isZero()) { // If we need to fix deadlocks, we do so now. if (!storm::settings::getModule<storm::settings::modules::MarkovChainSettings>().isDontFixDeadlocksSet()) { @@ -1088,7 +1182,7 @@ namespace storm { for (auto it = deadlockStates.begin(), ite = deadlockStates.end(); it != ite && count < 3; ++it, ++count) { STORM_LOG_INFO((*it).first.toPrettyString(generationInfo.rowMetaVariables) << std::endl); } - + if (program.getModelType() == storm::prism::Program::ModelType::DTMC) { // For DTMCs, we can simply add the identity of the global module for all deadlock states. transitionMatrix += deadlockStates * globalModule.identity; @@ -1175,7 +1269,7 @@ namespace storm { changed = false; storm::dd::Bdd<Type> tmp = reachableStates.relationalProduct(transitionBdd, generationInfo.rowMetaVariables, generationInfo.columnMetaVariables); storm::dd::Bdd<Type> newReachableStates = tmp && (!reachableStates); - + // Check whether new states were indeed discovered. if (!newReachableStates.isZero()) { changed = true; @@ -1192,7 +1286,7 @@ namespace storm { // Explicitly instantiate the symbolic model builder. template class DdPrismModelBuilder<storm::dd::DdType::CUDD>; template class DdPrismModelBuilder<storm::dd::DdType::Sylvan>; - + } // namespace adapters } // namespace storm diff --git a/src/builder/DdPrismModelBuilder.h b/src/builder/DdPrismModelBuilder.h index 3a468d6bc..44e9fdb9a 100644 --- a/src/builder/DdPrismModelBuilder.h +++ b/src/builder/DdPrismModelBuilder.h @@ -188,6 +188,14 @@ namespace storm { return synchronizingActionToDecisionDiagramMap.find(actionIndex) != synchronizingActionToDecisionDiagramMap.end(); } + std::set<uint_fast64_t> getSynchronizingActionIndices() const { + std::set<uint_fast64_t> result; + for (auto const& entry : synchronizingActionToDecisionDiagramMap) { + result.insert(entry.first); + } + return result; + } + // The decision diagram for the independent action. ActionDecisionDiagram independentAction; @@ -210,7 +218,11 @@ namespace storm { * Structure to store the result of the system creation phase. */ struct SystemResult; + private: + template <storm::dd::DdType TypePrime, typename ValueTypePrime> + friend class ModuleComposer; + static std::set<storm::expressions::Variable> equalizeAssignedGlobalVariables(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2); static std::set<storm::expressions::Variable> equalizeAssignedGlobalVariables(GenerationInformation const& generationInfo, std::vector<ActionDecisionDiagram>& actionDds); diff --git a/src/storage/prism/InterleavingParallelComposition.cpp b/src/storage/prism/InterleavingParallelComposition.cpp index 311b7eede..3c5de8f8e 100644 --- a/src/storage/prism/InterleavingParallelComposition.cpp +++ b/src/storage/prism/InterleavingParallelComposition.cpp @@ -12,7 +12,7 @@ namespace storm { } void InterleavingParallelComposition::writeToStream(std::ostream& stream) const { - stream << "(" << *left << " ||| " << *right << ")"; + stream << "(" << this->getLeftSubcomposition() << " ||| " << this->getRightSubcomposition() << ")"; } } diff --git a/src/storage/prism/InterleavingParallelComposition.h b/src/storage/prism/InterleavingParallelComposition.h index 883323bbf..42c8ca771 100644 --- a/src/storage/prism/InterleavingParallelComposition.h +++ b/src/storage/prism/InterleavingParallelComposition.h @@ -13,10 +13,6 @@ namespace storm { protected: virtual void writeToStream(std::ostream& stream) const override; - - private: - std::shared_ptr<Composition> left; - std::shared_ptr<Composition> right; }; } } diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 3ca4675e8..57fd2a471 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -27,11 +27,21 @@ namespace storm { } bool isValid(Composition const& composition) { - return boost::any_cast<bool>(composition.accept(*this)); + bool isValid = boost::any_cast<bool>(composition.accept(*this)); + if (appearingModules.size() != program.getNumberOfModules()) { + isValid = false; + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "Not every module is used in the system composition."); + } + return isValid; } virtual boost::any visit(ModuleComposition const& composition) override { - return program.hasModule(composition.getModuleName()); + bool isValid = program.hasModule(composition.getModuleName()); + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "The module \"" << composition.getModuleName() << "\" referred to in the system composition does not exist."); + isValid = appearingModules.find(composition.getModuleName()) == appearingModules.end(); + STORM_LOG_THROW(isValid, storm::exceptions::WrongFormatException, "The module \"" << composition.getModuleName() << "\" is referred to more than once in the system composition."); + appearingModules.insert(composition.getModuleName()); + return isValid; } virtual boost::any visit(RenamingComposition const& composition) override { @@ -56,6 +66,7 @@ namespace storm { private: storm::prism::Program const& program; + std::set<std::string> appearingModules; }; Program::Program(std::shared_ptr<storm::expressions::ExpressionManager> manager, ModelType modelType, std::vector<Constant> const& constants, std::vector<BooleanVariable> const& globalBooleanVariables, std::vector<IntegerVariable> const& globalIntegerVariables, std::vector<Formula> const& formulas, std::vector<Module> const& modules, std::map<std::string, uint_fast64_t> const& actionToIndexMap, std::vector<RewardModel> const& rewardModels, std::vector<Label> const& labels, boost::optional<InitialConstruct> const& initialConstruct, boost::optional<SystemCompositionConstruct> const& compositionConstruct, std::string const& filename, uint_fast64_t lineNumber, bool finalModel) @@ -352,6 +363,18 @@ namespace storm { return systemCompositionConstruct; } + std::shared_ptr<Composition> Program::getDefaultSystemComposition() const { + std::shared_ptr<Composition> current = std::make_shared<ModuleComposition>(this->modules.front().getName()); + + for (uint_fast64_t index = 1; index < this->modules.size(); ++index) { + std::shared_ptr<Composition> newComposition = std::make_shared<SynchronizingParallelComposition>(current, std::make_shared<ModuleComposition>(this->modules[index].getName())); + current = newComposition; + } + + + return current; + } + std::set<std::string> const& Program::getActions() const { return this->actions; } diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index fee87e40c..6fc0fc2f4 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -296,6 +296,13 @@ namespace storm { */ boost::optional<SystemCompositionConstruct> getOptionalSystemCompositionConstruct() const; + /*! + * Retrieves the default system composition for this program. + * + * @return The default system composition. + */ + std::shared_ptr<Composition> getDefaultSystemComposition() const; + /*! * Retrieves the set of actions present in the program. * diff --git a/src/storage/prism/RestrictedParallelComposition.cpp b/src/storage/prism/RestrictedParallelComposition.cpp index c3c296311..5c9e54770 100644 --- a/src/storage/prism/RestrictedParallelComposition.cpp +++ b/src/storage/prism/RestrictedParallelComposition.cpp @@ -14,7 +14,7 @@ namespace storm { } void RestrictedParallelComposition::writeToStream(std::ostream& stream) const { - stream << "(" << *left << " |[" << boost::algorithm::join(synchronizingActions, ", ") << "]| " << *right << ")"; + stream << "(" << this->getLeftSubcomposition() << " |[" << boost::algorithm::join(synchronizingActions, ", ") << "]| " << this->getRightSubcomposition() << ")"; } } diff --git a/src/storage/prism/RestrictedParallelComposition.h b/src/storage/prism/RestrictedParallelComposition.h index d04151762..bddcb663e 100644 --- a/src/storage/prism/RestrictedParallelComposition.h +++ b/src/storage/prism/RestrictedParallelComposition.h @@ -18,9 +18,7 @@ namespace storm { virtual void writeToStream(std::ostream& stream) const override; private: - std::shared_ptr<Composition> left; std::set<std::string> synchronizingActions; - std::shared_ptr<Composition> right; }; } } diff --git a/src/storage/prism/SynchronizingParallelComposition.cpp b/src/storage/prism/SynchronizingParallelComposition.cpp index bc502ce53..56652c3a7 100644 --- a/src/storage/prism/SynchronizingParallelComposition.cpp +++ b/src/storage/prism/SynchronizingParallelComposition.cpp @@ -12,7 +12,7 @@ namespace storm { } void SynchronizingParallelComposition::writeToStream(std::ostream& stream) const { - stream << "(" << *left << " || " << *right << ")"; + stream << "(" << this->getLeftSubcomposition() << " || " << this->getRightSubcomposition() << ")"; } } diff --git a/src/storage/prism/SynchronizingParallelComposition.h b/src/storage/prism/SynchronizingParallelComposition.h index 517525bc2..3bb668bb0 100644 --- a/src/storage/prism/SynchronizingParallelComposition.h +++ b/src/storage/prism/SynchronizingParallelComposition.h @@ -13,10 +13,6 @@ namespace storm { protected: virtual void writeToStream(std::ostream& stream) const override; - - private: - std::shared_ptr<Composition> left; - std::shared_ptr<Composition> right; }; } } From 4e97d294b314b33de9c0f8dc8260dd27d7a5400f Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 18 May 2016 19:03:48 +0200 Subject: [PATCH 2/4] fixed bug in composition Former-commit-id: 0efedc3ec13fd264e569a4476a6a97b84e44fbeb --- src/builder/DdPrismModelBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index d34ed80f3..6f56bf41c 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -286,7 +286,7 @@ namespace storm { if (synchronizationActionIndices.find(action.first) != synchronizationActionIndices.end()) { // If we are to synchronize over an action that does not exist in the second module, the result // is that the synchronization is the empty action. - if (right.hasSynchronizingAction(action.first)) { + if (!right.hasSynchronizingAction(action.first)) { action.second = emptyAction; } else { // Otherwise, the actions of the modules are synchronized. From 1df8a5c7ccee731cf06ba5624517c5c1ccb34eb3 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 18 May 2016 19:54:45 +0200 Subject: [PATCH 3/4] parallel composition (full, restricted, interleaving) for PRISM appears to work, added some more sanity checks for parallel composition Former-commit-id: 4ed40c8aba7a5b1720701ff65a06e99487a83528 --- src/builder/DdPrismModelBuilder.cpp | 39 ++++++++++++++----- src/storage/prism/Program.cpp | 24 +++++++++++- src/storage/prism/Program.h | 22 +++++++++++ .../prism/RestrictedParallelComposition.cpp | 4 ++ .../prism/RestrictedParallelComposition.h | 2 + 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index 6f56bf41c..98ff3128a 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -235,28 +235,47 @@ namespace storm { // Then, determine the action indices on which we need to synchronize. std::set<uint_fast64_t> leftSynchronizationActionIndices = left.getSynchronizingActionIndices(); - for (auto const& entry : leftSynchronizationActionIndices) { - std::cout << "entry 1: " << entry << std::endl; - } std::set<uint_fast64_t> rightSynchronizationActionIndices = right.getSynchronizingActionIndices(); - for (auto const& entry : rightSynchronizationActionIndices) { - std::cout << "entry 2: " << entry << std::endl; - } std::set<uint_fast64_t> synchronizationActionIndices; std::set_intersection(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(), rightSynchronizationActionIndices.begin(), rightSynchronizationActionIndices.end(), std::inserter(synchronizationActionIndices, synchronizationActionIndices.begin())); - // Finally, we compose the subcompositions to create the result. For this, we modify the left - // subcomposition in place and later return it. + // Finally, we compose the subcompositions to create the result. composeInParallel(left, right, synchronizationActionIndices); return left; } virtual boost::any visit(storm::prism::InterleavingParallelComposition const& composition) override { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Interleaving is currently not supported in symbolic model building."); + // First, we translate the subcompositions. + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this)); + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this)); + + // Finally, we compose the subcompositions to create the result. + composeInParallel(left, right, std::set<uint_fast64_t>()); + return left; } virtual boost::any visit(storm::prism::RestrictedParallelComposition const& composition) override { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Restricted parallel composition is currently not supported in symbolic model building."); + // First, we translate the subcompositions. + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this)); + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this)); + + // Construct the synchronizing action indices from the synchronizing action names. + std::set<uint_fast64_t> synchronizingActionIndices; + for (auto const& action : composition.getSynchronizingActions()) { + synchronizingActionIndices.insert(generationInfo.program.getActionIndex(action)); + } + + std::set<uint_fast64_t> leftSynchronizationActionIndices = left.getSynchronizingActionIndices(); + bool isContainedInLeft = std::includes(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(), synchronizingActionIndices.begin(), synchronizingActionIndices.end()); + STORM_LOG_WARN_COND(isContainedInLeft, "Left subcomposition of composition '" << composition << "' does not include all actions over which to synchronize."); + + std::set<uint_fast64_t> rightSynchronizationActionIndices = right.getSynchronizingActionIndices(); + bool isContainedInRight = std::includes(rightSynchronizationActionIndices.begin(), rightSynchronizationActionIndices.end(), synchronizingActionIndices.begin(), synchronizingActionIndices.end()); + STORM_LOG_WARN_COND(isContainedInRight, "Right subcomposition of composition '" << composition << "' does not include all actions over which to synchronize."); + + // Finally, we compose the subcompositions to create the result. + composeInParallel(left, right, synchronizingActionIndices); + return left; } private: diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 57fd2a471..a76be8dc6 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -61,7 +61,15 @@ namespace storm { } virtual boost::any visit(RestrictedParallelComposition const& composition) override { - return boost::any_cast<bool>(composition.getLeftSubcomposition().accept(*this)) && boost::any_cast<bool>(composition.getRightSubcomposition().accept(*this)); + bool isValid = boost::any_cast<bool>(composition.getLeftSubcomposition().accept(*this)) && boost::any_cast<bool>(composition.getRightSubcomposition().accept(*this)); + + for (auto const& action : composition.getSynchronizingActions()) { + if (!program.hasAction(action)) { + STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "System composition refers to unknown action '" << action << "'."); + } + } + + return isValid; } private: @@ -389,6 +397,20 @@ namespace storm { return indexNamePair->second; } + uint_fast64_t Program::getActionIndex(std::string const& actionName) const { + auto const& nameIndexPair = this->actionToIndexMap.find(actionName); + STORM_LOG_THROW(nameIndexPair != this->actionToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Unknown action name '" << actionName << "'."); + return nameIndexPair->second; + } + + bool Program::hasAction(std::string const& actionName) const { + return this->actionToIndexMap.find(actionName) != this->actionToIndexMap.end(); + } + + bool Program::hasAction(uint_fast64_t const& actionIndex) const { + return this->indexToActionMap.find(actionIndex) != this->indexToActionMap.end(); + } + std::set<uint_fast64_t> const& Program::getModuleIndicesByAction(std::string const& action) const { auto const& nameIndexPair = this->actionToIndexMap.find(action); STORM_LOG_THROW(nameIndexPair != this->actionToIndexMap.end(), storm::exceptions::OutOfRangeException, "Action name '" << action << "' does not exist."); diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index 6fc0fc2f4..c76bd7ed0 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -325,6 +325,28 @@ namespace storm { */ std::string const& getActionName(uint_fast64_t actionIndex) const; + /*! + * Retrieves the index of the action with the given name. + * + * @param actionName The name of the action. + * @return The index of the action. + */ + uint_fast64_t getActionIndex(std::string const& actionName) const; + + /*! + * Retrieves whether the program has an action with the given name. + * + * @return True iff the program has an action with the given name. + */ + bool hasAction(std::string const& actionName) const; + + /*! + * Retrieves whether the program has an action with the given index. + * + * @return True iff the program has an action with the given index. + */ + bool hasAction(uint_fast64_t const& actionIndex) const; + /*! * Retrieves the indices of all modules within this program that contain commands that are labelled with the * given action. diff --git a/src/storage/prism/RestrictedParallelComposition.cpp b/src/storage/prism/RestrictedParallelComposition.cpp index 5c9e54770..681fcc4b0 100644 --- a/src/storage/prism/RestrictedParallelComposition.cpp +++ b/src/storage/prism/RestrictedParallelComposition.cpp @@ -12,6 +12,10 @@ namespace storm { boost::any RestrictedParallelComposition::accept(CompositionVisitor& visitor) const { return visitor.visit(*this); } + + std::set<std::string> const& RestrictedParallelComposition::getSynchronizingActions() const { + return synchronizingActions; + } void RestrictedParallelComposition::writeToStream(std::ostream& stream) const { stream << "(" << this->getLeftSubcomposition() << " |[" << boost::algorithm::join(synchronizingActions, ", ") << "]| " << this->getRightSubcomposition() << ")"; diff --git a/src/storage/prism/RestrictedParallelComposition.h b/src/storage/prism/RestrictedParallelComposition.h index bddcb663e..9f0f14591 100644 --- a/src/storage/prism/RestrictedParallelComposition.h +++ b/src/storage/prism/RestrictedParallelComposition.h @@ -14,6 +14,8 @@ namespace storm { virtual boost::any accept(CompositionVisitor& visitor) const override; + std::set<std::string> const& getSynchronizingActions() const; + protected: virtual void writeToStream(std::ostream& stream) const override; From 4c4e830a4cca3627ff3ea8481858599c71474417 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 18 May 2016 22:18:51 +0200 Subject: [PATCH 4/4] started on supporting renaming/hiding in PRISM models Former-commit-id: ae9ef24ad5090df427adb2cb5af3584ac1cb8e06 --- src/builder/DdPrismModelBuilder.cpp | 112 ++++++++++++++++++---- src/builder/DdPrismModelBuilder.h | 2 + src/storage/prism/HidingComposition.cpp | 4 + src/storage/prism/HidingComposition.h | 2 + src/storage/prism/RenamingComposition.cpp | 4 + src/storage/prism/RenamingComposition.h | 2 + 6 files changed, 109 insertions(+), 17 deletions(-) diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index 98ff3128a..467821ee0 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -221,11 +221,34 @@ namespace storm { } virtual boost::any visit(storm::prism::RenamingComposition const& composition) override { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Renaming is currently not supported in symbolic model building."); + // First, we translate the subcomposition. + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram sub = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getSubcomposition().accept(*this)); + + // Create the mapping from action indices to action indices. + std::map<uint_fast64_t, uint_fast64_t> renaming; + for (auto const& namePair : composition.getActionRenaming()) { + STORM_LOG_THROW(generationInfo.program.hasAction(namePair.first), storm::exceptions::InvalidArgumentException, "Composition refers to unknown action '" << namePair.first << "'."); + STORM_LOG_THROW(generationInfo.program.hasAction(namePair.second), storm::exceptions::InvalidArgumentException, "Composition refers to unknown action '" << namePair.second << "'."); + renaming.emplace(generationInfo.program.getActionIndex(namePair.first), generationInfo.program.getActionIndex(namePair.second)); + } + + // Perform the renaming and return result. + return rename(sub, renaming); } virtual boost::any visit(storm::prism::HidingComposition const& composition) override { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Hiding is currently not supported in symbolic model building."); + // First, we translate the subcomposition. + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram sub = boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getSubcomposition().accept(*this)); + + // Create the mapping from action indices to action indices. + std::set<uint_fast64_t> actionIndicesToHide; + for (auto const& action : composition.getActionsToHide()) { + STORM_LOG_THROW(generationInfo.program.hasAction(action), storm::exceptions::InvalidArgumentException, "Composition refers to unknown action '" << action << "'."); + actionIndicesToHide.insert(generationInfo.program.getActionIndex(action)); + } + + // Perform the hiding and return result. + return hide(sub, actionIndicesToHide); } virtual boost::any visit(storm::prism::SynchronizingParallelComposition const& composition) override { @@ -280,16 +303,62 @@ namespace storm { private: /*! - * Composes the given modules while synchronizing over the provided action indices. As a result, the first - * module is modified in place and will contain the composition after a call to this method. + * Hides the actions of the given module according to the given set. As a result, the module is modified in + * place. */ - void composeInParallel(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& left, typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& right, std::set<uint_fast64_t> const& synchronizationActionIndices) { + void hide(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& sub, std::set<uint_fast64_t> const& actionIndicesToHide) const { + STORM_LOG_TRACE("Hiding the actions " << boost::join(actionIndicesToHide, ", ") << "."); + + for (auto const& action : sub.synchronizingActionToDecisionDiagramMap) { + if (actionIndicesToHide.find(action) != actionIndicesToHide.end()) { + + } + } + } + + /*! + * Renames the actions of the given module according to the given renaming. + */ + typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram rename(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& sub, std::map<uint_fast64_t, uint_fast64_t> const& renaming) const { + STORM_LOG_TRACE("Renaming actions."); + uint_fast64_t numberOfUsedNondeterminismVariables = sub.numberOfUsedNondeterminismVariables; + std::map<uint_fast64_t, typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram> actionIndexToDdMap; - std::vector<std::string> actionNames; - for (auto const& actionIndex : synchronizationActionIndices) { - actionNames.push_back(generationInfo.program.getActionName(actionIndex)); + // Go through all action DDs with a synchronizing label and rename them if they appear in the renaming. + for (auto const& action : sub.synchronizingActionToDecisionDiagramMap) { + auto renamingIt = renaming.find(action.first); + if (renamingIt != renaming.end()) { + // If the action is to be renamed and an action with the target index already exists, we need + // to combine the action DDs. + auto itNewActions = actionIndexToDdMap.find(renamingIt.second); + if (itNewActions != actionIndexToDdMap.end()) { + actionIndexToDdMap[renamingIt.second] = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, itNewActions->second); + } else { + // In this case, we can simply copy the action over. + actionIndexToDdMap[renamingIt.second] = action.second; + } + } else { + // If the action is not to be renamed, we need to copy it over. However, if some other action + // was renamed to the very same action name before, we need to combine the transitions. + auto itNewActions = actionIndexToDdMap.find(action.first); + if (itNewActions != actionIndexToDdMap.end()) { + actionIndexToDdMap[action.first] = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, itNewActions->second); + } else { + // In this case, we can simply copy the action over. + actionIndexToDdMap[action.first] = action.second; + } + } } - STORM_LOG_TRACE("Composing two modules over the actions '" << boost::join(actionNames, ", ") << "'."); + + return ModuleDecisionDiagram(sub.independentAction, actionIndexToDdMap, sub.identity, numberOfUsedNondeterminismVariables); + } + + /*! + * Composes the given modules while synchronizing over the provided action indices. As a result, the first + * module is modified in place and will contain the composition after a call to this method. + */ + void composeInParallel(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& left, typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& right, std::set<uint_fast64_t> const& synchronizationActionIndices) const { + STORM_LOG_TRACE("Composing two modules over the actions " << boost::join(synchronizationActionIndices, ", ") << "."); // Combine the tau action. uint_fast64_t numberOfUsedNondeterminismVariables = right.independentAction.numberOfUsedNondeterminismVariables; @@ -802,21 +871,30 @@ namespace storm { template <storm::dd::DdType Type, typename ValueType> typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2, storm::dd::Add<Type, ValueType> const& identityDd1, storm::dd::Add<Type, ValueType> const& identityDd2) { - storm::dd::Add<Type, ValueType> action1Extended = action1.transitionsDd * identityDd2; - storm::dd::Add<Type, ValueType> action2Extended = action2.transitionsDd * identityDd1; + // First extend the action DDs by the other identities. + STORM_LOG_TRACE("Multiplying identities to combine unsynchronized actions."); + action1.transitionsDd = action1.transitionsDd * identityDd2; + action2.transitionsDd = action2.transitionsDd * identityDd1; + + // Then combine the extended action DDs. + return combineUnsynchronizedActions(generationInfo, action1, action2); + } + + template <storm::dd::DdType Type, typename ValueType> + typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2) { STORM_LOG_TRACE("Combining unsynchronized actions."); // Make both action DDs write to the same global variables. std::set<storm::expressions::Variable> assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, action1, action2); if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) { - return ActionDecisionDiagram(action1.guardDd + action2.guardDd, action1Extended + action2Extended, assignedGlobalVariables, 0); + return ActionDecisionDiagram(action1.guardDd + action2.guardDd, action1.transitionsDd + action2.transitionsDd, assignedGlobalVariables, 0); } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { if (action1.transitionsDd.isZero()) { - return ActionDecisionDiagram(action2.guardDd, action2Extended, assignedGlobalVariables, action2.numberOfUsedNondeterminismVariables); + return ActionDecisionDiagram(action2.guardDd, action2.transitionsDd, assignedGlobalVariables, action2.numberOfUsedNondeterminismVariables); } else if (action2.transitionsDd.isZero()) { - return ActionDecisionDiagram(action1.guardDd, action1Extended, assignedGlobalVariables, action1.numberOfUsedNondeterminismVariables); + return ActionDecisionDiagram(action1.guardDd, action1.transitionsDd, assignedGlobalVariables, action1.numberOfUsedNondeterminismVariables); } // Bring both choices to the same number of variables that encode the nondeterminism. @@ -827,18 +905,18 @@ namespace storm { for (uint_fast64_t i = action2.numberOfUsedNondeterminismVariables; i < action1.numberOfUsedNondeterminismVariables; ++i) { nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>(); } - action2Extended *= nondeterminismEncoding; + action2.transitionsDd *= nondeterminismEncoding; } else if (action2.numberOfUsedNondeterminismVariables > action1.numberOfUsedNondeterminismVariables) { storm::dd::Add<Type, ValueType> nondeterminismEncoding = generationInfo.manager->template getAddOne<ValueType>(); for (uint_fast64_t i = action1.numberOfUsedNondeterminismVariables; i < action2.numberOfUsedNondeterminismVariables; ++i) { nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>(); } - action1Extended *= nondeterminismEncoding; + action1.transitionsDd *= nondeterminismEncoding; } // Add a new variable that resolves the nondeterminism between the two choices. - storm::dd::Add<Type, ValueType> combinedTransitions = generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[numberOfUsedNondeterminismVariables], 1).ite(action2Extended, action1Extended); + storm::dd::Add<Type, ValueType> combinedTransitions = generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[numberOfUsedNondeterminismVariables], 1).ite(action2.transitionsDd, action1.transitionsDd); return ActionDecisionDiagram((action1.guardDd.toBdd() || action2.guardDd.toBdd()).template toAdd<ValueType>(), combinedTransitions, assignedGlobalVariables, numberOfUsedNondeterminismVariables + 1); } else { diff --git a/src/builder/DdPrismModelBuilder.h b/src/builder/DdPrismModelBuilder.h index 44e9fdb9a..9de5cf2df 100644 --- a/src/builder/DdPrismModelBuilder.h +++ b/src/builder/DdPrismModelBuilder.h @@ -242,6 +242,8 @@ namespace storm { static ActionDecisionDiagram combineSynchronizingActions(GenerationInformation const& generationInfo, ActionDecisionDiagram const& action1, ActionDecisionDiagram const& action2); static ActionDecisionDiagram combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2, storm::dd::Add<Type, ValueType> const& identityDd1, storm::dd::Add<Type, ValueType> const& identityDd2); + + static ActionDecisionDiagram combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2); static ModuleDecisionDiagram createModuleDecisionDiagram(GenerationInformation& generationInfo, storm::prism::Module const& module, std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap); diff --git a/src/storage/prism/HidingComposition.cpp b/src/storage/prism/HidingComposition.cpp index baaa2572f..50afe0338 100644 --- a/src/storage/prism/HidingComposition.cpp +++ b/src/storage/prism/HidingComposition.cpp @@ -17,6 +17,10 @@ namespace storm { return *sub; } + std::set<std::string> const& HidingComposition::getActionsToHide() const { + return actionsToHide; + } + void HidingComposition::writeToStream(std::ostream& stream) const { stream << "(" << *sub << ")" << " " << "{" << boost::join(actionsToHide, ", ") << "}"; } diff --git a/src/storage/prism/HidingComposition.h b/src/storage/prism/HidingComposition.h index 048847fa5..094f00b0e 100644 --- a/src/storage/prism/HidingComposition.h +++ b/src/storage/prism/HidingComposition.h @@ -16,6 +16,8 @@ namespace storm { Composition const& getSubcomposition() const; + std::set<std::string> const& getActionsToHide() const; + protected: virtual void writeToStream(std::ostream& stream) const override; diff --git a/src/storage/prism/RenamingComposition.cpp b/src/storage/prism/RenamingComposition.cpp index ac92dc416..c1d928abb 100644 --- a/src/storage/prism/RenamingComposition.cpp +++ b/src/storage/prism/RenamingComposition.cpp @@ -19,6 +19,10 @@ namespace storm { return *sub; } + std::map<std::string, std::string> const& RenamingComposition::getActionRenaming() const { + return actionRenaming; + } + void RenamingComposition::writeToStream(std::ostream& stream) const { std::vector<std::string> renamings; for (auto const& renaming : actionRenaming) { diff --git a/src/storage/prism/RenamingComposition.h b/src/storage/prism/RenamingComposition.h index c20c1ec5a..117af5727 100644 --- a/src/storage/prism/RenamingComposition.h +++ b/src/storage/prism/RenamingComposition.h @@ -17,6 +17,8 @@ namespace storm { Composition const& getSubcomposition() const; + std::map<std::string, std::string> const& getActionRenaming() const; + protected: virtual void writeToStream(std::ostream& stream) const override;