Browse Source

Merge branch 'master' into portfolio

main
Tim Quatmann 5 years ago
parent
commit
98bd96eace
  1. 3
      CHANGELOG.md
  2. 13
      src/storm-cli-utilities/model-handling.h
  3. 15
      src/storm/api/builder.h
  4. 56
      src/storm/builder/BuilderOptions.cpp
  5. 134
      src/storm/builder/DdJaniModelBuilder.cpp
  6. 19
      src/storm/builder/DdJaniModelBuilder.h
  7. 88
      src/storm/builder/DdPrismModelBuilder.cpp
  8. 10
      src/storm/builder/DdPrismModelBuilder.h
  9. 101
      src/storm/builder/TerminalStatesGetter.cpp
  10. 58
      src/storm/builder/TerminalStatesGetter.h
  11. 12
      src/storm/settings/modules/BuildSettings.cpp
  12. 13
      src/storm/settings/modules/BuildSettings.h

3
CHANGELOG.md

@ -12,6 +12,9 @@ Version 1.4.x
- Setting `--engine dd-to-sparse --bisimulation` now triggers extracting the sparse bisimulation quotiont
- JIT model building is now invoked via `--engine jit` (instead of `--jit`)
- DRN: support import of choice labelling
- Added option `--build:buildchoiceorig` to build a model (PRISM or JANI) with choice origins (which are exported with, e.g. `--exportscheduler`).
- Apply the maximum progress assumption while building a Markov automata with the Dd engine.
- Added option `--build:nomaxprog` to disable applying the maximum progress assumption during model building (for Markov Automata)
- `storm-pomdp`: Only accept POMDPs that are canonical
- `storm-pomdp`: Prism language extended with observable expressions
- `storm-pomdp`: Various fixes that prevented usage.

13
src/storm-cli-utilities/model-handling.h

@ -374,7 +374,8 @@ namespace storm {
template <storm::dd::DdType DdType, typename ValueType>
std::shared_ptr<storm::models::ModelBase> buildModelDd(SymbolicInput const& input) {
return storm::api::buildSymbolicModel<DdType, ValueType>(input.model.get(), createFormulasToRespect(input.properties), storm::settings::getModule<storm::settings::modules::BuildSettings>().isBuildFullModelSet());
auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
return storm::api::buildSymbolicModel<DdType, ValueType>(input.model.get(), createFormulasToRespect(input.properties), buildSettings.isBuildFullModelSet(), !buildSettings.isApplyNoMaximumProgressAssumptionSet());
}
template <typename ValueType>
@ -382,19 +383,23 @@ namespace storm {
storm::builder::BuilderOptions options(createFormulasToRespect(input.properties), input.model.get());
options.setBuildChoiceLabels(buildSettings.isBuildChoiceLabelsSet());
options.setBuildStateValuations(buildSettings.isBuildStateValuationsSet());
bool buildChoiceOrigins = false;
bool buildChoiceOrigins = buildSettings.isBuildChoiceOriginsSet();
if (storm::settings::manager().hasModule(storm::settings::modules::CounterexampleGeneratorSettings::moduleName)) {
auto counterexampleGeneratorSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
if (counterexampleGeneratorSettings.isCounterexampleSet()) {
buildChoiceOrigins = counterexampleGeneratorSettings.isMinimalCommandSetGenerationSet();
buildChoiceOrigins |= counterexampleGeneratorSettings.isMinimalCommandSetGenerationSet();
}
options.setBuildChoiceOrigins(buildChoiceOrigins);
}
options.setBuildChoiceOrigins(buildChoiceOrigins);
if (input.model->getModelType() == storm::storage::SymbolicModelDescription::ModelType::POMDP) {
options.setBuildChoiceOrigins(true);
options.setBuildChoiceLabels(true);
}
if (buildSettings.isApplyNoMaximumProgressAssumptionSet()) {
options.setApplyMaximalProgressAssumption(false);
}
options.setAddOutOfBoundsState(buildSettings.isBuildOutOfBoundsStateSet());
if (buildSettings.isBuildFullModelSet()) {
options.clearTerminalStates();

15
src/storm/api/builder.h

@ -37,26 +37,29 @@ namespace storm {
}
template<storm::dd::DdType LibraryType, typename ValueType>
std::shared_ptr<storm::models::symbolic::Model<LibraryType, ValueType>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool buildFullModel = false) {
std::shared_ptr<storm::models::symbolic::Model<LibraryType, ValueType>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool buildFullModel = false, bool applyMaximumProgress = true) {
if (model.isPrismProgram()) {
typename storm::builder::DdPrismModelBuilder<LibraryType, ValueType>::Options options;
options = typename storm::builder::DdPrismModelBuilder<LibraryType, ValueType>::Options(formulas);
if (buildFullModel) {
options.buildAllLabels = true;
options.buildAllRewardModels = true;
options.terminalStates.clear();
}
storm::builder::DdPrismModelBuilder<LibraryType, ValueType> builder;
return builder.build(model.asPrismProgram(), options);
} else {
STORM_LOG_THROW(model.isJaniModel(), storm::exceptions::NotSupportedException, "Building symbolic model from this model description is unsupported.");
typename storm::builder::DdJaniModelBuilder<LibraryType, ValueType>::Options options;
options = typename storm::builder::DdJaniModelBuilder<LibraryType, ValueType>::Options(formulas);
typename storm::builder::DdJaniModelBuilder<LibraryType, ValueType>::Options options(formulas);
if (buildFullModel) {
options.buildAllLabels = true;
options.buildAllRewardModels = true;
options.applyMaximumProgressAssumption = false;
options.terminalStates.clear();
} else {
options.applyMaximumProgressAssumption = (model.getModelType() == storm::storage::SymbolicModelDescription::ModelType::MA && applyMaximumProgress);
}
storm::builder::DdJaniModelBuilder<LibraryType, ValueType> builder;
@ -65,12 +68,12 @@ namespace storm {
}
template<>
inline std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD, storm::RationalNumber>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool buildFullModel) {
inline std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD, storm::RationalNumber>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool, bool) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "CUDD does not support rational numbers.");
}
template<>
inline std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD, storm::RationalFunction>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool buildFullModel) {
inline std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD, storm::RationalFunction>> buildSymbolicModel(storm::storage::SymbolicModelDescription const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, bool, bool) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "CUDD does not support rational functions.");
}

