diff --git a/src/builder/DdJaniModelBuilder.cpp b/src/builder/DdJaniModelBuilder.cpp index 623ad0187..3a46a36c5 100644 --- a/src/builder/DdJaniModelBuilder.cpp +++ b/src/builder/DdJaniModelBuilder.cpp @@ -6,6 +6,15 @@ #include "src/logic/Formulas.h" +#include "src/storage/jani/Model.h" +#include "src/storage/jani/RenameComposition.h" +#include "src/storage/jani/AutomatonComposition.h" +#include "src/storage/jani/ParallelComposition.h" + +#include "src/storage/dd/Add.h" +#include "src/storage/dd/Bdd.h" +#include "src/adapters/AddExpressionAdapter.h" + #include "src/utility/macros.h" #include "src/utility/jani.h" #include "src/exceptions/InvalidArgumentException.h" @@ -119,34 +128,521 @@ namespace storm { this->model = this->model->substituteConstants(); } - /*! - * This structure represents a subcomponent of a composition. - */ + template <storm::dd::DdType Type, typename ValueType> + struct CompositionVariables { + CompositionVariables() : manager(std::make_shared<storm::dd::DdManager<Type>>()) { + // Intentionally left empty. + } + + 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; + + // A mapping from automata to the meta variable encoding their location. + std::map<std::string, storm::expressions::Variable> automatonToLocationVariableMap; + + // The meta variables used to encode the actions. + std::vector<storm::expressions::Variable> actionVariables; + + // The meta variables used to encode the remaining nondeterminism. + std::vector<storm::expressions::Variable> nondeterminismVariables; + + // 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; + + // DDs representing the identity for each automaton. + std::map<std::string, storm::dd::Add<Type, ValueType>> automatonToIdentityMap; + + // DDs representing the valid ranges of the variables of each automaton. + std::map<std::string, storm::dd::Add<Type, ValueType>> automatonToRangeMap; + }; + + // A class responsible for creating the necessary variables for a subsequent composition of automata. + template <storm::dd::DdType Type, typename ValueType> + class CompositionVariableCreator : public storm::jani::CompositionVisitor { + public: + CompositionVariableCreator(storm::jani::Model const& model) : model(model) { + // Intentionally left empty. + } + + CompositionVariables<Type, ValueType> create() { + // First, check whether every automaton appears exactly once in the system composition. + automata.clear(); + this->model.getSystemComposition().accept(*this, boost::none); + STORM_LOG_THROW(automata.size() == this->model.getNumberOfAutomata(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model whose system composition refers to a subset of automata."); + + // Then, check that the model does not contain unbounded integer variables. + STORM_LOG_THROW(!this->model.getGlobalVariables().containsUnboundedIntegerVariables(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model that contains global unbounded integer variables."); + for (auto const& automaton : this->model.getAutomata()) { + STORM_LOG_THROW(!automaton.getVariables().containsUnboundedIntegerVariables(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model that contains unbounded integer variables in automaton '" << automaton.getName() << "'."); + } + + // Based on this assumption, we create the variables. + return createVariables(); + } + + boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) override { + auto it = automata.find(composition.getAutomatonName()); + STORM_LOG_THROW(it != automata.end(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model whose system composition that refers to the same automaton multiple times."); + automata.insert(it, composition.getAutomatonName()); + return boost::none; + } + + boost::any visit(storm::jani::RenameComposition const& composition, boost::any const& data) override { + composition.getSubcomposition().accept(*this, boost::none); + return boost::none; + } + + boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override { + composition.getLeftSubcomposition().accept(*this, boost::none); + composition.getRightSubcomposition().accept(*this, boost::none); + return boost::none; + } + + private: + CompositionVariables<Type, ValueType> createVariables() { + CompositionVariables<Type, ValueType> result; + + for (auto const& action : this->model.getActions()) { + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable(action.getName()); + result.actionVariables.push_back(variablePair.first); + } + + // FIXME: check how many nondeterminism variables we should actually allocate. + uint64_t numberOfNondeterminismVariables = this->model.getNumberOfAutomata(); + for (auto const& automaton : this->model.getAutomata()) { + numberOfNondeterminismVariables *= automaton.getNumberOfEdges(); + } + for (uint_fast64_t i = 0; i < numberOfNondeterminismVariables; ++i) { + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable("nondet" + std::to_string(i)); + result.nondeterminismVariables.push_back(variablePair.first); + } + + // Create global variables. + for (auto const& variable : this->model.getGlobalVariables().getBoundedIntegerVariables()) { + createVariable(variable, result); + } + for (auto const& variable : this->model.getGlobalVariables().getBooleanVariables()) { + createVariable(variable, result); + } + + // Create the variables for the individual automata. + for (auto const& automaton : this->model.getAutomata()) { + storm::dd::Bdd<Type> identity = result.manager->getBddOne(); + storm::dd::Bdd<Type> range = result.manager->getBddOne(); + + // Start by creating a meta variable for the location of the automaton. + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable("l_" + automaton.getName(), 0, automaton.getNumberOfLocations()); + result.automatonToLocationVariableMap[automaton.getName()] = variablePair.first; + storm::dd::Add<Type, ValueType> variableIdentity = result.manager->template getIdentity<ValueType>(variablePair.first).equals(result.manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>() * result.manager->getRange(variablePair.first).template toAdd<ValueType>() * result.manager->getRange(variablePair.second).template toAdd<ValueType>(); + identity &= variableIdentity.toBdd(); + range &= result.manager->getRange(variablePair.first); + + // Then create variables for the variables of the automaton. + for (auto const& variable : automaton.getVariables().getBoundedIntegerVariables()) { + createVariable(variable, result); + identity &= result.variableToIdentityMap.at(variable.getExpressionVariable()).toBdd(); + range &= result.manager->getRange(result.variableToRowMetaVariableMap.at(variable.getExpressionVariable())); + } + for (auto const& variable : automaton.getVariables().getBooleanVariables()) { + createVariable(variable, result); + identity &= result.variableToIdentityMap.at(variable.getExpressionVariable()).toBdd(); + range &= result.manager->getRange(result.variableToRowMetaVariableMap.at(variable.getExpressionVariable())); + } + + result.automatonToIdentityMap[automaton.getName()] = identity.template toAdd<ValueType>(); + result.automatonToRangeMap[automaton.getName()] = range.template toAdd<ValueType>(); + } + + return result; + } + + void createVariable(storm::jani::BoundedIntegerVariable const& variable, CompositionVariables<Type, ValueType>& result) { + int_fast64_t low = variable.getLowerBound().evaluateAsInt(); + int_fast64_t high = variable.getUpperBound().evaluateAsInt(); + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable(variable.getName(), low, high); + + STORM_LOG_TRACE("Created meta variables for global integer variable: " << variablePair.first.getName() << "] and " << variablePair.second.getName() << "."); + + result.rowMetaVariables.insert(variablePair.first); + result.variableToRowMetaVariableMap.emplace(variable.getExpressionVariable(), variablePair.first); + + result.columnMetaVariables.insert(variablePair.second); + result.variableToColumnMetaVariableMap.emplace(variable.getExpressionVariable(), variablePair.second); + + storm::dd::Add<Type, ValueType> variableIdentity = result.manager->template getIdentity<ValueType>(variablePair.first).equals(result.manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>() * result.manager->getRange(variablePair.first).template toAdd<ValueType>() * result.manager->getRange(variablePair.second).template toAdd<ValueType>(); + result.variableToIdentityMap.emplace(variable.getExpressionVariable(), variableIdentity); + result.rowColumnMetaVariablePairs.push_back(variablePair); + + result.allGlobalVariables.insert(variable.getExpressionVariable()); + } + + void createVariable(storm::jani::BooleanVariable const& variable, CompositionVariables<Type, ValueType>& result) { + std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable(variable.getName()); + + STORM_LOG_TRACE("Created meta variables for global boolean variable: " << variablePair.first.getName() << " and " << variablePair.second.getName() << "."); + + result.rowMetaVariables.insert(variablePair.first); + result.variableToRowMetaVariableMap.emplace(variable.getExpressionVariable(), variablePair.first); + + result.columnMetaVariables.insert(variablePair.second); + result.variableToColumnMetaVariableMap.emplace(variable.getExpressionVariable(), variablePair.second); + + storm::dd::Add<Type, ValueType> variableIdentity = result.manager->template getIdentity<ValueType>(variablePair.first).equals(result.manager->template getIdentity<ValueType>(variablePair.second)).template toAdd<ValueType>(); + result.variableToIdentityMap.emplace(variable.getExpressionVariable(), variableIdentity); + + result.rowColumnMetaVariablePairs.push_back(variablePair); + result.allGlobalVariables.insert(variable.getExpressionVariable()); + } + + storm::jani::Model const& model; + std::set<std::string> automata; + }; + + template<storm::dd::DdType Type, typename ValueType> + struct EdgeDestinationDd { + EdgeDestinationDd(storm::dd::Add<Type, ValueType> const& transitionsDd, std::set<storm::expressions::Variable> const& writtenGlobalVariables = {}) : transitionsDd(transitionsDd), writtenGlobalVariables(writtenGlobalVariables) { + // Intentionally left empty. + } + + storm::dd::Add<Type, ValueType> transitionsDd; + std::set<storm::expressions::Variable> writtenGlobalVariables; + }; + + // This structure represents an edge. + template<storm::dd::DdType Type, typename ValueType> + struct EdgeDd { + EdgeDd(storm::dd::Add<Type> const& guardDd = storm::dd::Add<Type>(), storm::dd::Add<Type, ValueType> const& transitionsDd = storm::dd::Add<Type, ValueType>(), std::set<storm::expressions::Variable> const& writtenGlobalVariables = {}, std::set<storm::expressions::Variable> const& globalVariablesWrittenMultipleTimes = {}) { + // Intentionally left empty. + } + + EdgeDd(EdgeDd const& other) : globalVariablesWrittenMultipleTimes(other.globalVariablesWrittenMultipleTimes), writtenGlobalVariables(other.writtenGlobalVariables), guardDd(other.guardDd), transitionsDd(other.transitionsDd) { + // Intentionally left empty. + } + + EdgeDd& operator=(EdgeDd const& other) { + if (this != &other) { + globalVariablesWrittenMultipleTimes = other.globalVariablesWrittenMultipleTimes; + writtenGlobalVariables = other.writtenGlobalVariables; + guardDd = other.guardDd; + transitionsDd = other.transitionsDd; + } + return *this; + } + + std::set<storm::expressions::Variable> globalVariablesWrittenMultipleTimes; + std::set<storm::expressions::Variable> writtenGlobalVariables; + storm::dd::Add<Type, ValueType> guardDd; + storm::dd::Add<Type, ValueType> transitionsDd; + }; + + // This structure represents a subcomponent of a composition. template<storm::dd::DdType Type, typename ValueType> struct AutomatonDd { - + AutomatonDd(storm::dd::Add<Type, ValueType> const& identity) : identity(identity) { + // Intentionally left empty. + } + + std::map<uint64_t, std::vector<EdgeDd<Type, ValueType>>> actionIndexToEdges; + storm::dd::Add<Type, ValueType> identity; }; template <storm::dd::DdType Type, typename ValueType> - std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdJaniModelBuilder<Type, ValueType>::translate() { - auto system = this->model->getSystemComposition().accept(*this, boost::any()); + EdgeDd<Type, ValueType> extendEdgeWithIdentity(EdgeDd<Type, ValueType> const& edge, storm::dd::Add<Type, ValueType> const& identity) { + EdgeDd<Type, ValueType> result(edge); + result.transitionsDd *= identity; + return result; } template <storm::dd::DdType Type, typename ValueType> - boost::any DdJaniModelBuilder<Type, ValueType>::visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) { + EdgeDd<Type, ValueType> composeEdgesInParallel(EdgeDd<Type, ValueType> const& edge1, EdgeDd<Type, ValueType> const& edge2) { + EdgeDd<Type, ValueType> result; + + // Compute the set of variables written multiple times by the composition. + std::set<storm::expressions::Variable> oldVariablesWrittenMultipleTimes; + std::set_union(edge1.globalVariablesWrittenMultipleTimes.begin(), edge1.globalVariablesWrittenMultipleTimes.end(), edge2.globalVariablesWrittenMultipleTimes.begin(), edge2.globalVariablesWrittenMultipleTimes.end(), std::inserter(oldVariablesWrittenMultipleTimes, oldVariablesWrittenMultipleTimes.begin())); + std::set<storm::expressions::Variable> newVariablesWrittenMultipleTimes; + std::set_intersection(edge1.writtenGlobalVariables.begin(), edge1.writtenGlobalVariables.end(), edge2.writtenGlobalVariables.begin(), edge2.writtenGlobalVariables.end(), std::inserter(newVariablesWrittenMultipleTimes, newVariablesWrittenMultipleTimes.begin())); + + std::set<storm::expressions::Variable> variablesWrittenMultipleTimes; + std::set_union(oldVariablesWrittenMultipleTimes.begin(), oldVariablesWrittenMultipleTimes.end(), newVariablesWrittenMultipleTimes.begin(), newVariablesWrittenMultipleTimes.end(), std::inserter(variablesWrittenMultipleTimes, variablesWrittenMultipleTimes.begin())); + + result.globalVariablesWrittenMultipleTimes = std::move(variablesWrittenMultipleTimes); + + // Compute the set of variables written by the composition. + std::set<storm::expressions::Variable> variablesWritten; + std::set_union(edge1.writtenGlobalVariables.begin(), edge1.writtenGlobalVariables.end(), edge2.writtenGlobalVariables.begin(), edge2.writtenGlobalVariables.end(), std::inserter(variablesWritten, variablesWritten.begin())); + + result.writtenGlobalVariables = variablesWritten; + + // Compose the guards. + result.guardDd = edge1.guardDd * edge2.guardDd; + + // Compose the transitions. + result.transitionsDd = edge1.transitionsDd * edge2.transitionsDd; + + return result; } + // A class that is responsible for performing the actual composition. template <storm::dd::DdType Type, typename ValueType> - boost::any DdJaniModelBuilder<Type, ValueType>::visit(storm::jani::RenameComposition const& composition, boost::any const& data) { + class AutomatonComposer : public storm::jani::CompositionVisitor { + public: + AutomatonComposer(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables) : model(model), variables(variables) { + // Intentionally left empty. + } - } + AutomatonDd<Type, ValueType> compose() { + return boost::any_cast<AutomatonDd<Type, ValueType>>(this->model.getSystemComposition().accept(*this, boost::none)); + } + + boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) override { + return buildAutomatonDd(composition.getAutomatonName()); + } + + boost::any visit(storm::jani::RenameComposition const& composition, boost::any const& data) override { + AutomatonDd<Type, ValueType> subautomaton = boost::any_cast<AutomatonDd<Type, ValueType>>(composition.getSubcomposition().accept(*this, boost::none)); + + // Build a mapping from indices to indices for the renaming. + std::map<uint64_t, uint64_t> renamingIndexToIndex; + for (auto const& entry : composition.getRenaming()) { + if (this->model.getActionIndex(entry.first) != this->model.getSilentActionIndex()) { + // Distinguish the cases where we hide the action or properly rename it. + if (entry.second) { + renamingIndexToIndex.emplace(this->model.getActionIndex(entry.first), this->model.getActionIndex(entry.second.get())); + } else { + renamingIndexToIndex.emplace(this->model.getActionIndex(entry.first), this->model.getSilentActionIndex()); + } + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Renaming composition must not rename the silent action."); + } + } + + // Finally, apply the renaming. + AutomatonDd<Type, ValueType> result(subautomaton.identity); + for (auto const& actionEdges : subautomaton.actionIndexToEdges) { + auto it = renamingIndexToIndex.find(actionEdges.first); + if (it != renamingIndexToIndex.end()) { + // If we are to rename the action, do so. + result.actionIndexToEdges[it->second].insert(result.actionIndexToEdges[it->second].end(), actionEdges.second.begin(), actionEdges.second.end()); + } else { + // Otherwise copy over the edges. + result.actionIndexToEdges[it->first].insert(result.actionIndexToEdges[it->first].begin(), actionEdges.second.begin(), actionEdges.second.end()); + } + } + return result; + } + + boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override { + AutomatonDd<Type, ValueType> leftSubautomaton = boost::any_cast<AutomatonDd<Type, ValueType>>(composition.getLeftSubcomposition().accept(*this, boost::none)); + AutomatonDd<Type, ValueType> rightSubautomaton = boost::any_cast<AutomatonDd<Type, ValueType>>(composition.getRightSubcomposition().accept(*this, boost::none)); + + // Build the set of synchronizing action indices. + std::set<uint64_t> synchronizingActionIndices; + for (auto const& entry : composition.getSynchronizationAlphabet()) { + if (this->model.getActionIndex(entry) != this->model.getSilentActionIndex()) { + synchronizingActionIndices.insert(this->model.getActionIndex(entry)); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Parallel composition must not synchronize over silent action."); + } + } + + // Perform the composition. + + // First, consider all actions in the left subcomposition. + AutomatonDd<Type, ValueType> result(leftSubautomaton.identity * rightSubautomaton.identity); + for (auto const& actionEdges : leftSubautomaton.actionIndexToEdges) { + // If we need to synchronize over this action, do so now. + if (synchronizingActionIndices.find(actionEdges.first) != synchronizingActionIndices.end()) { + auto rightIt = rightSubautomaton.actionIndexToEdges.find(actionEdges.first); + if (rightIt != rightSubautomaton.actionIndexToEdges.end()) { + for (auto const& edge1 : actionEdges.second) { + for (auto const& edge2 : rightIt->second) { + result.actionIndexToEdges[actionEdges.first].push_back(composeEdgesInParallel(edge1, edge2)); + } + } + } + } else { + // Extend all edges by the missing identity (unsynchronizing) and copy them over. + for (auto const& edge : actionEdges.second) { + result.actionIndexToEdges[actionEdges.first].push_back(extendEdgeWithIdentity(edge, rightSubautomaton.identity)); + } + } + } + + // Then, consider all actions in the right subcomposition. All synchronizing actions can be ignored, because + // we would have dealt with them earlier if there was a suitable synchronization partner. Since there is none, + // such transitions can not be taken and we can drop them. + for (auto const& actionEdges : rightSubautomaton.actionIndexToEdges) { + if (synchronizingActionIndices.find(actionEdges.first) == synchronizingActionIndices.end()) { + for (auto const& edge : actionEdges.second) { + result.actionIndexToEdges[actionEdges.first].push_back(extendEdgeWithIdentity(edge, leftSubautomaton.identity)); + } + } + } + + return result; + } + + private: + EdgeDestinationDd<Type, ValueType> buildEdgeDestinationDd(storm::jani::Automaton const& automaton, storm::jani::EdgeDestination const& destination, storm::dd::Add<Type, ValueType> const& guard) { + storm::dd::Add<Type, ValueType> transitionsDd = variables.manager->template getAddOne<ValueType>(); + + STORM_LOG_TRACE("Translating edge destination."); + + // Iterate over all assignments (boolean and integer) and build the DD for it. + std::set<storm::expressions::Variable> assignedVariables; + for (auto const& assignment : destination.getAssignments()) { + // Record the variable as being written. + STORM_LOG_TRACE("Assigning to variable " << variables.variableToRowMetaVariableMap.at(assignment.getExpressionVariable()).getName()); + assignedVariables.insert(assignment.getExpressionVariable()); + + // Translate the written variable. + auto const& primedMetaVariable = variables.variableToColumnMetaVariableMap.at(assignment.getExpressionVariable()); + storm::dd::Add<Type, ValueType> writtenVariable = variables.manager->template getIdentity<ValueType>(primedMetaVariable); + + // Translate the expression that is being assigned. + storm::dd::Add<Type, ValueType> updateExpression = variables.rowExpressionAdapter->translateExpression(assignment.getExpressionVariable()); + + // Combine the update expression with the guard. + storm::dd::Add<Type, ValueType> result = updateExpression * guard; + + // Combine the variable and the assigned expression. + result = result.equals(writtenVariable).template toAdd<ValueType>(); + result *= guard; + + // Restrict the transitions to the range of the written variable. + result = result * variables.manager->getRange(primedMetaVariable).template toAdd<ValueType>(); + + transitionsDd *= result; + } + + // Compute the set of assigned global variables. + std::set<storm::expressions::Variable> assignedGlobalVariables; + std::set_intersection(assignedVariables.begin(), assignedVariables.end(), variables.allGlobalVariables.begin(), variables.allGlobalVariables.end(), std::inserter(assignedGlobalVariables, assignedGlobalVariables.begin())); + + // All unassigned boolean variables need to keep their value. + for (storm::jani::BooleanVariable const& variable : automaton.getVariables().getBooleanVariables()) { + if (assignedVariables.find(variable.getExpressionVariable()) == assignedVariables.end()) { + STORM_LOG_TRACE("Multiplying identity of variable " << variable.getName()); + transitionsDd *= variables.variableToIdentityMap.at(variable.getExpressionVariable()); + } + } + + // All unassigned integer variables need to keep their value. + for (storm::jani::BoundedIntegerVariable const& variable : automaton.getVariables().getBoundedIntegerVariables()) { + if (assignedVariables.find(variable.getExpressionVariable()) == assignedVariables.end()) { + STORM_LOG_TRACE("Multiplying identity of variable " << variable.getName()); + transitionsDd *= variables.variableToIdentityMap.at(variable.getExpressionVariable()); + } + } + + return EdgeDestinationDd<Type, ValueType>(transitionsDd * variables.rowExpressionAdapter->translateExpression(destination.getProbability()), assignedGlobalVariables); + } + + /*! + * Builds the DD for the given edge. + */ + EdgeDd<Type, ValueType> buildEdgeDd(storm::jani::Automaton const& automaton, storm::jani::Edge const& edge) { + STORM_LOG_TRACE("Translating guard " << edge.getGuard()); + storm::dd::Add<Type, ValueType> guard = variables.rowExpressionAdapter->translateExpression(edge.getGuard()) * variables.automatonToRangeMap.at(automaton.getName()); + STORM_LOG_WARN_COND(!guard.isZero(), "The guard '" << edge.getGuard() << "' is unsatisfiable."); + + if (!guard.isZero()) { + // Create the DDs representing the individual updates. + std::vector<EdgeDestinationDd<Type, ValueType>> destinationDds; + for (storm::jani::EdgeDestination const& destination : edge.getDestinations()) { + destinationDds.push_back(buildEdgeDestinationDd(automaton, destination, guard)); + + STORM_LOG_WARN_COND(!destinationDds.back().transitionsDd.isZero(), "Destination does not have any effect."); + } + + // Start by gathering all variables that were written in at least one update. + std::set<storm::expressions::Variable> globalVariablesInSomeUpdate; + + // If the edge is not labeled with the silent action, we have to analyze which portion of the global + // variables was written by any of the updates and make all update results equal w.r.t. this set. If + // the edge is labeled with the silent action, we can already multiply the identities of all global variables. + if (edge.getActionId() != this->model.getSilentActionIndex()) { + std::for_each(destinationDds.begin(), destinationDds.end(), [&globalVariablesInSomeUpdate] (EdgeDestinationDd<Type, ValueType> const& edgeDestinationDd) { globalVariablesInSomeUpdate.insert(edgeDestinationDd.writtenGlobalVariables.begin(), edgeDestinationDd.writtenGlobalVariables.end()); } ); + } else { + globalVariablesInSomeUpdate = variables.allGlobalVariables; + } + + // Then, multiply the missing identities. + for (auto& destinationDd : destinationDds) { + std::set<storm::expressions::Variable> missingIdentities; + std::set_difference(globalVariablesInSomeUpdate.begin(), globalVariablesInSomeUpdate.end(), destinationDd.writtenGlobalVariables.begin(), destinationDd.writtenGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); + + for (auto const& variable : missingIdentities) { + STORM_LOG_TRACE("Multiplying identity for variable " << variable.getName() << " to destination DD."); + destinationDd.transitionsDd *= variables.variableToIdentityMap.at(variable); + } + } + + // Now combine the destination DDs to the edge DD. + storm::dd::Add<Type, ValueType> transitionsDd = variables.manager->template getAddZero<ValueType>(); + for (auto const& destinationDd : destinationDds) { + transitionsDd += destinationDd.transitionsDd; + } + + return EdgeDd<Type, ValueType>(guard, guard * transitionsDd, globalVariablesInSomeUpdate); + } else { + return EdgeDd<Type, ValueType>(variables.manager->template getAddZero<ValueType>(), variables.manager->template getAddZero<ValueType>()); + } + } + + /*! + * Builds the DD for the automaton with the given name. + */ + AutomatonDd<Type, ValueType> buildAutomatonDd(std::string const& automatonName) { + AutomatonDd<Type, ValueType> result(variables.automatonToIdentityMap.at(automatonName)); + + storm::jani::Automaton const& automaton = this->model.getAutomaton(automatonName); + for (auto const& edge : automaton.getEdges()) { + // Build the edge and add it if it adds transitions. + EdgeDd<Type, ValueType> edgeDd = buildEdgeDd(automaton, edge); + if (!edgeDd.guardDd.isZero()) { + result.actionIndexToEdges[edge.getActionId()].push_back(edgeDd); + } + } + + return result; + } + + // The model that is referred to by the composition. + storm::jani::Model const& model; + + // The variable to use when building an automaton. + CompositionVariables<Type, ValueType> const& variables; + }; template <storm::dd::DdType Type, typename ValueType> - boost::any DdJaniModelBuilder<Type, ValueType>::visit(storm::jani::ParallelComposition const& composition, boost::any const& data) { + std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdJaniModelBuilder<Type, ValueType>::translate() { + CompositionVariableCreator<Type, ValueType> variableCreator(*this->model); + CompositionVariables<Type, ValueType> variables = variableCreator.create(); + + AutomatonComposer<Type, ValueType> composer(*this->model, variables); + AutomatonDd<Type, ValueType> system = composer.compose(); + // FIXME + return nullptr; } - template class DdJaniModelBuilder<storm::dd::DdType::CUDD, double>; template class DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>; diff --git a/src/builder/DdJaniModelBuilder.h b/src/builder/DdJaniModelBuilder.h index a3f07a21c..a90f911b5 100644 --- a/src/builder/DdJaniModelBuilder.h +++ b/src/builder/DdJaniModelBuilder.h @@ -19,7 +19,7 @@ namespace storm { namespace builder { template <storm::dd::DdType Type, typename ValueType = double> - class DdJaniModelBuilder : public storm::jani::CompositionVisitor { + class DdJaniModelBuilder { public: struct Options { /*! @@ -108,11 +108,7 @@ namespace storm { * @return The translated model. */ storm::jani::Model const& getTranslatedModel() const; - - virtual boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) override; - virtual boost::any visit(storm::jani::RenameComposition const& composition, boost::any const& data) override; - virtual boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override; - + private: /// The model to translate. boost::optional<storm::jani::Model> model; diff --git a/src/storage/jani/Automaton.cpp b/src/storage/jani/Automaton.cpp index 79f658356..ccdec51aa 100644 --- a/src/storage/jani/Automaton.cpp +++ b/src/storage/jani/Automaton.cpp @@ -8,133 +8,42 @@ namespace storm { namespace jani { namespace detail { - EdgeIterator::EdgeIterator(Automaton& automaton, outer_iter out_it, outer_iter out_ite, inner_iter in_it) : automaton(automaton), out_it(out_it), out_ite(out_ite), in_it(in_it) { + Edges::Edges(iterator it, iterator ite) : it(it), ite(ite) { // Intentionally left empty. } - EdgeIterator& EdgeIterator::operator++() { - incrementIterator(); - return *this; + Edges::iterator Edges::begin() const { + return it; } - EdgeIterator& EdgeIterator::operator++(int) { - incrementIterator(); - return *this; + Edges::iterator Edges::end() const { + return ite; } - Edge& EdgeIterator::operator*() { - return *in_it; + bool Edges::empty() const { + return it == ite; } - bool EdgeIterator::operator==(EdgeIterator const& other) const { - return this->out_it == other.out_it && this->in_it == other.in_it; - } - - bool EdgeIterator::operator!=(EdgeIterator const& other) const { - return !(*this == other); - } - - void EdgeIterator::incrementIterator() { - ++in_it; - - // If the inner iterator has reached its end move it to the beginning of the next outer element. - if (in_it == out_it->end()) { - ++out_it; - while (out_it != out_ite && out_it->empty()) { - ++out_it; - in_it = out_it->end(); - } - if (out_it != out_ite) { - in_it = out_it->begin(); - } - } - } - - ConstEdgeIterator::ConstEdgeIterator(Automaton const& automaton, outer_iter out_it, outer_iter out_ite, inner_iter in_it) : automaton(automaton), out_it(out_it), out_ite(out_ite), in_it(in_it) { - // Intentionally left empty. - } - - ConstEdgeIterator& ConstEdgeIterator::operator++() { - incrementIterator(); - return *this; - } - - ConstEdgeIterator& ConstEdgeIterator::operator++(int) { - incrementIterator(); - return *this; - } - - Edge const& ConstEdgeIterator::operator*() const { - return *in_it; - } - - bool ConstEdgeIterator::operator==(ConstEdgeIterator const& other) const { - return this->out_it == other.out_it && this->in_it == other.in_it; - } - - bool ConstEdgeIterator::operator!=(ConstEdgeIterator const& other) const { - return !(*this == other); - } - - void ConstEdgeIterator::incrementIterator() { - ++in_it; - - // If the inner iterator has reached its end move it to the beginning of the next outer element. - if (in_it == out_it->end()) { - ++out_it; - while (out_it != out_ite && out_it->empty()) { - ++out_it; - in_it = out_it->end(); - } - if (out_it != out_ite) { - in_it = out_it->begin(); - } - } - } - - Edges::Edges(Automaton& automaton) : automaton(automaton) { + ConstEdges::ConstEdges(const_iterator it, const_iterator ite) : it(it), ite(ite) { // Intentionally left empty. } - EdgeIterator Edges::begin() { - auto outer = automaton.edges.begin(); - while (outer != automaton.edges.end() && outer->empty()) { - ++outer; - } - if (outer == automaton.edges.end()) { - return end(); - } else { - return EdgeIterator(automaton, outer, automaton.edges.end(), outer->begin()); - } - } - - EdgeIterator Edges::end() { - return EdgeIterator(automaton, automaton.edges.end(), automaton.edges.end(), automaton.edges.back().end()); - } - - ConstEdges::ConstEdges(Automaton const& automaton) : automaton(automaton) { - // Intentionally left empty. + ConstEdges::const_iterator ConstEdges::begin() const { + return it; } - ConstEdgeIterator ConstEdges::begin() const { - auto outer = automaton.edges.begin(); - while (outer != automaton.edges.end() && outer->empty()) { - ++outer; - } - if (outer == automaton.edges.end()) { - return end(); - } else { - return ConstEdgeIterator(automaton, outer, automaton.edges.end(), outer->begin()); - } + ConstEdges::const_iterator ConstEdges::end() const { + return ite; } - - ConstEdgeIterator ConstEdges::end() const { - return ConstEdgeIterator(automaton, automaton.edges.end(), automaton.edges.end(), automaton.edges.back().end()); + + bool ConstEdges::empty() const { + return it == ite; } } Automaton::Automaton(std::string const& name) : name(name) { - // Intentionally left empty. + // Add a sentinel element to the mapping from locations to starting indices. + locationToStartingIndex.push_back(0); } std::string const& Automaton::getName() const { @@ -177,7 +86,7 @@ namespace storm { STORM_LOG_THROW(!this->hasLocation(location.getName()), storm::exceptions::WrongFormatException, "Cannot add location with name '" << location.getName() << "', because a location with this name already exists."); locationToIndex.emplace(location.getName(), locations.size()); locations.push_back(location); - edges.push_back(EdgeSet()); + locationToStartingIndex.push_back(edges.size()); return locations.size() - 1; } @@ -204,31 +113,62 @@ namespace storm { uint64_t Automaton::getInitialLocationIndex() const { return initialLocationIndex; } + + Automaton::Edges Automaton::getEdgesFromLocation(std::string const& name) { + auto it = locationToIndex.find(name); + STORM_LOG_THROW(it != locationToIndex.end(), storm::exceptions::InvalidArgumentException, "Cannot retrieve edges from unknown location '" << name << "."); + return getEdgesFromLocation(it->second); + } + + Automaton::Edges Automaton::getEdgesFromLocation(uint64_t index) { + auto it = edges.begin(); + std::advance(it, locationToStartingIndex[index]); + auto ite = edges.begin(); + std::advance(ite, locationToStartingIndex[index + 1]); + return Edges(it, ite); + } - EdgeSet const& Automaton::getEdgesFromLocation(std::string const& name) const { + Automaton::ConstEdges Automaton::getEdgesFromLocation(std::string const& name) const { auto it = locationToIndex.find(name); STORM_LOG_THROW(it != locationToIndex.end(), storm::exceptions::InvalidArgumentException, "Cannot retrieve edges from unknown location '" << name << "."); return getEdgesFromLocation(it->second); } - EdgeSet const& Automaton::getEdgesFromLocation(uint64_t index) const { - return edges[index]; + Automaton::ConstEdges Automaton::getEdgesFromLocation(uint64_t index) const { + auto it = edges.begin(); + std::advance(it, locationToStartingIndex[index]); + auto ite = edges.begin(); + std::advance(ite, locationToStartingIndex[index + 1]); + return ConstEdges(it, ite); } void Automaton::addEdge(Edge const& edge) { STORM_LOG_THROW(edge.getSourceLocationId() < locations.size(), storm::exceptions::InvalidArgumentException, "Cannot add edge with unknown source location index '" << edge.getSourceLocationId() << "'."); - edges[edge.getSourceLocationId()].addEdge(edge); + + // Find the right position for the edge and insert it properly. + auto posIt = edges.begin(); + std::advance(posIt, locationToStartingIndex[edge.getSourceLocationId() + 1]); + edges.insert(posIt, edge); + + // Now update the starting indices of all subsequent locations. + for (uint64_t locationIndex = edge.getSourceLocationId() + 1; locationIndex < locationToStartingIndex.size(); ++locationIndex) { + ++locationToStartingIndex[locationIndex]; + } } - Automaton::Edges Automaton::getEdges() { - return Edges(*this); + std::vector<Edge>& Automaton::getEdges() { + return edges; } - Automaton::ConstEdges Automaton::getEdges() const { - return ConstEdges(*this); + std::vector<Edge> const& Automaton::getEdges() const { + return edges; } uint64_t Automaton::getNumberOfLocations() const { + return locations.size(); + } + + uint64_t Automaton::getNumberOfEdges() const { return edges.size(); } diff --git a/src/storage/jani/Automaton.h b/src/storage/jani/Automaton.h index 36a525703..08b803248 100644 --- a/src/storage/jani/Automaton.h +++ b/src/storage/jani/Automaton.h @@ -5,7 +5,7 @@ #include <unordered_map> #include "src/storage/jani/VariableSet.h" -#include "src/storage/jani/EdgeSet.h" +#include "src/storage/jani/Edge.h" #include "src/storage/jani/Location.h" namespace storm { @@ -14,108 +14,58 @@ namespace storm { class Automaton; namespace detail { - class EdgeIterator { - private: - typedef std::vector<EdgeSet>::iterator outer_iter; - typedef EdgeSet::iterator inner_iter; - + class Edges { public: - /*! - * Creates an iterator over all edges. - */ - EdgeIterator(Automaton& automaton, outer_iter out_it, outer_iter out_ite, inner_iter in_it); - - // Methods to advance the iterator. - EdgeIterator& operator++(); - EdgeIterator& operator++(int); - - Edge& operator*(); - - bool operator==(EdgeIterator const& other) const; - bool operator!=(EdgeIterator const& other) const; - - private: - // Moves the iterator to the next position. - void incrementIterator(); - - // The underlying automaton. - Automaton& automaton; + typedef std::vector<Edge>::iterator iterator; + typedef std::vector<Edge>::const_iterator const_iterator; - // The current iterator positions. - outer_iter out_it; - outer_iter out_ite; - inner_iter in_it; - }; - - class ConstEdgeIterator { - private: - typedef std::vector<EdgeSet>::const_iterator outer_iter; - typedef EdgeSet::const_iterator inner_iter; + Edges(iterator it, iterator ite); - public: /*! - * Creates an iterator over all edges. + * Retrieves an iterator to the edges. */ - ConstEdgeIterator(Automaton const& automaton, outer_iter out_it, outer_iter out_ite, inner_iter in_it); - - // Methods to advance the iterator. - ConstEdgeIterator& operator++(); - ConstEdgeIterator& operator++(int); - - Edge const& operator*() const; - - bool operator==(ConstEdgeIterator const& other) const; - bool operator!=(ConstEdgeIterator const& other) const; - - private: - // Moves the iterator to the next position. - void incrementIterator(); - - // The underlying automaton. - Automaton const& automaton; - - // The current iterator positions. - outer_iter out_it; - outer_iter out_ite; - inner_iter in_it; - }; - - class Edges { - public: - Edges(Automaton& automaton); + iterator begin() const; /*! - * Retrieves an iterator to all edges of the automaton. + * Retrieves an end iterator to the edges. */ - EdgeIterator begin(); + iterator end() const; /*! - * Retrieves the end iterator to edges of the automaton. + * Determines whether this set of edges is empty. */ - EdgeIterator end(); + bool empty() const; private: - // The underlying automaton. - Automaton& automaton; + iterator it; + iterator ite; }; class ConstEdges { public: - ConstEdges(Automaton const& automaton); + typedef std::vector<Edge>::iterator iterator; + typedef std::vector<Edge>::const_iterator const_iterator; + + ConstEdges(const_iterator it, const_iterator ite); /*! - * Retrieves an iterator to all edges of the automaton. + * Retrieves an iterator to the edges. */ - ConstEdgeIterator begin() const; + const_iterator begin() const; /*! - * Retrieves the end iterator to edges of the automaton. + * Retrieves an end iterator to the edges. */ - ConstEdgeIterator end() const; + const_iterator end() const; + + /*! + * Determines whether this set of edges is empty. + */ + bool empty() const; private: - // The underlying automaton. - Automaton const& automaton; + const_iterator it; + const_iterator ite; }; } @@ -209,16 +159,26 @@ namespace storm { * Retrieves the index of the initial location. */ uint64_t getInitialLocationIndex() const; + + /*! + * Retrieves the edges of the location with the given name. + */ + Edges getEdgesFromLocation(std::string const& name); + + /*! + * Retrieves the edges of the location with the given index. + */ + Edges getEdgesFromLocation(uint64_t index); /*! * Retrieves the edges of the location with the given name. */ - EdgeSet const& getEdgesFromLocation(std::string const& name) const; + ConstEdges getEdgesFromLocation(std::string const& name) const; /*! * Retrieves the edges of the location with the given index. */ - EdgeSet const& getEdgesFromLocation(uint64_t index) const; + ConstEdges getEdgesFromLocation(uint64_t index) const; /*! * Adds an edge to the automaton. @@ -228,35 +188,44 @@ namespace storm { /*! * Retrieves the edges of the automaton. */ - Edges getEdges(); + std::vector<Edge>& getEdges(); /*! * Retrieves the edges of the automaton. */ - ConstEdges getEdges() const; + std::vector<Edge> const& getEdges() const; /*! * Retrieves the number of locations. */ uint64_t getNumberOfLocations() const; - + + /*! + * Retrieves the number of edges. + */ + uint64_t getNumberOfEdges() const; + private: - // The name of the automaton. + /// The name of the automaton. std::string name; - // The set of variables of this automaton. + /// The set of variables of this automaton. VariableSet variables; - // The locations of the automaton. + /// The locations of the automaton. std::vector<Location> locations; - // A mapping of location names to their indices. + /// A mapping of location names to their indices. std::unordered_map<std::string, uint64_t> locationToIndex; - // All edges of the automaton. The edges at index i are the edges of the location with index i. - std::vector<EdgeSet> edges; + /// All edges of the automaton + std::vector<Edge> edges; + + /// A mapping from location indices to the starting indices. If l is mapped to i, it means that the edges + /// leaving location l start at index i of the edges vector. + std::vector<uint64_t> locationToStartingIndex; - // The index of the initial location. + /// The index of the initial location. uint64_t initialLocationIndex; }; diff --git a/src/storage/jani/EdgeSet.cpp b/src/storage/jani/EdgeSet.cpp deleted file mode 100644 index d71b3f5be..000000000 --- a/src/storage/jani/EdgeSet.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "src/storage/jani/EdgeSet.h" - -namespace storm { - namespace jani { - - EdgeSet::EdgeSet(std::vector<Edge> const& edges) : edges(edges) { - // Intentionally left empty. - } - - EdgeSet::iterator EdgeSet::begin() { - return edges.begin(); - } - - EdgeSet::iterator EdgeSet::end() { - return edges.end(); - } - - EdgeSet::const_iterator EdgeSet::begin() const { - return edges.cbegin(); - } - - EdgeSet::const_iterator EdgeSet::end() const { - return edges.cend(); - } - - void EdgeSet::addEdge(Edge const& edge) { - edges.push_back(edge); - } - - bool EdgeSet::empty() const { - return edges.empty(); - } - - } -} \ No newline at end of file diff --git a/src/storage/jani/EdgeSet.h b/src/storage/jani/EdgeSet.h deleted file mode 100644 index a9e7336ad..000000000 --- a/src/storage/jani/EdgeSet.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "src/storage/jani/Edge.h" - -namespace storm { - namespace jani { - - class EdgeSet { - public: - typedef std::vector<Edge> container_type; - typedef container_type::iterator iterator; - typedef container_type::const_iterator const_iterator; - - /*! - * Creates a new set of edges. - */ - EdgeSet(std::vector<Edge> const& edges = std::vector<Edge>()); - - /*! - * Adds the edge to the edges. - */ - void addEdge(Edge const& edge); - - /*! - * Retrieves whether the set of edges is empty. - */ - bool empty() const; - - // Methods to get an iterator to the edges. - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - - private: - // The set of edges. - container_type edges; - }; - - } -} \ No newline at end of file diff --git a/src/storage/jani/Model.cpp b/src/storage/jani/Model.cpp index ec0066ed4..1d976d084 100644 --- a/src/storage/jani/Model.cpp +++ b/src/storage/jani/Model.cpp @@ -65,6 +65,10 @@ namespace storm { return it->second; } + std::vector<Action> const& Model::getActions() const { + return actions; + } + uint64_t Model::addConstant(Constant const& constant) { auto it = constantToIndex.find(constant.getName()); STORM_LOG_THROW(it == constantToIndex.end(), storm::exceptions::WrongFormatException, "Cannot add constant with name '" << constant.getName() << "', because a constant with that name already exists."); @@ -147,6 +151,10 @@ namespace storm { return automata[it->second]; } + std::size_t Model::getNumberOfAutomata() const { + return automata.size(); + } + std::shared_ptr<Composition> Model::getStandardSystemComposition() const { std::set<std::string> fullSynchronizationAlphabet = getActionNames(false); @@ -249,6 +257,7 @@ namespace storm { // Substitute constants in all global variables. for (auto& variable : result.getGlobalVariables()) { + std::cout << "init: " << variable.getInitialValue() << std::endl; variable.setInitialValue(variable.getInitialValue().substitute(constantSubstitution)); } for (auto& variable : result.getGlobalVariables().getBoundedIntegerVariables()) { diff --git a/src/storage/jani/Model.h b/src/storage/jani/Model.h index e719f6d96..258f9417d 100644 --- a/src/storage/jani/Model.h +++ b/src/storage/jani/Model.h @@ -72,6 +72,11 @@ namespace storm { */ Action const& getAction(uint64_t index) const; + /*! + * Retrieves the actions of the model. + */ + std::vector<Action> const& getActions() const; + /*! * Adds the given constant to the model. */ @@ -157,6 +162,11 @@ namespace storm { */ Automaton const& getAutomaton(std::string const& name) const; + /*! + * Retrieves the number of automata in this model. + */ + std::size_t getNumberOfAutomata() const; + /*! * Sets the system composition expression of the JANI model. */ diff --git a/src/storage/jani/RenameComposition.cpp b/src/storage/jani/RenameComposition.cpp index a88139fa7..77ab94f5c 100644 --- a/src/storage/jani/RenameComposition.cpp +++ b/src/storage/jani/RenameComposition.cpp @@ -20,6 +20,10 @@ namespace storm { return visitor.visit(*this, data); } + std::map<std::string, boost::optional<std::string>> const& RenameComposition::getRenaming() const { + return renaming; + } + void RenameComposition::write(std::ostream& stream) const { std::vector<std::string> renamingStrings; diff --git a/src/storage/jani/VariableSet.cpp b/src/storage/jani/VariableSet.cpp index e4d37957d..97697e2f7 100644 --- a/src/storage/jani/VariableSet.cpp +++ b/src/storage/jani/VariableSet.cpp @@ -9,190 +9,96 @@ namespace storm { namespace detail { - VariableSetIterator::VariableSetIterator(VariableSet& variableSet, boost::variant<bool_iter, bint_iter, int_iter> initialIterator) : variableSet(variableSet), it(initialIterator) { - // Intentionally left empty. - } - - VariableSetIterator& VariableSetIterator::operator++() { - incrementIterator(); - return *this; - } - - VariableSetIterator& VariableSetIterator::operator++(int) { - incrementIterator(); - return *this; - } - - Variable& VariableSetIterator::operator*() { - if (it.which() == 0) { - return *boost::get<bool_iter>(it); - } else if (it.which() == 1) { - return *boost::get<bint_iter>(it); - } else { - return *boost::get<int_iter>(it); - } - } - - bool VariableSetIterator::operator==(VariableSetIterator const& other) const { - return this->it == other.it; + template<typename VariableType> + VariableType& Dereferencer<VariableType>::operator()(std::shared_ptr<VariableType> const& d) { + return *d; } - bool VariableSetIterator::operator!=(VariableSetIterator const& other) const { - return this->it != other.it; - } - - void VariableSetIterator::incrementIterator() { - if (it.which() == 0) { - bool_iter& tmp = boost::get<bool_iter>(it); - if (tmp != variableSet.getBooleanVariables().end()) { - ++tmp; - } else { - it = variableSet.getBoundedIntegerVariables().begin(); - } - } else if (it.which() == 1) { - bint_iter& tmp = boost::get<bint_iter>(it); - if (tmp != variableSet.getBoundedIntegerVariables().end()) { - ++tmp; - } else { - it = variableSet.getUnboundedIntegerVariables().begin(); - } - } else { - ++boost::get<int_iter>(it); - } - } - - ConstVariableSetIterator::ConstVariableSetIterator(VariableSet const& variableSet, boost::variant<bool_iter, bint_iter, int_iter> initialIterator) : variableSet(variableSet), it(initialIterator) { + template<typename VariableType> + Variables<VariableType>::Variables(input_iterator it, input_iterator ite) : it(it), ite(ite) { // Intentionally left empty. } - - ConstVariableSetIterator& ConstVariableSetIterator::operator++() { - incrementIterator(); - return *this; - } - - ConstVariableSetIterator& ConstVariableSetIterator::operator++(int) { - incrementIterator(); - return *this; - } - - Variable const& ConstVariableSetIterator::operator*() { - if (it.which() == 0) { - return *boost::get<bool_iter>(it); - } else if (it.which() == 1) { - return *boost::get<bint_iter>(it); - } else { - return *boost::get<int_iter>(it); - } + + template<typename VariableType> + typename Variables<VariableType>::iterator Variables<VariableType>::begin() { + return boost::make_transform_iterator(it, Dereferencer<VariableType>()); } - bool ConstVariableSetIterator::operator==(ConstVariableSetIterator const& other) const { - return this->it == other.it; + template<typename VariableType> + typename Variables<VariableType>::iterator Variables<VariableType>::end() { + return boost::make_transform_iterator(ite, Dereferencer<VariableType>()); } - - bool ConstVariableSetIterator::operator!=(ConstVariableSetIterator const& other) const { - return this->it != other.it; - } - - void ConstVariableSetIterator::incrementIterator() { - if (it.which() == 0) { - bool_iter& tmp = boost::get<bool_iter>(it); - if (tmp != variableSet.getBooleanVariables().end()) { - ++tmp; - } else { - it = variableSet.getBoundedIntegerVariables().begin(); - } - } else if (it.which() == 1) { - bint_iter& tmp = boost::get<bint_iter>(it); - if (tmp != variableSet.getBoundedIntegerVariables().end()) { - ++tmp; - } else { - it = variableSet.getUnboundedIntegerVariables().begin(); - } - } else { - ++boost::get<int_iter>(it); - } - } - - IntegerVariables::IntegerVariables(VariableSet& variableSet) : variableSet(variableSet) { - // Intentionally left empty. - } - - VariableSetIterator IntegerVariables::begin() { - return VariableSetIterator(variableSet, variableSet.getBoundedIntegerVariables().begin()); - } - - VariableSetIterator IntegerVariables::end() { - return VariableSetIterator(variableSet, variableSet.getUnboundedIntegerVariables().end()); - } - - ConstIntegerVariables::ConstIntegerVariables(VariableSet const& variableSet) : variableSet(variableSet) { + + template<typename VariableType> + ConstVariables<VariableType>::ConstVariables(const_input_iterator it, const_input_iterator ite) : it(it), ite(ite) { // Intentionally left empty. } - ConstVariableSetIterator ConstIntegerVariables::begin() const { - return ConstVariableSetIterator(variableSet, variableSet.getBoundedIntegerVariables().begin()); + template<typename VariableType> + typename ConstVariables<VariableType>::const_iterator ConstVariables<VariableType>::begin() { + return boost::make_transform_iterator(it, Dereferencer<VariableType>()); } - ConstVariableSetIterator ConstIntegerVariables::end() const { - return ConstVariableSetIterator(variableSet, variableSet.getUnboundedIntegerVariables().end()); + template<typename VariableType> + typename ConstVariables<VariableType>::const_iterator ConstVariables<VariableType>::end() { + return boost::make_transform_iterator(ite, Dereferencer<VariableType>()); } + } VariableSet::VariableSet() { // Intentionally left empty. } - std::vector<BooleanVariable>& VariableSet::getBooleanVariables() { - return booleanVariables; + detail::Variables<BooleanVariable> VariableSet::getBooleanVariables() { + return detail::Variables<BooleanVariable>(booleanVariables.begin(), booleanVariables.end()); } - std::vector<BooleanVariable> const& VariableSet::getBooleanVariables() const { - return booleanVariables; - } - - std::vector<BoundedIntegerVariable>& VariableSet::getBoundedIntegerVariables() { - return boundedIntegerVariables; + detail::ConstVariables<BooleanVariable> VariableSet::getBooleanVariables() const { + return detail::ConstVariables<BooleanVariable>(booleanVariables.begin(), booleanVariables.end()); } - std::vector<BoundedIntegerVariable> const& VariableSet::getBoundedIntegerVariables() const { - return boundedIntegerVariables; + detail::Variables<BoundedIntegerVariable> VariableSet::getBoundedIntegerVariables() { + return detail::Variables<BoundedIntegerVariable>(boundedIntegerVariables.begin(), boundedIntegerVariables.end()); } - std::vector<UnboundedIntegerVariable>& VariableSet::getUnboundedIntegerVariables() { - return unboundedIntegerVariables; + detail::ConstVariables<BoundedIntegerVariable> VariableSet::getBoundedIntegerVariables() const { + return detail::ConstVariables<BoundedIntegerVariable>(boundedIntegerVariables.begin(), boundedIntegerVariables.end()); } - std::vector<UnboundedIntegerVariable> const& VariableSet::getUnboundedIntegerVariables() const { - return unboundedIntegerVariables; + detail::Variables<UnboundedIntegerVariable> VariableSet::getUnboundedIntegerVariables() { + return detail::Variables<UnboundedIntegerVariable>(unboundedIntegerVariables.begin(), unboundedIntegerVariables.end()); } - VariableSet::IntegerVariables VariableSet::getIntegerVariables() { - return IntegerVariables(*this); + detail::ConstVariables<UnboundedIntegerVariable> VariableSet::getUnboundedIntegerVariables() const { + return detail::ConstVariables<UnboundedIntegerVariable>(unboundedIntegerVariables.begin(), unboundedIntegerVariables.end()); } - VariableSet::ConstIntegerVariables VariableSet::getIntegerVariables() const { - return ConstIntegerVariables(*this); - } - void VariableSet::addBooleanVariable(BooleanVariable const& variable) { STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); - booleanVariables.push_back(variable); + std::shared_ptr<BooleanVariable> newVariable = std::make_shared<BooleanVariable>(variable); + variables.push_back(newVariable); + booleanVariables.push_back(newVariable); nameToVariable.emplace(variable.getName(), variable.getExpressionVariable()); - variableToVariable.emplace(variable.getExpressionVariable(), std::make_pair(0, booleanVariables.size() - 1)); + variableToVariable.emplace(variable.getExpressionVariable(), newVariable); } void VariableSet::addBoundedIntegerVariable(BoundedIntegerVariable const& variable) { STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); - boundedIntegerVariables.push_back(variable); + std::shared_ptr<BoundedIntegerVariable> newVariable = std::make_shared<BoundedIntegerVariable>(variable); + variables.push_back(newVariable); + boundedIntegerVariables.push_back(newVariable); nameToVariable.emplace(variable.getName(), variable.getExpressionVariable()); - variableToVariable.emplace(variable.getExpressionVariable(), std::make_pair(1, boundedIntegerVariables.size() - 1)); + variableToVariable.emplace(variable.getExpressionVariable(), newVariable); } void VariableSet::addUnboundedIntegerVariable(UnboundedIntegerVariable const& variable) { STORM_LOG_THROW(!this->hasVariable(variable.getName()), storm::exceptions::WrongFormatException, "Cannot add variable with name '" << variable.getName() << "', because a variable with that name already exists."); - unboundedIntegerVariables.push_back(variable); + std::shared_ptr<UnboundedIntegerVariable> newVariable = std::make_shared<UnboundedIntegerVariable>(variable); + variables.push_back(newVariable); + unboundedIntegerVariables.push_back(newVariable); nameToVariable.emplace(variable.getName(), variable.getExpressionVariable()); - variableToVariable.emplace(variable.getExpressionVariable(), std::make_pair(2, boundedIntegerVariables.size() - 1)); + variableToVariable.emplace(variable.getExpressionVariable(), newVariable); } bool VariableSet::hasVariable(std::string const& name) const { @@ -206,36 +112,42 @@ namespace storm { } VariableSet::iterator VariableSet::begin() { - return iterator(*this, booleanVariables.begin()); + return boost::make_transform_iterator(variables.begin(), detail::Dereferencer<Variable>()); } VariableSet::const_iterator VariableSet::begin() const { - return const_iterator(*this, booleanVariables.begin()); + return boost::make_transform_iterator(variables.begin(), detail::Dereferencer<Variable const>()); } VariableSet::iterator VariableSet::end() { - return iterator(*this, unboundedIntegerVariables.end()); + return boost::make_transform_iterator(variables.end(), detail::Dereferencer<Variable>()); } VariableSet::const_iterator VariableSet::end() const { - return const_iterator(*this, unboundedIntegerVariables.end()); + return boost::make_transform_iterator(variables.end(), detail::Dereferencer<Variable const>()); } Variable const& VariableSet::getVariable(storm::expressions::Variable const& variable) const { auto it = variableToVariable.find(variable); STORM_LOG_THROW(it != variableToVariable.end(), storm::exceptions::InvalidArgumentException, "Unable to retrieve unknown variable '" << variable.getName() << "'."); - - if (it->second.first == 0) { - return booleanVariables[it->second.second]; - } else if (it->second.first == 1) { - return boundedIntegerVariables[it->second.second]; - } else { - return unboundedIntegerVariables[it->second.second]; - } + return *it->second; } bool VariableSet::hasVariable(storm::expressions::Variable const& variable) const { return variableToVariable.find(variable) != variableToVariable.end(); } + + bool VariableSet::containsBooleanVariable() const { + return !booleanVariables.empty(); + } + + bool VariableSet::containsBoundedIntegerVariable() const { + return !boundedIntegerVariables.empty(); + } + + bool VariableSet::containsUnboundedIntegerVariables() const { + return !unboundedIntegerVariables.empty(); + } + } } \ No newline at end of file diff --git a/src/storage/jani/VariableSet.h b/src/storage/jani/VariableSet.h index 8c8ec6815..058e56457 100644 --- a/src/storage/jani/VariableSet.h +++ b/src/storage/jani/VariableSet.h @@ -3,7 +3,7 @@ #include <vector> #include <set> -#include <boost/variant.hpp> +#include <boost/iterator/transform_iterator.hpp> #include "src/storage/jani/BooleanVariable.h" #include "src/storage/jani/UnboundedIntegerVariable.h" @@ -16,117 +16,55 @@ namespace storm { namespace detail { - class VariableSetIterator { - private: - typedef std::vector<BooleanVariable>::iterator bool_iter; - typedef std::vector<BoundedIntegerVariable>::iterator bint_iter; - typedef std::vector<UnboundedIntegerVariable>::iterator int_iter; - + template<typename VariableType> + class Dereferencer { public: - /*! - * Creates an iterator over all variables. - */ - VariableSetIterator(VariableSet& variableSet, boost::variant<bool_iter, bint_iter, int_iter> initialIterator); - - // Methods to advance the iterator. - VariableSetIterator& operator++(); - VariableSetIterator& operator++(int); - - Variable& operator*(); - - bool operator==(VariableSetIterator const& other) const; - bool operator!=(VariableSetIterator const& other) const; - - private: - // Moves the iterator to the next position. - void incrementIterator(); - - // The underlying variable set. - VariableSet& variableSet; - - // The current iterator position. - boost::variant<bool_iter, bint_iter, int_iter> it; + VariableType& operator()(std::shared_ptr<VariableType> const& d); }; - class ConstVariableSetIterator { - private: - typedef std::vector<BooleanVariable>::const_iterator bool_iter; - typedef std::vector<BoundedIntegerVariable>::const_iterator bint_iter; - typedef std::vector<UnboundedIntegerVariable>::const_iterator int_iter; - + template<typename VariableType> + class Variables { public: - /*! - * Creates an iterator over all variables. - */ - ConstVariableSetIterator(VariableSet const& variableSet, boost::variant<bool_iter, bint_iter, int_iter> initialIterator); - - // Methods to advance the iterator. - ConstVariableSetIterator& operator++(); - ConstVariableSetIterator& operator++(int); - - Variable const& operator*(); - - bool operator==(ConstVariableSetIterator const& other) const; - bool operator!=(ConstVariableSetIterator const& other) const; - - private: - // Moves the iterator to the next position. - void incrementIterator(); - - // The underlying variable set. - VariableSet const& variableSet; + typedef typename std::vector<std::shared_ptr<VariableType>>::iterator input_iterator; + typedef typename std::vector<std::shared_ptr<VariableType>>::const_iterator const_input_iterator; + typedef boost::transform_iterator<Dereferencer<VariableType>, input_iterator> iterator; + typedef boost::transform_iterator<Dereferencer<VariableType const>, const_input_iterator> const_iterator; - // The current iterator position. - boost::variant<bool_iter, bint_iter, int_iter> it; - }; - - class IntegerVariables { - public: - IntegerVariables(VariableSet& variableSet); + Variables(input_iterator it, input_iterator ite); - /*! - * Retrieves an iterator to all integer variables (bounded and unbounded) in the variable set. - */ - VariableSetIterator begin(); - - /*! - * Retrieves the end iterator to all integer variables (bounded and unbounded) in the variable set. - */ - VariableSetIterator end(); + iterator begin(); + iterator end(); private: - // The underlying variable set. - VariableSet& variableSet; + input_iterator it; + input_iterator ite; }; - - class ConstIntegerVariables { + + template<typename VariableType> + class ConstVariables { public: - ConstIntegerVariables(VariableSet const& variableSet); + typedef typename std::vector<std::shared_ptr<VariableType>>::iterator input_iterator; + typedef typename std::vector<std::shared_ptr<VariableType>>::const_iterator const_input_iterator; + typedef boost::transform_iterator<Dereferencer<VariableType>, input_iterator> iterator; + typedef boost::transform_iterator<Dereferencer<VariableType const>, const_input_iterator> const_iterator; - /*! - * Retrieves an iterator to all integer variables (bounded and unbounded) in the variable set. - */ - ConstVariableSetIterator begin() const; + ConstVariables(const_input_iterator it, const_input_iterator ite); - /*! - * Retrieves the end iterator to all integer variables (bounded and unbounded) in the variable set. - */ - ConstVariableSetIterator end() const; + const_iterator begin(); + const_iterator end(); private: - // The underlying variable set. - VariableSet const& variableSet; + input_iterator it; + input_iterator ite; }; } class VariableSet { public: - friend class detail::VariableSetIterator; - - typedef detail::VariableSetIterator iterator; - typedef detail::ConstVariableSetIterator const_iterator; - typedef detail::IntegerVariables IntegerVariables; - typedef detail::ConstIntegerVariables ConstIntegerVariables; + typedef typename std::vector<std::shared_ptr<Variable>>::iterator input_iterator; + typedef typename std::vector<std::shared_ptr<Variable>>::const_iterator const_input_iterator; + typedef boost::transform_iterator<detail::Dereferencer<Variable>, input_iterator> iterator; + typedef boost::transform_iterator<detail::Dereferencer<Variable>, const_input_iterator> const_iterator; /*! * Creates an empty variable set. @@ -136,42 +74,32 @@ namespace storm { /*! * Retrieves the boolean variables in this set. */ - std::vector<BooleanVariable>& getBooleanVariables(); + detail::Variables<BooleanVariable> getBooleanVariables(); /*! * Retrieves the boolean variables in this set. */ - std::vector<BooleanVariable> const& getBooleanVariables() const; + detail::ConstVariables<BooleanVariable> getBooleanVariables() const; /*! * Retrieves the bounded integer variables in this set. */ - std::vector<BoundedIntegerVariable>& getBoundedIntegerVariables(); + detail::Variables<BoundedIntegerVariable> getBoundedIntegerVariables(); /*! * Retrieves the bounded integer variables in this set. */ - std::vector<BoundedIntegerVariable> const& getBoundedIntegerVariables() const; + detail::ConstVariables<BoundedIntegerVariable> getBoundedIntegerVariables() const; /*! * Retrieves the unbounded integer variables in this set. */ - std::vector<UnboundedIntegerVariable>& getUnboundedIntegerVariables(); + detail::Variables<UnboundedIntegerVariable> getUnboundedIntegerVariables(); /*! * Retrieves the unbounded integer variables in this set. */ - std::vector<UnboundedIntegerVariable> const& getUnboundedIntegerVariables() const; - - /*! - * Retrieves an iterable object to all integer (bounded and unbounded) variables in the variable set. - */ - IntegerVariables getIntegerVariables(); - - /*! - * Retrieves an iterable object to all integer (bounded and unbounded) variables in the variable set. - */ - ConstIntegerVariables getIntegerVariables() const; + detail::ConstVariables<UnboundedIntegerVariable> getUnboundedIntegerVariables() const; /*! * Adds the given boolean variable to this set. @@ -228,21 +156,39 @@ namespace storm { */ const_iterator end() const; + /*! + * Retrieves whether the set of variables contains a boolean variable. + */ + bool containsBooleanVariable() const; + + /*! + * Retrieves whether the set of variables contains a bounded integer variable. + */ + bool containsBoundedIntegerVariable() const; + + /*! + * Retrieves whether the set of variables contains an unbounded integer variable. + */ + bool containsUnboundedIntegerVariables() const; + private: + /// The vector of all variables. + std::vector<std::shared_ptr<Variable>> variables; + /// The boolean variables in this set. - std::vector<BooleanVariable> booleanVariables; + std::vector<std::shared_ptr<BooleanVariable>> booleanVariables; /// The bounded integer variables in this set. - std::vector<BoundedIntegerVariable> boundedIntegerVariables; + std::vector<std::shared_ptr<BoundedIntegerVariable>> boundedIntegerVariables; /// The unbounded integer variables in this set. - std::vector<UnboundedIntegerVariable> unboundedIntegerVariables; + std::vector<std::shared_ptr<UnboundedIntegerVariable>> unboundedIntegerVariables; /// A set of all variable names currently in use. std::map<std::string, storm::expressions::Variable> nameToVariable; /// A mapping from expression variables to their variable objects. - std::map<storm::expressions::Variable, std::pair<uint8_t, uint64_t>> variableToVariable; + std::map<storm::expressions::Variable, std::shared_ptr<Variable>> variableToVariable; }; } diff --git a/test/functional/.DS_Store b/test/functional/.DS_Store deleted file mode 100644 index 31a582c70..000000000 Binary files a/test/functional/.DS_Store and /dev/null differ diff --git a/test/functional/builder/DdJaniModelBuilderTest.cpp b/test/functional/builder/DdJaniModelBuilderTest.cpp new file mode 100644 index 000000000..6b73c1a59 --- /dev/null +++ b/test/functional/builder/DdJaniModelBuilderTest.cpp @@ -0,0 +1,75 @@ +#include "gtest/gtest.h" +#include "storm-config.h" +#include "src/models/symbolic/Dtmc.h" +#include "src/models/symbolic/Ctmc.h" +#include "src/models/symbolic/Mdp.h" + +#include "src/storage/dd/Add.h" +#include "src/storage/dd/Bdd.h" + +#include "src/models/symbolic/StandardRewardModel.h" +#include "src/parser/PrismParser.h" +#include "src/builder/DdJaniModelBuilder.h" + +TEST(DdJaniModelBuilderTest_Sylvan, Dtmc) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + storm::jani::Model janiModel = program.toJani(); + + storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double> builder(janiModel); + std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::Sylvan>> model = builder.translate(); + EXPECT_EQ(13ul, model->getNumberOfStates()); + EXPECT_EQ(20ul, model->getNumberOfTransitions()); + + program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); + janiModel = program.toJani(); + builder = storm::builder::DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>(janiModel); + model = builder.translate(); + EXPECT_EQ(677ul, model->getNumberOfStates()); + EXPECT_EQ(867ul, model->getNumberOfTransitions()); + + program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); + janiModel = program.toJani(); + model = builder.translate(); + EXPECT_EQ(8607ul, model->getNumberOfStates()); + EXPECT_EQ(15113ul, model->getNumberOfTransitions()); + + program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); + janiModel = program.toJani(); + model = builder.translate(); + EXPECT_EQ(273ul, model->getNumberOfStates()); + EXPECT_EQ(397ul, model->getNumberOfTransitions()); + + program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); + janiModel = program.toJani(); + model = builder.translate(); + EXPECT_EQ(1728ul, model->getNumberOfStates()); + EXPECT_EQ(2505ul, model->getNumberOfTransitions()); +} + +//TEST(DdPrismModelBuilderTest_Cudd, Dtmc) { +// storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); +// +// std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD>> model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>().translateProgram(program); +// EXPECT_EQ(13ul, model->getNumberOfStates()); +// EXPECT_EQ(20ul, model->getNumberOfTransitions()); +// +// program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/brp-16-2.pm"); +// model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>().translateProgram(program); +// EXPECT_EQ(677ul, model->getNumberOfStates()); +// EXPECT_EQ(867ul, model->getNumberOfTransitions()); +// +// program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); +// model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>().translateProgram(program); +// EXPECT_EQ(8607ul, model->getNumberOfStates()); +// EXPECT_EQ(15113ul, model->getNumberOfTransitions()); +// +// program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/leader-3-5.pm"); +// model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>().translateProgram(program); +// EXPECT_EQ(273ul, model->getNumberOfStates()); +// EXPECT_EQ(397ul, model->getNumberOfTransitions()); +// +// program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/nand-5-2.pm"); +// model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>().translateProgram(program); +// EXPECT_EQ(1728ul, model->getNumberOfStates()); +// EXPECT_EQ(2505ul, model->getNumberOfTransitions()); +//} \ No newline at end of file