Browse Source

started to include reachability in JANI model generation

Former-commit-id: d54f35b999
main
dehnert 9 years ago
parent
commit
310db8a234
  1. 141
      src/builder/DdJaniModelBuilder.cpp
  2. 49
      src/builder/DdPrismModelBuilder.cpp
  3. 2
      src/builder/DdPrismModelBuilder.h
  4. 9
      src/parser/JaniParser.cpp
  5. 44
      src/storage/jani/Exporter.cpp
  6. 33
      src/storage/prism/Program.cpp
  7. 44
      src/utility/dd.cpp
  8. 24
      src/utility/dd.h
  9. 24
      test/functional/builder/DdJaniModelBuilderTest.cpp
  10. 4
      test/functional/builder/DdPrismModelBuilderTest.cpp

141
src/builder/DdJaniModelBuilder.cpp

@ -18,9 +18,14 @@
#include "src/models/symbolic/Dtmc.h"
#include "src/models/symbolic/Ctmc.h"
#include "src/models/symbolic/Mdp.h"
#include "src/models/symbolic/StandardRewardModel.h"
#include "src/settings/SettingsManager.h"
#include "src/settings/modules/MarkovChainSettings.h"
#include "src/utility/macros.h"
#include "src/utility/jani.h"
#include "src/utility/dd.h"
#include "src/exceptions/InvalidArgumentException.h"
namespace storm {
@ -164,7 +169,8 @@ namespace storm {
std::map<uint64_t, storm::expressions::Variable> actionVariablesMap;
// The meta variables used to encode the remaining nondeterminism.
std::vector<storm::expressions::Variable> nondeterminismVariables;
std::vector<storm::expressions::Variable> orderedNondeterminismVariables;
std::set<storm::expressions::Variable> nondeterminismVariables;
// DDs representing the identity for each variable.
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> variableToIdentityMap;
@ -242,7 +248,8 @@ namespace storm {
}
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);
result.orderedNondeterminismVariables.push_back(variablePair.first);
result.nondeterminismVariables.insert(variablePair.first);
}
// Create global variables.
@ -269,6 +276,10 @@ namespace storm {
identity &= variableIdentity.toBdd();
range &= result.manager->getRange(variablePair.first);
// Add the location variable to the row/column variables.
result.rowMetaVariables.insert(variablePair.first);
result.columnMetaVariables.insert(variablePair.second);
// Then create variables for the variables of the automaton.
for (auto const& variable : automaton.getVariables().getBoundedIntegerVariables()) {
createVariable(variable, result);
@ -687,17 +698,17 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
storm::dd::Add<Type, ValueType> encodeIndex(uint64_t index, std::vector<storm::expressions::Variable> const& nondeterminismVariables, CompositionVariables<Type, ValueType> const& variables) {
storm::dd::Add<Type, ValueType> encodeIndex(uint64_t index, std::vector<storm::expressions::Variable> const& orderedNondeterminismVariables, CompositionVariables<Type, ValueType> const& variables) {
storm::dd::Add<Type, ValueType> result = variables.manager->template getAddZero<ValueType>();
STORM_LOG_TRACE("Encoding " << index << " with " << nondeterminismVariables.size() << " binary variable(s).");
STORM_LOG_TRACE("Encoding " << index << " with " << orderedNondeterminismVariables.size() << " binary variable(s).");
std::map<storm::expressions::Variable, int_fast64_t> metaVariableNameToValueMap;
for (uint_fast64_t i = 0; i < nondeterminismVariables.size(); ++i) {
if (index & (1ull << (nondeterminismVariables.size() - i - 1))) {
metaVariableNameToValueMap.emplace(nondeterminismVariables[i], 1);
for (uint_fast64_t i = 0; i < orderedNondeterminismVariables.size(); ++i) {
if (index & (1ull << (orderedNondeterminismVariables.size() - i - 1))) {
metaVariableNameToValueMap.emplace(orderedNondeterminismVariables[i], 1);
} else {
metaVariableNameToValueMap.emplace(nondeterminismVariables[i], 0);
metaVariableNameToValueMap.emplace(orderedNondeterminismVariables[i], 0);
}
}
@ -717,21 +728,23 @@ namespace storm {
};
template <storm::dd::DdType Type, typename ValueType>
SystemDd<Type, ValueType> buildSystemDd(storm::jani::Model const& model, AutomatonDd<Type, ValueType> const& automatonDd, CompositionVariables<Type, ValueType> const& variables) {
SystemDd<Type, ValueType> buildSystemDd(storm::jani::Model const& model, AutomatonDd<Type, ValueType> const& automatonDd, CompositionVariables<Type, ValueType>& variables) {
// If the model is an MDP, we need to encode the nondeterminism using additional variables.
if (model.getModelType() == storm::jani::ModelType::MDP) {
// Determine how many nondeterminism variables we need.
std::vector<storm::expressions::Variable> actionVariables;
std::vector<storm::expressions::Variable> orderedActionVariables;
std::set<storm::expressions::Variable> actionVariables;
std::map<uint64_t, uint64_t> actionIndexToVariableIndex;
uint64_t maximalNumberOfEdgesPerAction = 0;
for (auto const& action : automatonDd.actionIndexToEdges) {
actionVariables.push_back(variables.actionVariablesMap.at(action.first));
actionIndexToVariableIndex[action.first] = actionVariables.size() - 1;
orderedActionVariables.push_back(variables.actionVariablesMap.at(action.first));
actionVariables.insert(orderedActionVariables.back());
actionIndexToVariableIndex[action.first] = orderedActionVariables.size() - 1;
maximalNumberOfEdgesPerAction = std::max(maximalNumberOfEdgesPerAction, static_cast<uint64_t>(action.second.size()));
}
uint64_t numberOfNondeterminismVariables = static_cast<uint64_t>(std::ceil(std::log2(maximalNumberOfEdgesPerAction)));
std::vector<storm::expressions::Variable> nondeterminismVariables(numberOfNondeterminismVariables);
std::copy(variables.nondeterminismVariables.begin(), variables.nondeterminismVariables.begin() + numberOfNondeterminismVariables, nondeterminismVariables.begin());
std::vector<storm::expressions::Variable> orderedNondeterminismVariables(numberOfNondeterminismVariables);
std::copy(variables.orderedNondeterminismVariables.begin(), variables.orderedNondeterminismVariables.begin() + numberOfNondeterminismVariables, orderedNondeterminismVariables.begin());
// Prepare result.
storm::dd::Add<Type, ValueType> result = variables.manager->template getAddZero<ValueType>();
@ -742,11 +755,11 @@ namespace storm {
uint64_t edgeIndex = 0;
for (auto const& edge : action.second) {
edgesForAction += edge.transitionsDd * computeMissingGlobalVariableIdentities(edge, variables) * encodeIndex(edgeIndex, nondeterminismVariables, variables);
edgesForAction += edge.transitionsDd * computeMissingGlobalVariableIdentities(edge, variables) * encodeIndex(edgeIndex, orderedNondeterminismVariables, variables);
++edgeIndex;
}
result += edgesForAction * encodeAction<Type, ValueType>(actionIndexToVariableIndex.at(action.first), actionVariables, variables);
result += edgesForAction * encodeAction<Type, ValueType>(actionIndexToVariableIndex.at(action.first), orderedActionVariables, variables);
}
return SystemDd<Type, ValueType>(result, result.sumAbstract(variables.columnMetaVariables), numberOfNondeterminismVariables);
@ -780,7 +793,7 @@ namespace storm {
} else if (modelType == storm::jani::ModelType::CTMC) {
return std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>>(new storm::models::symbolic::Ctmc<Type, ValueType>(variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables, variables.columnExpressionAdapter, variables.rowColumnMetaVariablePairs, std::map<std::string, storm::expressions::Expression>(), modelComponents.rewardModels));
} else if (modelType == storm::jani::ModelType::MDP) {
return std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>>(new storm::models::symbolic::Mdp<Type, ValueType>(variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables, variables.columnExpressionAdapter, variables.rowColumnMetaVariablePairs, variables.allNondeterminismVariables, std::map<std::string, storm::expressions::Expression>(), modelComponents.rewardModels));
return std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>>(new storm::models::symbolic::Mdp<Type, ValueType>(variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables, variables.columnExpressionAdapter, variables.rowColumnMetaVariablePairs, variables.nondeterminismVariables, std::map<std::string, storm::expressions::Expression>(), modelComponents.rewardModels));
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Invalid model type.");
}
@ -788,13 +801,18 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
void postprocessSystemDd(storm::jani::Model const& model, SystemDd<Type, ValueType>& system, CompositionVariables<Type, ValueType> const& variables, typename DdJaniModelBuilder<Type, ValueType>::Options const& options) {
void postprocessSystemAndVariables(storm::jani::Model const& model, SystemDd<Type, ValueType>& system, CompositionVariables<Type, ValueType>& variables, typename DdJaniModelBuilder<Type, ValueType>::Options const& options) {
// Get rid of the nondeterminism variables that were not used.
for (uint64_t index = system.numberOfNondeterminismVariables; index < variables.orderedNondeterminismVariables.size(); ++index) {
variables.nondeterminismVariables.erase(variables.orderedNondeterminismVariables[index]);
}
variables.orderedNondeterminismVariables.resize(system.numberOfNondeterminismVariables);
// For DTMCs, we normalize each row to 1 (to account for non-determinism).
if (model.getModelType() == storm::jani::ModelType::DTMC) {
system.transitionsDd = system.transitionsDd / system.stateActionDd;
}
// If we were asked to treat some states as terminal states, we cut away their transitions now.
if (options.terminalStates || options.negatedTerminalStates) {
std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = model.getConstantsSubstitution();
@ -811,19 +829,66 @@ namespace storm {
terminalStatesBdd |= !variables.rowExpressionAdapter->translateExpression(negatedTerminalExpression).toBdd();
}
system.transitionMatrix *= (!terminalStatesBdd).template toAdd<ValueType>();
system.transitionsDd *= (!terminalStatesBdd).template toAdd<ValueType>();
}
}
template <storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> computeInitial(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables) {
storm::dd::Bdd<Type> initialStates = variables.rowExpressionAdapter->translateExpression( generationInfo.program.getInitialConstruct().getInitialStatesExpression()).toBdd();
storm::dd::Bdd<Type> computeInitialStates(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables) {
storm::dd::Bdd<Type> initialStates = variables.rowExpressionAdapter->translateExpression(model.getInitialStatesExpression(true)).toBdd();
for (auto const& automaton : model.getAutomata()) {
initialStates &= variables.manager->getEncoding(variables.automatonToLocationVariableMap.at(automaton.getName()).first, automaton.getInitialLocationIndex());
}
for (auto const& metaVariable : variables.rowMetaVariables) {
initialStates &= variables.manager->getRange(metaVariable);
}
return initialStates;
}
for (auto const& metaVariable : generationInfo.rowMetaVariables) {
initialStates &= generationInfo.manager->getRange(metaVariable);
template <storm::dd::DdType Type, typename ValueType>
void fixDeadlocks(storm::jani::ModelType const& modelType, storm::dd::Add<Type, ValueType>& transitionMatrix, storm::dd::Bdd<Type> const& transitionMatrixBdd, storm::dd::Bdd<Type> const& reachableStates, CompositionVariables<Type, ValueType> const& variables) {
// Detect deadlocks and 1) fix them if requested 2) throw an error otherwise.
storm::dd::Bdd<Type> statesWithTransition = transitionMatrixBdd.existsAbstract(variables.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()) {
STORM_LOG_INFO("Fixing deadlocks in " << deadlockStates.getNonZeroCount() << " states. The first three of these states are: ");
uint_fast64_t count = 0;
for (auto it = deadlockStates.begin(), ite = deadlockStates.end(); it != ite && count < 3; ++it, ++count) {
STORM_LOG_INFO((*it).first.toPrettyString(variables.rowMetaVariables) << std::endl);
}
return initialStates;
// Create a global identity DD.
storm::dd::Add<Type, ValueType> globalIdentity = variables.manager->template getAddOne<ValueType>();
for (auto const& identity : variables.automatonToIdentityMap) {
globalIdentity *= identity.second;
}
for (auto const& variable : variables.allGlobalVariables) {
globalIdentity *= variables.variableToIdentityMap.at(variable);
}
if (modelType == storm::jani::ModelType::DTMC || modelType == storm::jani::ModelType::CTMC) {
// For DTMCs, we can simply add the identity of the global module for all deadlock states.
transitionMatrix += deadlockStates * globalIdentity;
} else if (modelType == storm::jani::ModelType::MDP) {
// For MDPs, however, we need to select an action associated with the self-loop, if we do not
// want to attach a lot of self-loops to the deadlock states.
storm::dd::Add<Type, ValueType> action = variables.manager->template getAddOne<ValueType>();
for (auto const& variable : variables.actionVariablesMap) {
action *= variables.manager->template getIdentity<ValueType>(variable.second);
}
for (auto const& variable : variables.orderedNondeterminismVariables) {
action *= variables.manager->template getIdentity<ValueType>(variable);
}
transitionMatrix += deadlockStates * globalIdentity * action;
}
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The model contains " << deadlockStates.getNonZeroCount() << " deadlock states. Please unset the option to not fix deadlocks, if you want to fix them automatically.");
}
}
}
template <storm::dd::DdType Type, typename ValueType>
@ -840,12 +905,34 @@ namespace storm {
SystemDd<Type, ValueType> system = buildSystemDd(*this->model, globalAutomaton, variables);
// Postprocess the system. This modifies the systemDd in place.
postprocessSystemDd(*this->model, system, variables, options);
postprocessSystemAndVariables(*this->model, system, variables, options);
// Start creating the model components.
ModelComponents<Type, ValueType> modelComponents;
// Build initial states.
modelComponents.initialStates = computeInitialStates(*this->model, variables);
// Perform reachability analysis to obtain reachable states.
storm::dd::Bdd<Type> transitionMatrixBdd = system.transitionsDd.notZero();
if (this->model->getModelType() == storm::jani::ModelType::MDP) {
transitionMatrixBdd = transitionMatrixBdd.existsAbstract(variables.nondeterminismVariables);
}
transitionMatrixBdd.template toAdd<ValueType>().exportToDot("trans_before_reach.dot");
modelComponents.initialStates.template toAdd<ValueType>().exportToDot("initial.dot");
modelComponents.reachableStates = storm::utility::dd::computeReachableStates(modelComponents.initialStates, transitionMatrixBdd, variables.rowMetaVariables, variables.columnMetaVariables);
modelComponents.reachableStates.template toAdd<ValueType>().exportToDot("reach.dot");
// Cut transitions to reachable states.
storm::dd::Add<Type, ValueType> reachableStatesAdd = modelComponents.reachableStates.template toAdd<ValueType>();
modelComponents.transitionMatrix = system.transitionsDd * reachableStatesAdd;
system.transitionsDd.exportToDot("trans_full.dot");
system.stateActionDd *= reachableStatesAdd;
// Fix deadlocks if existing.
// fixDeadlocks(this->model->getModelType(), modelComponents.transitionMatrix, transitionMatrixBdd, modelComponents.reachableStates, variables);
// Finally, create the model.
return createModel(this->model->getModelType(), variables, modelComponents);
}

49
src/builder/DdPrismModelBuilder.cpp

@ -7,7 +7,6 @@
#include "src/models/symbolic/Mdp.h"
#include "src/models/symbolic/StandardRewardModel.h"
#include "src/storage/dd/DdManager.h"
#include "src/settings/SettingsManager.h"
#include "src/exceptions/InvalidStateException.h"
@ -16,9 +15,11 @@
#include "src/utility/prism.h"
#include "src/utility/math.h"
#include "src/utility/dd.h"
#include "src/storage/dd/DdManager.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"
#include "src/storage/dd/Bdd.h"
@ -1328,7 +1329,7 @@ namespace storm {
transitionMatrixBdd = transitionMatrixBdd.existsAbstract(generationInfo.allNondeterminismVariables);
}
storm::dd::Bdd<Type> reachableStates = computeReachableStates(generationInfo, initialStates, transitionMatrixBdd);
storm::dd::Bdd<Type> reachableStates = storm::utility::dd::computeReachableStates<Type>(initialStates, transitionMatrixBdd, generationInfo.rowMetaVariables, generationInfo.columnMetaVariables);
storm::dd::Add<Type, ValueType> reachableStatesAdd = reachableStates.template toAdd<ValueType>();
transitionMatrix *= reachableStatesAdd;
stateActionDd *= reachableStatesAdd;
@ -1347,17 +1348,23 @@ namespace storm {
STORM_LOG_INFO((*it).first.toPrettyString(generationInfo.rowMetaVariables) << std::endl);
}
if (program.getModelType() == storm::prism::Program::ModelType::DTMC) {
if (program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::CTMC) {
storm::dd::Add<Type, ValueType> identity = globalModule.identity;
// Make sure that global variables do not change along the introduced self-loops.
for (auto const& var : generationInfo.allGlobalVariables) {
identity *= generationInfo.variableToIdentityMap.at(var);
}
// For DTMCs, we can simply add the identity of the global module for all deadlock states.
transitionMatrix += deadlockStates * globalModule.identity;
transitionMatrix += deadlockStates * identity;
} else if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
// For MDPs, however, we need to select an action associated with the self-loop, if we do not
// want to attach a lot of self-loops to the deadlock states.
storm::dd::Add<Type, ValueType> action = generationInfo.manager->template getAddOne<ValueType>();
std::for_each(generationInfo.allNondeterminismVariables.begin(), generationInfo.allNondeterminismVariables.end(),
[&action, &generationInfo] (storm::expressions::Variable const& metaVariable) {
for (auto const& metaVariable : generationInfo.allNondeterminismVariables) {
action *= generationInfo.manager->template getIdentity<ValueType>(metaVariable);
});
}
// Make sure that global variables do not change along the introduced self-loops.
for (auto const& var : generationInfo.allGlobalVariables) {
action *= generationInfo.variableToIdentityMap.at(var);
@ -1421,32 +1428,6 @@ namespace storm {
return initialStates;
}
template <storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> DdPrismModelBuilder<Type, ValueType>::computeReachableStates(GenerationInformation& generationInfo, storm::dd::Bdd<Type> const& initialStates, storm::dd::Bdd<Type> const& transitionBdd) {
storm::dd::Bdd<Type> reachableStates = initialStates;
// Perform the BFS to discover all reachable states.
bool changed = true;
uint_fast64_t iteration = 0;
do {
STORM_LOG_TRACE("Iteration " << iteration << " of reachability analysis.");
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;
}
reachableStates |= newReachableStates;
++iteration;
} while (changed);
return reachableStates;
}
// Explicitly instantiate the symbolic model builder.
template class DdPrismModelBuilder<storm::dd::DdType::CUDD>;
template class DdPrismModelBuilder<storm::dd::DdType::Sylvan>;

2
src/builder/DdPrismModelBuilder.h

@ -257,8 +257,6 @@ namespace storm {
static storm::dd::Bdd<Type> createInitialStatesDecisionDiagram(GenerationInformation& generationInfo);
static storm::dd::Bdd<Type> computeReachableStates(GenerationInformation& generationInfo, storm::dd::Bdd<Type> const& initialStates, storm::dd::Bdd<Type> const& transitions);
// This member holds the program that was most recently translated (if any).
boost::optional<storm::prism::Program> preparedProgram;
};

9
src/parser/JaniParser.cpp

@ -104,10 +104,12 @@ namespace storm {
// TODO something.
} else if(variableStructure.at("type") == "bool") {
STORM_LOG_THROW(initExpr.hasBooleanType(), storm::exceptions::InvalidJaniException, "Initial value for Boolean variable " << name << " (scope: " << scopeDescription << ") should have Boolean type.");
return std::make_shared<storm::jani::BooleanVariable>(name, expressionManager->declareBooleanVariable(exprManagerName), initExpr);
// TODO: reenable and put initExpr in the place where it belongs.
// return std::make_shared<storm::jani::BooleanVariable>(name, expressionManager->declareBooleanVariable(exprManagerName), initExpr);
} else if(variableStructure.at("type") == "int") {
STORM_LOG_THROW(initExpr.hasIntegerType(), storm::exceptions::InvalidJaniException, "Initial value for interger variable " << name << " (scope: " << scopeDescription << ") should have integer type.");
return std::make_shared<storm::jani::UnboundedIntegerVariable>(name, expressionManager->declareIntegerVariable(exprManagerName), initExpr);
// TODO: reenable and put initExpr in the place where it belongs.
// return std::make_shared<storm::jani::UnboundedIntegerVariable>(name, expressionManager->declareIntegerVariable(exprManagerName), initExpr);
} else {
// TODO clocks.
STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown type description " << variableStructure.at("type").dump() << " for Variable '" << name << "' (scope: " << scopeDescription << ")");
@ -129,7 +131,8 @@ namespace storm {
if(basictype == "int") {
STORM_LOG_THROW(lowerboundExpr.hasIntegerType(), storm::exceptions::InvalidJaniException, "Lower bound for bounded integer variable " << name << "(scope: " << scopeDescription << ") must be integer-typed");
STORM_LOG_THROW(upperboundExpr.hasIntegerType(), storm::exceptions::InvalidJaniException, "Upper bound for bounded integer variable " << name << "(scope: " << scopeDescription << ") must be integer-typed");
return std::make_shared<storm::jani::BoundedIntegerVariable>(name, expressionManager->declareIntegerVariable(exprManagerName), lowerboundExpr, upperboundExpr, initExpr);
// TODO: reenable and put initExpr in the place where it belongs.
// return std::make_shared<storm::jani::BoundedIntegerVariable>(name, expressionManager->declareIntegerVariable(exprManagerName), lowerboundExpr, upperboundExpr, initExpr);
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unsupported base " << basictype << " for bounded variable " << name << "(scope: " << scopeDescription << ") ");
}

44
src/storage/jani/Exporter.cpp

@ -88,10 +88,6 @@ namespace storm {
out << ",";
clearLine(out);
appendIndent(out, indent);
appendField(out, "initial-value");
appendValue(out, expressionToString(variable.getInitialValue()));
clearLine(out);
appendIndent(out, indent);
out << "}";
}
@ -137,10 +133,6 @@ namespace storm {
appendBoundedIntegerVariableType(out, variable, indent + 2);
out << ",";
clearLine(out);
appendIndent(out, indent + 1);
appendField(out, "initial-value");
appendValue(out, expressionToString(variable.getInitialValue()));
clearLine(out);
appendIndent(out, indent);
out << "}";
}
@ -159,10 +151,6 @@ namespace storm {
appendValue(out, "int");
out << ",";
clearLine(out);
appendIndent(out, indent + 1);
appendField(out, "initial-value");
appendValue(out, expressionToString(variable.getInitialValue()));
clearLine(out);
appendIndent(out, indent);
out << "}";
}
@ -359,6 +347,21 @@ namespace storm {
appendField(out, "initial-location");
appendValue(out, std::to_string(automaton.getInitialLocationIndex()));
clearLine(out);
if (automaton.hasInitialStatesExpression()) {
appendIndent(out, indent + 1);
appendField(out, "initial-states");
clearLine(out);
appendIndent(out, indent + 2);
out << "{";
clearLine(out);
appendIndent(out, indent + 3);
appendField(out, "exp");
appendValue(out, expressionToString(automaton.getInitialStatesExpression()));
clearLine(out);
appendIndent(out, indent + 2);
out << "}";
clearLine(out);
}
appendEdges(out, model, automaton, indent + 1);
@ -401,6 +404,23 @@ namespace storm {
clearLine(out);
appendVariables(out, model.getGlobalVariables(), 1);
clearLine(out);
if (model.hasInitialStatesExpression()) {
appendIndent(out, 1);
appendField(out, "initial-states");
clearLine(out);
appendIndent(out, 2);
out << "{";
clearLine(out);
appendIndent(out, 3);
appendField(out, "exp");
appendValue(out, expressionToString(model.getInitialStatesExpression()));
clearLine(out);
appendIndent(out, 2);
out << "}";
clearLine(out);
}
appendAutomata(out, model, 1);
clearLine(out);
out << "}" << std::endl;

33
src/storage/prism/Program.cpp

@ -1494,6 +1494,7 @@ namespace storm {
default: modelType = storm::jani::ModelType::UNDEFINED;
}
storm::jani::Model janiModel("jani_from_prism", modelType, 1, manager);
storm::expressions::Expression globalInitialStatesExpression;
// Add all constants of the PRISM program to the JANI model.
for (auto const& constant : constants) {
@ -1502,10 +1503,14 @@ namespace storm {
// Add all global variables of the PRISM program to the JANI model.
for (auto const& variable : globalBooleanVariables) {
janiModel.addBooleanVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression()));
janiModel.addBooleanVariable(storm::jani::BooleanVariable(variable.getName(), variable.getExpressionVariable()));
storm::expressions::Expression variableInitialExpression = storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression());
globalInitialStatesExpression = globalInitialStatesExpression.isInitialized() ? globalInitialStatesExpression && variableInitialExpression : variableInitialExpression;
}
for (auto const& variable : globalIntegerVariables) {
janiModel.addBoundedIntegerVariable(storm::jani::BoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getLowerBoundExpression(), variable.getUpperBoundExpression(), variable.getInitialValueExpression()));
janiModel.addBoundedIntegerVariable(storm::jani::BoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getLowerBoundExpression(), variable.getUpperBoundExpression()));
storm::expressions::Expression variableInitialExpression = variable.getExpressionVariable() == variable.getInitialValueExpression();
globalInitialStatesExpression = globalInitialStatesExpression.isInitialized() ? globalInitialStatesExpression && variableInitialExpression : variableInitialExpression;
}
// Add all actions of the PRISM program to the JANI model.
@ -1545,29 +1550,42 @@ namespace storm {
// previously built mapping to make variables global that are read by more than one module.
for (auto const& module : modules) {
storm::jani::Automaton automaton(module.getName());
storm::expressions::Expression initialStatesExpression;
for (auto const& variable : module.getBooleanVariables()) {
storm::jani::BooleanVariable newBooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.getInitialValueExpression());
storm::jani::BooleanVariable newBooleanVariable(variable.getName(), variable.getExpressionVariable());
std::set<uint_fast64_t> const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()];
// If there is exactly one module reading and writing the variable, we can make the variable local to this module.
if (accessingModuleIndices.size() == 1) {
automaton.addBooleanVariable(newBooleanVariable);
} else { // if (accessingModuleIndices.size() > 1) {
storm::expressions::Expression variableInitialExpression = storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression());
initialStatesExpression = initialStatesExpression.isInitialized() ? initialStatesExpression && variableInitialExpression : variableInitialExpression;
} else if (accessingModuleIndices.size() > 1) {
// Otherwise, we need to make it global.
janiModel.addBooleanVariable(newBooleanVariable);
storm::expressions::Expression variableInitialExpression = storm::expressions::iff(variable.getExpressionVariable(), variable.getInitialValueExpression());
globalInitialStatesExpression = globalInitialStatesExpression.isInitialized() ? globalInitialStatesExpression && variableInitialExpression : variableInitialExpression;
}
}
for (auto const& variable : module.getIntegerVariables()) {
storm::jani::BoundedIntegerVariable newIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getLowerBoundExpression(), variable.getUpperBoundExpression(), variable.getInitialValueExpression());
storm::jani::BoundedIntegerVariable newIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.getLowerBoundExpression(), variable.getUpperBoundExpression());
std::set<uint_fast64_t> const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()];
// If there is exactly one module reading and writing the variable, we can make the variable local to this module.
if (accessingModuleIndices.size() == 1) {
automaton.addBoundedIntegerVariable(newIntegerVariable);
} else { //if (accessingModuleIndices.size() > 1) {
storm::expressions::Expression variableInitialExpression = variable.getExpressionVariable() == variable.getInitialValueExpression();
initialStatesExpression = initialStatesExpression.isInitialized() ? initialStatesExpression && variableInitialExpression : variableInitialExpression;
} else if (accessingModuleIndices.size() > 1) {
// Otherwise, we need to make it global.
janiModel.addBoundedIntegerVariable(newIntegerVariable);
storm::expressions::Expression variableInitialExpression = variable.getExpressionVariable() == variable.getInitialValueExpression();
globalInitialStatesExpression = globalInitialStatesExpression.isInitialized() ? globalInitialStatesExpression && variableInitialExpression : variableInitialExpression;
}
}
// Set the proper expression characterizing the initial values of the automaton's variables.
automaton.setInitialStatesExpression(initialStatesExpression);
// Create a single location that will have all the edges.
uint64_t onlyLocation = automaton.addLocation(storm::jani::Location("l"));
automaton.setInitialLocation(onlyLocation);
@ -1603,6 +1621,9 @@ namespace storm {
janiModel.addAutomaton(automaton);
}
// Set the proper expression characterizing the initial values of the global variables.
janiModel.setInitialStatesExpression(globalInitialStatesExpression);
// Set the standard system composition. This is possible, because we reject non-standard compositions anyway.
janiModel.setSystemComposition(janiModel.getStandardSystemComposition());

44
src/utility/dd.cpp

@ -0,0 +1,44 @@
#include "src/utility/dd.h"
#include "src/storage/dd/Add.h"
#include "src/storage/dd/Bdd.h"
#include "src/utility/macros.h"
namespace storm {
namespace utility {
namespace dd {
template <storm::dd::DdType Type>
storm::dd::Bdd<Type> computeReachableStates(storm::dd::Bdd<Type> const& initialStates, storm::dd::Bdd<Type> const& transitions, std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables) {
storm::dd::Bdd<Type> reachableStates = initialStates;
// Perform the BFS to discover all reachable states.
bool changed = true;
uint_fast64_t iteration = 0;
do {
STORM_LOG_TRACE("Iteration " << iteration << " of reachability analysis.");
changed = false;
storm::dd::Bdd<Type> tmp = reachableStates.relationalProduct(transitions, rowMetaVariables, columnMetaVariables);
storm::dd::Bdd<Type> newReachableStates = tmp && (!reachableStates);
// Check whether new states were indeed discovered.
if (!newReachableStates.isZero()) {
changed = true;
}
reachableStates |= newReachableStates;
++iteration;
} while (changed);
return reachableStates;
}
template storm::dd::Bdd<storm::dd::DdType::CUDD> computeReachableStates(storm::dd::Bdd<storm::dd::DdType::CUDD> const& initialStates, storm::dd::Bdd<storm::dd::DdType::CUDD> const& transitions, std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables);
template storm::dd::Bdd<storm::dd::DdType::Sylvan> computeReachableStates(storm::dd::Bdd<storm::dd::DdType::Sylvan> const& initialStates, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& transitions, std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables);
}
}
}

24
src/utility/dd.h

@ -0,0 +1,24 @@
#pragma once
#include <set>
#include "src/storage/dd/DdType.h"
namespace storm {
namespace expressions {
class Variable;
}
namespace dd {
template<storm::dd::DdType Type>
class Bdd;
}
namespace utility {
namespace dd {
template <storm::dd::DdType Type>
storm::dd::Bdd<Type> computeReachableStates(storm::dd::Bdd<Type> const& initialStates, storm::dd::Bdd<Type> const& transitions, std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables);
}
}
}

24
test/functional/builder/DdJaniModelBuilderTest.cpp

@ -76,8 +76,12 @@ TEST(DdJaniModelBuilderTest_Cudd, Dtmc) {
auto t2 = std::chrono::high_resolution_clock::now();
std::cout << "die: " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << std::endl;
// EXPECT_EQ(13ul, model->getNumberOfStates());
// EXPECT_EQ(20ul, model->getNumberOfTransitions());
model->getTransitionMatrix().exportToDot("trans.dot");
std::cout << "nnz: " << model->getTransitionMatrix().getNonZeroCount() << std::endl;
std::cout << "nodes: " << model->getTransitionMatrix().getNodeCount() << std::endl;
std::cout << "vars: " << model->getTransitionMatrix().getContainedMetaVariables().size() << std::endl;
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();
@ -86,8 +90,8 @@ TEST(DdJaniModelBuilderTest_Cudd, Dtmc) {
model = builder.translate();
t2 = std::chrono::high_resolution_clock::now();
std::cout << "brp: " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << std::endl;
// EXPECT_EQ(677ul, model->getNumberOfStates());
// EXPECT_EQ(867ul, model->getNumberOfTransitions());
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();
@ -96,8 +100,8 @@ TEST(DdJaniModelBuilderTest_Cudd, Dtmc) {
model = builder.translate();
t2 = std::chrono::high_resolution_clock::now();
std::cout << "crowds: " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << std::endl;
// EXPECT_EQ(8607ul, model->getNumberOfStates());
// EXPECT_EQ(15113ul, model->getNumberOfTransitions());
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();
@ -106,8 +110,8 @@ TEST(DdJaniModelBuilderTest_Cudd, Dtmc) {
model = builder.translate();
t2 = std::chrono::high_resolution_clock::now();
std::cout << "lead: " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << std::endl;
// EXPECT_EQ(273ul, model->getNumberOfStates());
// EXPECT_EQ(397ul, model->getNumberOfTransitions());
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();
@ -116,8 +120,8 @@ TEST(DdJaniModelBuilderTest_Cudd, Dtmc) {
model = builder.translate();
t2 = std::chrono::high_resolution_clock::now();
std::cout << "nand: " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << std::endl;
// EXPECT_EQ(1728ul, model->getNumberOfStates());
// EXPECT_EQ(2505ul, model->getNumberOfTransitions());
EXPECT_EQ(1728ul, model->getNumberOfStates());
EXPECT_EQ(2505ul, model->getNumberOfTransitions());
}
//TEST(DdPrismModelBuilderTest_Cudd, Dtmc) {

4
test/functional/builder/DdPrismModelBuilderTest.cpp

@ -57,6 +57,10 @@ 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);
model->getTransitionMatrix().exportToDot("trans_prism.dot");
std::cout << "nnz: " << model->getTransitionMatrix().getNonZeroCount() << std::endl;
std::cout << "nodes: " << model->getTransitionMatrix().getNodeCount() << std::endl;
std::cout << "vars: " << model->getTransitionMatrix().getContainedMetaVariables().size() << std::endl;
EXPECT_EQ(13ul, model->getNumberOfStates());
EXPECT_EQ(20ul, model->getNumberOfTransitions());

Loading…
Cancel
Save