56
src/storm/builder/BuilderOptions.cpp

@ -1,5 +1,7 @@
#include "storm/builder/BuilderOptions.h"
#include "storm/builder/TerminalStatesGetter.h"
#include "storm/logic/Formulas.h"
#include "storm/logic/LiftableTransitionRewardsVisitor.h"
@ -95,58 +97,8 @@ namespace storm {
}
void BuilderOptions::setTerminalStatesFromFormula(storm::logic::Formula const& formula) {
if (formula.isAtomicExpressionFormula()) {
addTerminalExpression(formula.asAtomicExpressionFormula().getExpression(), true);
} else if (formula.isAtomicLabelFormula()) {
addTerminalLabel(formula.asAtomicLabelFormula().getLabel(), true);
} else if (formula.isEventuallyFormula()) {
storm::logic::Formula const& sub = formula.asEventuallyFormula().getSubformula();
if (sub.isAtomicExpressionFormula() || sub.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(sub);
}
} else if (formula.isUntilFormula()) {
storm::logic::Formula const& right = formula.asUntilFormula().getRightSubformula();
if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(right);
}
storm::logic::Formula const& left = formula.asUntilFormula().getLeftSubformula();
if (left.isAtomicExpressionFormula()) {
addTerminalExpression(left.asAtomicExpressionFormula().getExpression(), false);
} else if (left.isAtomicLabelFormula()) {
addTerminalLabel(left.asAtomicLabelFormula().getLabel(), false);
}
} else if (formula.isBoundedUntilFormula()) {
storm::logic::BoundedUntilFormula const& boundedUntil = formula.asBoundedUntilFormula();
bool hasLowerBound = false;
for (uint64_t i = 0; i < boundedUntil.getDimension(); ++i) {
if (boundedUntil.hasLowerBound(i) && (boundedUntil.getLowerBound(i).containsVariables() || !storm::utility::isZero(boundedUntil.getLowerBound(i).evaluateAsRational()))) {
hasLowerBound = true;
break;
}
}
if (!hasLowerBound) {
storm::logic::Formula const& right = boundedUntil.getRightSubformula();
if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(right);
}
}
storm::logic::Formula const& left = boundedUntil.getLeftSubformula();
if (left.isAtomicExpressionFormula()) {
addTerminalExpression(left.asAtomicExpressionFormula().getExpression(), false);
} else if (left.isAtomicLabelFormula()) {
addTerminalLabel(left.asAtomicLabelFormula().getLabel(), false);
}
} else if (formula.isProbabilityOperatorFormula()) {
storm::logic::Formula const& sub = formula.asProbabilityOperatorFormula().getSubformula();
if (sub.isEventuallyFormula() || sub.isUntilFormula() || sub.isBoundedUntilFormula()) {
this->setTerminalStatesFromFormula(sub);
}
} else if (formula.isRewardOperatorFormula() || formula.isTimeOperatorFormula()) {
storm::logic::Formula const& sub = formula.asOperatorFormula().getSubformula();
if (sub.isEventuallyFormula()) {
this->setTerminalStatesFromFormula(sub);
}
}
getTerminalStatesFromFormula(formula, [this](storm::expressions::Expression const& expr, bool inverted){ this->addTerminalExpression(expr, inverted);}
, [this](std::string const& label, bool inverted){ this->addTerminalLabel(label, inverted);});
}
std::set<std::string> const& BuilderOptions::getRewardModelNames() const {

134
src/storm/builder/DdJaniModelBuilder.cpp

@ -112,18 +112,18 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
DdJaniModelBuilder<Type, ValueType>::Options::Options(bool buildAllLabels, bool buildAllRewardModels) : buildAllLabels(buildAllLabels), buildAllRewardModels(buildAllRewardModels), rewardModelsToBuild(), constantDefinitions(), terminalStates(), negatedTerminalStates() {
DdJaniModelBuilder<Type, ValueType>::Options::Options(bool buildAllLabels, bool buildAllRewardModels, bool applyMaximumProgressAssumption) : buildAllLabels(buildAllLabels), buildAllRewardModels(buildAllRewardModels), applyMaximumProgressAssumption(applyMaximumProgressAssumption), rewardModelsToBuild(), constantDefinitions() {
// Intentionally left empty.
}
template <storm::dd::DdType Type, typename ValueType>
DdJaniModelBuilder<Type, ValueType>::Options::Options(storm::logic::Formula const& formula) : buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions(), terminalStates(), negatedTerminalStates() {
DdJaniModelBuilder<Type, ValueType>::Options::Options(storm::logic::Formula const& formula) : buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions() {
this->preserveFormula(formula);
this->setTerminalStatesFromFormula(formula);
}
template <storm::dd::DdType Type, typename ValueType>
DdJaniModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) : buildAllLabels(false), buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions(), terminalStates(), negatedTerminalStates() {
DdJaniModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) : buildAllLabels(false), buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions() {
if (!formulas.empty()) {
for (auto const& formula : formulas) {
this->preserveFormula(*formula);
@ -137,12 +137,7 @@ namespace storm {
template <storm::dd::DdType Type, typename ValueType>
void DdJaniModelBuilder<Type, ValueType>::Options::preserveFormula(storm::logic::Formula const& formula) {
// If we already had terminal states, we need to erase them.
if (terminalStates) {
terminalStates.reset();
}
if (negatedTerminalStates) {
negatedTerminalStates.reset();
}
terminalStates.clear();
// If we are not required to build all reward models, we determine the reward models we need to build.
if (!buildAllRewardModels) {
@ -159,28 +154,7 @@ namespace storm {
template <storm::dd::DdType Type, typename ValueType>
void DdJaniModelBuilder<Type, ValueType>::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) {
if (formula.isAtomicExpressionFormula()) {
terminalStates = formula.asAtomicExpressionFormula().getExpression();
} else if (formula.isEventuallyFormula()) {
storm::logic::Formula const& sub = formula.asEventuallyFormula().getSubformula();
if (sub.isAtomicExpressionFormula() || sub.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(sub);
}
} else if (formula.isUntilFormula()) {
storm::logic::Formula const& right = formula.asUntilFormula().getRightSubformula();
if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(right);
}
storm::logic::Formula const& left = formula.asUntilFormula().getLeftSubformula();
if (left.isAtomicExpressionFormula()) {
negatedTerminalStates = left.asAtomicExpressionFormula().getExpression();
}
} else if (formula.isProbabilityOperatorFormula()) {
storm::logic::Formula const& sub = formula.asProbabilityOperatorFormula().getSubformula();
if (sub.isEventuallyFormula() || sub.isUntilFormula()) {
this->setTerminalStatesFromFormula(sub);
}
}
terminalStates = getTerminalStatesFromFormula(formula);
}
template <storm::dd::DdType Type, typename ValueType>
@ -754,6 +728,19 @@ namespace storm {
return ActionDd(newGuard, newTransitions, newTransientEdgeAssignments, newLocalNondeterminismVariables, newVariableToWritingFragment, newIllegalFragment);
}
/*!
* Conjuncts the guard of the action with the provided condition, i.e., this action is only enabled if the provided condition is true.
*/
void conjunctGuardWith(storm::dd::Bdd<Type> const& condition) {
guard &= condition;
storm::dd::Add<Type, ValueType> conditionAdd = condition.template toAdd<ValueType>();
transitions *= conditionAdd;
for (auto& t : transientEdgeAssignments) {
t.second *= conditionAdd;
}
illegalFragment &= condition;
}
bool isInputEnabled() const {
return inputEnabled;
}
@ -762,10 +749,10 @@ namespace storm {
inputEnabled = true;
}
// A DD that represents all states that have this edge enabled.
// A DD that represents all states that have this action enabled.
storm::dd::Bdd<Type> guard;
// A DD that represents the transitions of this edge.
// A DD that represents the transitions of this action.
storm::dd::Add<Type, ValueType> transitions;
// A mapping from transient variables to their assignments.
@ -879,11 +866,12 @@ namespace storm {
std::pair<uint64_t, uint64_t> localNondeterminismVariables;
};
CombinedEdgesSystemComposer(storm::jani::Model const& model, storm::jani::CompositionInformation const& actionInformation, CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables) : SystemComposer<Type, ValueType>(model, variables, transientVariables), actionInformation(actionInformation) {
CombinedEdgesSystemComposer(storm::jani::Model const& model, storm::jani::CompositionInformation const& actionInformation, CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables, bool applyMaximumProgress) : SystemComposer<Type, ValueType>(model, variables, transientVariables), actionInformation(actionInformation), applyMaximumProgress(applyMaximumProgress) {
// Intentionally left empty.
}
storm::jani::CompositionInformation const& actionInformation;
bool applyMaximumProgress;
ComposerResult<Type, ValueType> compose() override {
STORM_LOG_THROW(this->model.hasStandardCompliantComposition(), storm::exceptions::WrongFormatException, "Model builder only supports non-nested parallel compositions.");
@ -1005,13 +993,14 @@ namespace storm {
boost::optional<uint64_t> previousActionPosition = synchVector.getPositionOfPrecedingParticipatingAction(subcompositionIndex);
STORM_LOG_ASSERT(previousActionPosition, "Inconsistent information about synchronization vector.");
AutomatonDd const& previousAutomatonDd = subautomata[previousActionPosition.get()];
auto precedingActionIt = previousAutomatonDd.actions.find(ActionIdentification(actionInformation.getActionIndex(synchVector.getInput(previousActionPosition.get())), synchronizationVectorIndex, isCtmc));
auto precedingActionIndex = actionInformation.getActionIndex(synchVector.getInput(previousActionPosition.get()));
auto precedingActionIt = previousAutomatonDd.actions.find(ActionIdentification(precedingActionIndex, synchronizationVectorIndex, isCtmc));
uint64_t highestLocalNondeterminismVariable = 0;
if (precedingActionIt != previousAutomatonDd.actions.end()) {
highestLocalNondeterminismVariable = precedingActionIt->second.getHighestLocalNondeterminismVariable();
} else {
STORM_LOG_WARN("Subcomposition does not have action that is mentioned in parallel composition.");
STORM_LOG_WARN("Subcomposition does not have action" << actionInformation.getActionName(precedingActionIndex) << " that is mentioned in parallel composition.");
}
actionInstantiations[actionIndex].emplace_back(actionIndex, synchronizationVectorIndex, highestLocalNondeterminismVariable, isCtmc);
}
@ -1027,6 +1016,9 @@ namespace storm {
AutomatonDd composeInParallel(std::vector<AutomatonDd> const& subautomata, std::vector<storm::jani::SynchronizationVector> const& synchronizationVectors) {
AutomatonDd result(this->variables.manager->template getAddOne<ValueType>());
// Disjunction of all guards of non-markovian actions (only required for maximum progress assumption.
storm::dd::Bdd<Type> nonMarkovianActionGuards = this->variables.manager->getBddZero();
// Build the results of the synchronization vectors.
std::unordered_map<ActionIdentification, std::vector<ActionDd>, ActionIdentificationHash> actions;
for (uint64_t synchronizationVectorIndex = 0; synchronizationVectorIndex < synchronizationVectors.size(); ++synchronizationVectorIndex) {
@ -1034,6 +1026,11 @@ namespace storm {
boost::optional<ActionDd> synchronizingAction = combineSynchronizingActions(subautomata, synchVector, synchronizationVectorIndex);
if (synchronizingAction) {
if (applyMaximumProgress) {
STORM_LOG_ASSERT(this->model.getModelType() == storm::jani::ModelType::MA, "Maximum progress assumption enabled for unexpected model type.");
// By the JANI standard, we can assume that synchronizing actions of MAs are always non-Markovian.
nonMarkovianActionGuards |= synchronizingAction->guard;
}
actions[ActionIdentification(actionInformation.getActionIndex(synchVector.getOutput()), this->model.getModelType() == storm::jani::ModelType::CTMC)].emplace_back(synchronizingAction.get());
}
}
@ -1077,9 +1074,26 @@ namespace storm {
auto& allSilentActionDds = actions[silentActionIdentification];
allSilentActionDds.insert(allSilentActionDds.end(), silentActionDds.begin(), silentActionDds.end());
}
// Add guards of non-markovian actions
if (applyMaximumProgress) {
auto allSilentActionDdsIt = actions.find(silentActionIdentification);
if (allSilentActionDdsIt != actions.end()) {
for (ActionDd const& silentActionDd : allSilentActionDdsIt->second) {
nonMarkovianActionGuards |= silentActionDd.guard;
}
}
}
if (!silentMarkovianActionDds.empty()) {
auto& allMarkovianSilentActionDds = actions[silentMarkovianActionIdentification];
allMarkovianSilentActionDds.insert(allMarkovianSilentActionDds.end(), silentMarkovianActionDds.begin(), silentMarkovianActionDds.end());
if (applyMaximumProgress && !nonMarkovianActionGuards.isZero()) {
auto invertedNonMarkovianGuards = !nonMarkovianActionGuards;
for (ActionDd& markovianActionDd : allMarkovianSilentActionDds) {
markovianActionDd.conjunctGuardWith(invertedNonMarkovianGuards);
}
}
}
// Finally, combine (potentially) multiple action DDs.
@ -1936,7 +1950,7 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> postprocessSystem(storm::jani::Model const& model, ComposerResult<Type, ValueType>& system, CompositionVariables<Type, ValueType> const& variables, typename DdJaniModelBuilder<Type, ValueType>::Options const& options) {
storm::dd::Bdd<Type> postprocessSystem(storm::jani::Model const& model, ComposerResult<Type, ValueType>& system, CompositionVariables<Type, ValueType> const& variables, typename DdJaniModelBuilder<Type, ValueType>::Options const& options, std::map<std::string, storm::expressions::Expression> const& labelsToExpressionMap) {
// For DTMCs, we normalize each row to 1 (to account for non-determinism).
if (model.getModelType() == storm::jani::ModelType::DTMC) {
storm::dd::Add<Type, ValueType> stateToNumberOfChoices = system.transitions.sumAbstract(variables.columnMetaVariables);
@ -1949,25 +1963,23 @@ namespace storm {
}
// 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();
storm::dd::Bdd<Type> terminalStatesBdd = variables.manager->getBddZero();
if (options.terminalStates) {
storm::expressions::Expression terminalExpression = options.terminalStates.get().substitute(constantsSubstitution);
STORM_LOG_TRACE("Making the states satisfying " << terminalExpression << " terminal.");
terminalStatesBdd = variables.rowExpressionAdapter->translateExpression(terminalExpression).toBdd();
}
if (options.negatedTerminalStates) {
storm::expressions::Expression negatedTerminalExpression = options.negatedTerminalStates.get().substitute(constantsSubstitution);
STORM_LOG_TRACE("Making the states *not* satisfying " << negatedTerminalExpression << " terminal.");
terminalStatesBdd |= !variables.rowExpressionAdapter->translateExpression(negatedTerminalExpression).toBdd();
}
storm::dd::Bdd<Type> terminalStatesBdd = variables.manager->getBddZero();
if (!options.terminalStates.empty()) {
storm::expressions::Expression terminalExpression = options.terminalStates.asExpression([&model, &labelsToExpressionMap](std::string const& labelName) {
auto exprIt = labelsToExpressionMap.find(labelName);
if (exprIt != labelsToExpressionMap.end()) {
return exprIt->second;
} else {
STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'.");
// If the label name is "init" we can abort 'exploration' directly at the initial state. If it is deadlock, we do not have to abort.
return model.getExpressionManager().boolean(labelName == "init");
}
});
terminalExpression = terminalExpression.substitute(model.getConstantsSubstitution());
terminalStatesBdd = variables.rowExpressionAdapter->translateExpression(terminalExpression).toBdd();
system.transitions *= (!terminalStatesBdd).template toAdd<ValueType>();
return terminalStatesBdd;
}
return variables.manager->getBddZero();
return terminalStatesBdd;
}
template <storm::dd::DdType Type, typename ValueType>
@ -2164,18 +2176,25 @@ namespace storm {
std::vector<storm::expressions::Variable> rewardVariables = selectRewardVariables<Type, ValueType>(preparedModel, options);
// Create a builder to compose and build the model.
CombinedEdgesSystemComposer<Type, ValueType> composer(preparedModel, actionInformation, variables, rewardVariables);
bool applyMaximumProgress = options.applyMaximumProgressAssumption && model.getModelType() == storm::jani::ModelType::MA;
CombinedEdgesSystemComposer<Type, ValueType> composer(preparedModel, actionInformation, variables, rewardVariables, applyMaximumProgress);
ComposerResult<Type, ValueType> system = composer.compose();
// Postprocess the variables in place.
postprocessVariables(preparedModel.getModelType(), system, variables);
// Build the label to expressions mapping.
auto labelsToExpressionMap = buildLabelExpressions(preparedModel, variables, options);
// Postprocess the system in place and get the states that were terminal (i.e. whose transitions were cut off).
storm::dd::Bdd<Type> terminalStates = postprocessSystem(preparedModel, system, variables, options);
storm::dd::Bdd<Type> terminalStates = postprocessSystem(preparedModel, system, variables, options, labelsToExpressionMap);
// Start creating the model components.
ModelComponents<Type, ValueType> modelComponents;
// Set the label expressions
modelComponents.labelToExpressionMap = std::move(labelsToExpressionMap);
// Build initial states.
modelComponents.initialStates = computeInitialStates(preparedModel, variables);
@ -2203,9 +2222,6 @@ namespace storm {
// Build the reward models.
modelComponents.rewardModels = buildRewardModels(reachableStatesAdd, modelComponents.transitionMatrix, preparedModel.getModelType(), variables, system, rewardVariables);
// Build the label to expressions mapping.
modelComponents.labelToExpressionMap = buildLabelExpressions(preparedModel, variables, options);
// Finally, create the model.
return createModel(preparedModel.getModelType(), variables, modelComponents);
}

19
src/storm/builder/DdJaniModelBuilder.h

@ -6,6 +6,7 @@
#include "storm/storage/jani/Property.h"
#include "storm/logic/Formula.h"
#include "storm/builder/TerminalStatesGetter.h"
namespace storm {
@ -42,7 +43,7 @@ namespace storm {
/*!
* Creates an object representing the default building options.
*/
Options(bool buildAllLabels = false, bool buildAllRewardModels = false);
Options(bool buildAllLabels = false, bool buildAllRewardModels = false, bool applyMaximumProgressAssumption = true);
/*! Creates an object representing the suggested building options assuming that the given formula is the
* only one to check. Additional formulas may be preserved by calling <code>preserveFormula</code>.
@ -93,9 +94,6 @@ namespace storm {
/// A flag that indicates whether all labels are to be built. In this case, the label names are to be ignored.
bool buildAllLabels;
/// A set of labels to build.
std::set<std::string> labelNames;
/*!
* Retrieves whether the flag to build all reward models is set.
@ -105,19 +103,22 @@ namespace storm {
// A flag that indicates whether or not all reward models are to be build.
bool buildAllRewardModels;
/// A flag that indicates whether the maximum progress assumption should be applied.
bool applyMaximumProgressAssumption;
/// A set of labels to build.
std::set<std::string> labelNames;
// A list of reward models to be build in case not all reward models are to be build.
std::set<std::string> rewardModelsToBuild;
// An optional mapping that, if given, contains defining expressions for undefined constants.
boost::optional<std::map<storm::expressions::Variable, storm::expressions::Expression>> constantDefinitions;
// An optional expression or label that (a subset of) characterizes the terminal states of the model.
// An optional set of expression or labels that characterizes (a subset of) the terminal states of the model.
// If this is set, the outgoing transitions of these states are replaced with a self-loop.
boost::optional<storm::expressions::Expression> terminalStates;
storm::builder::TerminalStates terminalStates;
// An optional expression or label whose negation characterizes (a subset of) the terminal states of the
// model. If this is set, the outgoing transitions of these states are replaced with a self-loop.
boost::optional<storm::expressions::Expression> negatedTerminalStates;
};
/*!

88
src/storm/builder/DdPrismModelBuilder.cpp

@ -550,13 +550,13 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
DdPrismModelBuilder<Type, ValueType>::Options::Options(storm::logic::Formula const& formula) : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(std::set<std::string>()), terminalStates(), negatedTerminalStates() {
DdPrismModelBuilder<Type, ValueType>::Options::Options(storm::logic::Formula const& formula) : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(std::set<std::string>()) {
this->preserveFormula(formula);
this->setTerminalStatesFromFormula(formula);
}
template <storm::dd::DdType Type, typename ValueType>
DdPrismModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(), terminalStates(), negatedTerminalStates() {
DdPrismModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild() {
for (auto const& formula : formulas) {
this->preserveFormula(*formula);
}
@ -568,12 +568,7 @@ namespace storm {
template <storm::dd::DdType Type, typename ValueType>
void DdPrismModelBuilder<Type, ValueType>::Options::preserveFormula(storm::logic::Formula const& formula) {
// If we already had terminal states, we need to erase them.
if (terminalStates) {
terminalStates.reset();
}
if (negatedTerminalStates) {
negatedTerminalStates.reset();
}
terminalStates.clear();
// If we are not required to build all reward models, we determine the reward models we need to build.
if (!buildAllRewardModels) {
@ -593,32 +588,7 @@ namespace storm {
template <storm::dd::DdType Type, typename ValueType>
void DdPrismModelBuilder<Type, ValueType>::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) {
if (formula.isAtomicExpressionFormula()) {
terminalStates = formula.asAtomicExpressionFormula().getExpression();
} else if (formula.isAtomicLabelFormula()) {
terminalStates = formula.asAtomicLabelFormula().getLabel();
} else if (formula.isEventuallyFormula()) {
storm::logic::Formula const& sub = formula.asEventuallyFormula().getSubformula();
if (sub.isAtomicExpressionFormula() || sub.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(sub);
}
} else if (formula.isUntilFormula()) {
storm::logic::Formula const& right = formula.asUntilFormula().getRightSubformula();
if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) {
this->setTerminalStatesFromFormula(right);
}
storm::logic::Formula const& left = formula.asUntilFormula().getLeftSubformula();
if (left.isAtomicExpressionFormula()) {
negatedTerminalStates = left.asAtomicExpressionFormula().getExpression();
} else if (left.isAtomicLabelFormula()) {
negatedTerminalStates = left.asAtomicLabelFormula().getLabel();
}
} else if (formula.isProbabilityOperatorFormula()) {
storm::logic::Formula const& sub = formula.asProbabilityOperatorFormula().getSubformula();
if (sub.isEventuallyFormula() || sub.isUntilFormula()) {
this->setTerminalStatesFromFormula(sub);
}
}
terminalStates = getTerminalStatesFromFormula(formula);
}
template <storm::dd::DdType Type, typename ValueType>
@ -1356,52 +1326,18 @@ namespace storm {
// If we were asked to treat some states as terminal states, we cut away their transitions now.
storm::dd::Bdd<Type> terminalStatesBdd = generationInfo.manager->getBddZero();
if (options.terminalStates || options.negatedTerminalStates) {
std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = program.getConstantsSubstitution();
if (options.terminalStates) {
storm::expressions::Expression terminalExpression;
if (options.terminalStates.get().type() == typeid(storm::expressions::Expression)) {
terminalExpression = boost::get<storm::expressions::Expression>(options.terminalStates.get());
} else {
std::string const& labelName = boost::get<std::string>(options.terminalStates.get());
if (!options.terminalStates.empty()) {
storm::expressions::Expression terminalExpression = options.terminalStates.asExpression([&program](std::string const& labelName) {
if (program.hasLabel(labelName)) {
terminalExpression = program.getLabelExpression(labelName);
return program.getLabelExpression(labelName);
} else {
STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'.");
// If the label name is "init" we can abort 'exploration' directly at the initial state. If it is deadlock, we do not have to abort.
return program.getManager().boolean(labelName == "init");
}
}
if (terminalExpression.isInitialized()) {
// If the expression refers to constants of the model, we need to substitute them.
terminalExpression = terminalExpression.substitute(constantsSubstitution);
STORM_LOG_TRACE("Making the states satisfying " << terminalExpression << " terminal.");
terminalStatesBdd = generationInfo.rowExpressionAdapter->translateExpression(terminalExpression).toBdd();
}
}
if (options.negatedTerminalStates) {
storm::expressions::Expression negatedTerminalExpression;
if (options.negatedTerminalStates.get().type() == typeid(storm::expressions::Expression)) {
negatedTerminalExpression = boost::get<storm::expressions::Expression>(options.negatedTerminalStates.get());
} else {
std::string const& labelName = boost::get<std::string>(options.negatedTerminalStates.get());
if (program.hasLabel(labelName)) {
negatedTerminalExpression = program.getLabelExpression(labelName);
} else {
STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'.");
}
}
if (negatedTerminalExpression.isInitialized()) {
// If the expression refers to constants of the model, we need to substitute them.
negatedTerminalExpression = negatedTerminalExpression.substitute(constantsSubstitution);
STORM_LOG_TRACE("Making the states *not* satisfying " << negatedTerminalExpression << " terminal.");
terminalStatesBdd |= !generationInfo.rowExpressionAdapter->translateExpression(negatedTerminalExpression).toBdd();
}
}
});
terminalExpression = terminalExpression.substitute(program.getConstantsSubstitution());
terminalStatesBdd = generationInfo.rowExpressionAdapter->translateExpression(terminalExpression).toBdd();
transitionMatrix *= (!terminalStatesBdd).template toAdd<ValueType>();
}

10
src/storm/builder/DdPrismModelBuilder.h

@ -7,6 +7,8 @@
#include "storm/storage/prism/Program.h"
#include "storm/builder/TerminalStatesGetter.h"
#include "storm/logic/Formulas.h"
#include "storm/adapters/AddExpressionAdapter.h"
#include "storm/utility/macros.h"
@ -88,13 +90,9 @@ namespace storm {
// An optional set of labels that, if given, restricts the labels that are built.
boost::optional<std::set<std::string>> labelsToBuild;
// An optional expression or label that (a subset of) characterizes the terminal states of the model.
// An optional set of expression or labels that characterizes (a subset of) the terminal states of the model.
// If this is set, the outgoing transitions of these states are replaced with a self-loop.
boost::optional<boost::variant<storm::expressions::Expression, std::string>> terminalStates;
// An optional expression or label whose negation characterizes (a subset of) the terminal states of the
// model. If this is set, the outgoing transitions of these states are replaced with a self-loop.
boost::optional<boost::variant<storm::expressions::Expression, std::string>> negatedTerminalStates;
storm::builder::TerminalStates terminalStates;
};
/*!

101
src/storm/builder/TerminalStatesGetter.cpp

@ -0,0 +1,101 @@
#include "storm/builder/TerminalStatesGetter.h"
#include "storm/storage/expressions/Expression.h"
#include "storm/logic/Formulas.h"
namespace storm {
namespace builder {
void getTerminalStatesFromFormula(storm::logic::Formula const& formula, std::function<void(storm::expressions::Expression const&, bool)> const& terminalExpressionCallback, std::function<void(std::string const&, bool)> const& terminalLabelCallback) {
if (formula.isAtomicExpressionFormula()) {
terminalExpressionCallback(formula.asAtomicExpressionFormula().getExpression(), true);
} else if (formula.isAtomicLabelFormula()) {
terminalLabelCallback(formula.asAtomicLabelFormula().getLabel(), true);
} else if (formula.isEventuallyFormula()) {
storm::logic::Formula const& sub = formula.asEventuallyFormula().getSubformula();
if (sub.isAtomicExpressionFormula() || sub.isAtomicLabelFormula()) {
getTerminalStatesFromFormula(sub, terminalExpressionCallback, terminalLabelCallback);
}
} else if (formula.isUntilFormula()) {
storm::logic::Formula const& right = formula.asUntilFormula().getRightSubformula();
if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) {
getTerminalStatesFromFormula(right, terminalExpressionCallback, terminalLabelCallback);
}
storm::logic::Formula const& left = formula.asUntilFormula().getLeftSubformula();
if (left.isAtomicExpressionFormula()) {
terminalExpressionCallback(left.asAtomicExpressionFormula().getExpression(), false);
} else if (left.isAtomicLabelFormula()) {
terminalLabelCallback(left.asAtomicLabelFormula().getLabel(), false);
}
} else if (formula.isBoundedUntilFormula()) {
storm::logic::BoundedUntilFormula const& boundedUntil = formula.asBoundedUntilFormula();
bool hasLowerBound = false;
for (uint64_t i = 0; i < boundedUntil.getDimension(); ++i) {
if (boundedUntil.hasLowerBound(i) && (boundedUntil.getLowerBound(i).containsVariables() || !storm::utility::isZero(boundedUntil.getLowerBound(i).evaluateAsRational()))) {
hasLowerBound = true;
break;
}
}
if (!hasLowerBound) {
storm::logic::Formula const& right = boundedUntil.getRightSubformula();
if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) {
getTerminalStatesFromFormula(right, terminalExpressionCallback, terminalLabelCallback);
}
}
storm::logic::Formula const& left = boundedUntil.getLeftSubformula();
if (left.isAtomicExpressionFormula()) {
terminalExpressionCallback(left.asAtomicExpressionFormula().getExpression(), false);
} else if (left.isAtomicLabelFormula()) {
terminalLabelCallback(left.asAtomicLabelFormula().getLabel(), false);
}
} else if (formula.isProbabilityOperatorFormula()) {
storm::logic::Formula const& sub = formula.asProbabilityOperatorFormula().getSubformula();
if (sub.isEventuallyFormula() || sub.isUntilFormula() || sub.isBoundedUntilFormula()) {
getTerminalStatesFromFormula(sub, terminalExpressionCallback, terminalLabelCallback);
}
} else if (formula.isRewardOperatorFormula() || formula.isTimeOperatorFormula()) {
storm::logic::Formula const& sub = formula.asOperatorFormula().getSubformula();
if (sub.isEventuallyFormula()) {
getTerminalStatesFromFormula(sub, terminalExpressionCallback, terminalLabelCallback);
}
}
}
void TerminalStates::clear() {
terminalExpressions.clear();
negatedTerminalExpressions.clear();
terminalLabels.clear();
negatedTerminalLabels.clear();
}
bool TerminalStates::empty() const {
return terminalExpressions.empty() && negatedTerminalExpressions.empty() && terminalLabels.empty() && negatedTerminalLabels.empty();
}
storm::expressions::Expression TerminalStates::asExpression(std::function<storm::expressions::Expression(std::string const&)> const& labelToExpressionMap) const {
auto allTerminalExpressions = terminalExpressions;
for (auto const& e : negatedTerminalExpressions) {
allTerminalExpressions.push_back(!e);
}
for (auto const& l : terminalLabels) {
allTerminalExpressions.push_back(labelToExpressionMap(l));
}
for (auto const& l : negatedTerminalLabels) {
allTerminalExpressions.push_back(!labelToExpressionMap(l));
}
STORM_LOG_ASSERT(!allTerminalExpressions.empty(), "Unable to convert empty terminal state set to expression");
return storm::expressions::disjunction(allTerminalExpressions);
}
TerminalStates getTerminalStatesFromFormula(storm::logic::Formula const& formula) {
TerminalStates result;
getTerminalStatesFromFormula(formula, [&result](storm::expressions::Expression const& expr, bool inverted){ if(inverted) { result.terminalExpressions.push_back(expr);} else {result.negatedTerminalExpressions.push_back(expr);} },
[&result](std::string const& label, bool inverted){ if(inverted) { result.terminalLabels.push_back(label);} else {result.negatedTerminalLabels.push_back(label);} });
return result;
}
}
}

58
src/storm/builder/TerminalStatesGetter.h

@ -0,0 +1,58 @@
#pragma once
#include <functional>
#include <string>
#include <vector>
namespace storm {
namespace expressions {
class Expression;
}
namespace logic {
class Formula;
}
namespace builder {
/*!
* Traverses the formula. If an expression (or label) is found such that checking the formula does not require further exploration from a state
* satisfying (or violating) the expression (or label), the corresponding callback function is called.
* @param formula The formula to analyzed
* @param terminalExpressionCallback called on terminal expressions. The corresponding flag indicates whether exploration can stop from states satisfying (true) or violating (false) the expression
* @param terminalLabelCallback called on terminal labels. The corresponding flag indicates whether exploration can stop from states having (true) or not having (false) the label
*/
void getTerminalStatesFromFormula(storm::logic::Formula const& formula, std::function<void(storm::expressions::Expression const&, bool)> const& terminalExpressionCallback, std::function<void(std::string const&, bool)> const& terminalLabelCallback);
struct TerminalStates {
std::vector<storm::expressions::Expression> terminalExpressions; // if one of these is true, we can stop exploration
std::vector<storm::expressions::Expression> negatedTerminalExpressions; // if one of these is false, we can stop exploration
std::vector<std::string> terminalLabels; // if a state has one of these labels, we can stop exploration
std::vector<std::string> negatedTerminalLabels; // if a state does not have all of these labels, we can stop exploration
/*!
* Clears all terminal states. After calling this, empty() holds.
*/
void clear();
/*!
* True if no terminal states are specified
*/
bool empty() const;
/*!
* Returns an expression that evaluates to true only if the exploration can stop at the corresponding state
* Should only be called if this is not empty.
*/
storm::expressions::Expression asExpression(std::function<storm::expressions::Expression(std::string const&)> const& labelToExpressionMap) const;
};
/*!
* Traverses the formula. If an expression (or label) is found such that checking the formula does not require further exploration from a state
* satisfying (or violating) the expression (or label), it is inserted into the returned struct.
*/
TerminalStates getTerminalStatesFromFormula(storm::logic::Formula const& formula);
}
}

12
src/storm/settings/modules/BuildSettings.cpp

@ -28,7 +28,9 @@ namespace storm {
const std::string dontFixDeadlockOptionShortName = "ndl";
const std::string noBuildOptionName = "nobuild";
const std::string fullModelBuildOptionName = "buildfull";
const std::string applyNoMaxProgAssumptionOptionName = "nomaxprog";
const std::string buildChoiceLabelOptionName = "buildchoicelab";
const std::string buildChoiceOriginsOptionName = "buildchoiceorig";
const std::string buildStateValuationsOptionName = "buildstateval";
const std::string buildOutOfBoundsStateOptionName = "buildoutofboundsstate";
const std::string bitsForUnboundedVariablesOptionName = "int-bits";
@ -38,7 +40,9 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, prismCompatibilityOptionName, false, "Enables PRISM compatibility. This may be necessary to process some PRISM models.").setShortName(prismCompatibilityOptionShortName).build());
this->addOption(storm::settings::OptionBuilder(moduleName, dontFixDeadlockOptionName, false, "If the model contains deadlock states, they need to be fixed by setting this option.").setShortName(dontFixDeadlockOptionShortName).setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, fullModelBuildOptionName, false, "If set, include all rewards and labels.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, applyNoMaxProgAssumptionOptionName, false, "If set, the maximum progress assumption is not applied while building the model (relevant for MAs)").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, buildChoiceLabelOptionName, false, "If set, also build the choice labels").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, buildChoiceOriginsOptionName, false, "If set, also build information that for each choice indicates the part(s) of the input that yielded the choice.").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, buildStateValuationsOptionName, false, "If set, also build the state valuations").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, noBuildOptionName, false, "If set, do not build the model.").setIsAdvanced().build());
@ -75,9 +79,17 @@ namespace storm {
return this->getOption(noBuildOptionName).getHasOptionBeenSet();
}
bool BuildSettings::isApplyNoMaximumProgressAssumptionSet() const {
return this->getOption(applyNoMaxProgAssumptionOptionName).getHasOptionBeenSet();
}
bool BuildSettings::isBuildChoiceLabelsSet() const {
return this->getOption(buildChoiceLabelOptionName).getHasOptionBeenSet();
}
bool BuildSettings::isBuildChoiceOriginsSet() const {
return this->getOption(buildChoiceOriginsOptionName).getHasOptionBeenSet();
}
bool BuildSettings::isBuildStateValuationsSet() const {
return this->getOption(buildStateValuationsOptionName).getHasOptionBeenSet();

13
src/storm/settings/modules/BuildSettings.h

@ -72,12 +72,21 @@ namespace storm {
* @return true iff the full model should be build.
*/
bool isBuildFullModelSet() const;
/*!
* Retrieves whether the maximum progress assumption is to be applied when building the model
*/
bool isApplyNoMaximumProgressAssumptionSet() const;
/*!
* Retrieves whether the choice labels should be build
* @return
*/
bool isBuildChoiceLabelsSet() const;
/*!
* Retrieves whether the choice origins should be build
*/
bool isBuildChoiceOriginsSet() const;
/*!
* Retrieves whether the choice labels should be build

|||||||
100:0
Loading…
Cancel
Save