Browse Source

first working version of symbolic Markov automaton bisimulation

tempestpy_adaptions
dehnert 7 years ago
parent
commit
9e5e1980dd
  1. 23
      src/storm-cli-utilities/model-handling.h
  2. 2
      src/storm/api/bisimulation.h
  3. 59
      src/storm/builder/DdJaniModelBuilder.cpp
  4. 4
      src/storm/models/ModelBase.cpp
  5. 7
      src/storm/models/ModelBase.h
  6. 128
      src/storm/models/symbolic/MarkovAutomaton.cpp
  7. 111
      src/storm/models/symbolic/MarkovAutomaton.h
  8. 4
      src/storm/models/symbolic/Mdp.cpp
  9. 5
      src/storm/models/symbolic/Model.cpp
  10. 12
      src/storm/models/symbolic/Model.h
  11. 6
      src/storm/storage/dd/BisimulationDecomposition.cpp
  12. 30
      src/storm/storage/dd/bisimulation/NondeterministicModelPartitionRefiner.cpp
  13. 6
      src/storm/storage/dd/bisimulation/NondeterministicModelPartitionRefiner.h
  14. 4
      src/storm/storage/dd/bisimulation/PartitionRefiner.cpp
  15. 9
      src/storm/storage/dd/bisimulation/QuotientExtractor.cpp
  16. 2
      src/storm/storage/dd/bisimulation/SignatureComputer.cpp
  17. 2
      src/storm/storage/dd/cudd/InternalCuddAdd.cpp

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

@ -23,6 +23,7 @@
#include "storm/models/sparse/StandardRewardModel.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/ResourceSettings.h"
@ -304,6 +305,23 @@ namespace storm {
}
}
template <storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type
preprocessDdMarkovAutomaton(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model) {
return model;
}
template <storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type
preprocessDdMarkovAutomaton(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model) {
auto ma = model->template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>();
if (!ma->isClosed()) {
return std::make_shared<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>(ma->close());
} else {
return model;
}
}
template <storm::dd::DdType DdType, typename ValueType>
std::shared_ptr<storm::models::Model<ValueType>> preprocessDdModelBisimulation(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input, storm::settings::modules::BisimulationSettings const& bisimulationSettings) {
STORM_LOG_WARN_COND(!bisimulationSettings.isWeakBisimulationSet(), "Weak bisimulation is currently not supported on DDs. Falling back to strong bisimulation.");
@ -318,6 +336,11 @@ namespace storm {
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
std::pair<std::shared_ptr<storm::models::Model<ValueType>>, bool> result = std::make_pair(model, false);
if (model->isOfType(storm::models::ModelType::MarkovAutomaton)) {
result.first = preprocessDdMarkovAutomaton(result.first->template as<storm::models::symbolic::Model<DdType, ValueType>>());
result.second = true;
}
if (generalSettings.isBisimulationSet()) {
result.first = preprocessDdModelBisimulation(model, input, bisimulationSettings);
result.second = true;

2
src/storm/api/bisimulation.h

@ -58,7 +58,7 @@ namespace storm {
template <storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager) {
STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Dtmc) || model->isOfType(storm::models::ModelType::Ctmc) || model->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Symbolic bisimulation minimization is currently only available for DTMCs and CTMCs.");
STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Dtmc) || model->isOfType(storm::models::ModelType::Ctmc) || model->isOfType(storm::models::ModelType::Mdp) || model->isOfType(storm::models::ModelType::MarkovAutomaton), storm::exceptions::NotSupportedException, "Symbolic bisimulation minimization is currently only available for DTMCs, CTMCs, MDPs and MAs.");
STORM_LOG_THROW(bisimulationType == storm::storage::BisimulationType::Strong, storm::exceptions::NotSupportedException, "Currently only strong bisimulation is supported.");
// Try to get rid of non state-rewards to easy bisimulation computation.

59
src/storm/builder/DdJaniModelBuilder.cpp

@ -6,7 +6,6 @@
#include "storm/logic/Formulas.h"
#include "storm/storage/jani/Edge.h"
#include "storm/storage/jani/EdgeDestination.h"
#include "storm/storage/jani/Model.h"
@ -25,6 +24,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Ctmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/settings/SettingsManager.h"
@ -560,7 +560,7 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
storm::dd::Add<Type, ValueType> encodeAction(boost::optional<uint64_t> const& actionIndex, CompositionVariables<Type, ValueType> const& variables) {
storm::dd::Add<Type, ValueType> encodeAction(boost::optional<uint64_t> const& actionIndex, boost::optional<bool> const& markovian, CompositionVariables<Type, ValueType> const& variables) {
storm::dd::Add<Type, ValueType> encoding = variables.manager->template getAddOne<ValueType>();
for (auto it = variables.actionVariablesMap.rbegin(), ite = variables.actionVariablesMap.rend(); it != ite; ++it) {
@ -571,6 +571,14 @@ namespace storm {
}
}
if (markovian) {
if (markovian.get()) {
encoding *= variables.markovMarker.template toAdd<ValueType>();
} else {
encoding *= (!variables.markovMarker).template toAdd<ValueType>();
}
}
return encoding;
}
@ -695,6 +703,10 @@ namespace storm {
this->markovian = markovian;
}
bool isMarkovian() const {
return this->markovian;
}
bool operator==(ActionIdentification const& other) const {
bool result = actionIndex == other.actionIndex && markovian == other.markovian;
if (synchronizationVectorIndex) {
@ -1117,7 +1129,7 @@ namespace storm {
result = ActionDd(result.guard || actionIt->guard, result.transitions + actionIt->transitions, joinTransientAssignmentMaps(result.transientEdgeAssignments, actionIt->transientEdgeAssignments), std::make_pair<uint64_t, uint64_t>(0, 0), joinVariableWritingFragmentMaps(result.variableToWritingFragment, actionIt->variableToWritingFragment), result.illegalFragment || actionIt->illegalFragment);
}
return result;
} else if (this->model.getModelType() == storm::jani::ModelType::MDP || this->model.getModelType() == storm::jani::ModelType::LTS ) {
} else if (this->model.getModelType() == storm::jani::ModelType::MDP || this->model.getModelType() == storm::jani::ModelType::LTS || this->model.getModelType() == storm::jani::ModelType::MA) {
// Ensure that all actions start at the same local nondeterminism variable.
uint_fast64_t lowestLocalNondeterminismVariable = actions.front().getLowestLocalNondeterminismVariable();
uint_fast64_t highestLocalNondeterminismVariable = actions.front().getHighestLocalNondeterminismVariable();
@ -1378,7 +1390,7 @@ namespace storm {
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
bool overlappingGuards = false;
for (auto const& edgeDd : edgeDds) {
STORM_LOG_THROW((this->model.getModelType() == storm::jani::ModelType::CTMC) == edgeDd.isMarkovian, storm::exceptions::WrongFormatException, "Unexpected non-Markovian edge in CTMC.");
STORM_LOG_THROW((this->model.getModelType() == storm::jani::ModelType::CTMC || this->model.getModelType() == storm::jani::ModelType::MA) == edgeDd.isMarkovian, storm::exceptions::WrongFormatException, "Unexpected edge type.");
// Check for overlapping guards.
overlappingGuards = !(edgeDd.guard && allGuards).isZero();
@ -1617,8 +1629,12 @@ namespace storm {
}
ComposerResult<Type, ValueType> buildSystemFromAutomaton(AutomatonDd& automaton) {
STORM_LOG_TRACE("Building system from final automaton.");
auto modelType = this->model.getModelType();
// If the model is an MDP, we need to encode the nondeterminism using additional variables.
if (this->model.getModelType() == storm::jani::ModelType::MDP || this->model.getModelType() == storm::jani::ModelType::LTS) {
if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::MA || modelType == storm::jani::ModelType::LTS) {
storm::dd::Add<Type, ValueType> result = this->variables.manager->template getAddZero<ValueType>();
storm::dd::Bdd<Type> illegalFragment = this->variables.manager->getBddZero();
@ -1629,14 +1645,20 @@ namespace storm {
// Add missing global variable identities, action and nondeterminism encodings.
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
std::unordered_set<uint64_t> actionIndices;
std::unordered_set<ActionIdentification, ActionIdentificationHash> containedActions;
for (auto& action : automaton.actions) {
STORM_LOG_TRACE("Treating action with index " << action.first.actionIndex << (action.first.isMarkovian() ? " (Markovian)" : "") << ".");
uint64_t actionIndex = action.first.actionIndex;
STORM_LOG_THROW(actionIndices.find(actionIndex) == actionIndices.end(), storm::exceptions::WrongFormatException, "Duplication action " << actionInformation.getActionName(actionIndex));
actionIndices.insert(action.first.actionIndex);
bool markovian = action.first.isMarkovian();
ActionIdentification identificationWithoutSynchVector(actionIndex, markovian);
STORM_LOG_THROW(containedActions.find(identificationWithoutSynchVector) == containedActions.end(), storm::exceptions::WrongFormatException, "Duplicate action " << actionInformation.getActionName(actionIndex));
containedActions.insert(identificationWithoutSynchVector);
illegalFragment |= action.second.illegalFragment;
addMissingGlobalVariableIdentities(action.second);
storm::dd::Add<Type, ValueType> actionEncoding = encodeAction(actionIndex != storm::jani::Model::SILENT_ACTION_INDEX ? boost::optional<uint64_t>(actionIndex) : boost::none, this->variables);
storm::dd::Add<Type, ValueType> actionEncoding = encodeAction(actionIndex != storm::jani::Model::SILENT_ACTION_INDEX ? boost::make_optional(actionIndex) : boost::none, this->model.getModelType() == storm::jani::ModelType::MA ? boost::make_optional(markovian) : boost::none, this->variables);
storm::dd::Add<Type, ValueType> missingNondeterminismEncoding = encodeIndex(0, action.second.getHighestLocalNondeterminismVariable(), numberOfUsedNondeterminismVariables - action.second.getHighestLocalNondeterminismVariable(), this->variables);
storm::dd::Add<Type, ValueType> extendedTransitions = actionEncoding * missingNondeterminismEncoding * action.second.transitions;
for (auto const& transientAssignment : action.second.transientEdgeAssignments) {
@ -1645,9 +1667,9 @@ namespace storm {
result += extendedTransitions;
}
return ComposerResult<Type, ValueType>(result, automaton.transientLocationAssignments, transientEdgeAssignments, illegalFragment, numberOfUsedNondeterminismVariables);
} else if (this->model.getModelType() == storm::jani::ModelType::DTMC || this->model.getModelType() == storm::jani::ModelType::CTMC) {
} else if (modelType == storm::jani::ModelType::DTMC || modelType == storm::jani::ModelType::CTMC) {
// Simply add all actions, but make sure to include the missing global variable identities.
storm::dd::Add<Type, ValueType> result = this->variables.manager->template getAddZero<ValueType>();
@ -1689,6 +1711,8 @@ namespace storm {
result = std::make_shared<storm::models::symbolic::Ctmc<Type, ValueType>>(variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates, modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables, variables.rowColumnMetaVariablePairs, modelComponents.labelToExpressionMap, modelComponents.rewardModels);
} else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS) {
result = std::make_shared<storm::models::symbolic::Mdp<Type, ValueType>>(variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates, modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables, variables.rowColumnMetaVariablePairs, variables.allNondeterminismVariables, modelComponents.labelToExpressionMap, modelComponents.rewardModels);
} else if (modelType == storm::jani::ModelType::MA) {
result = std::make_shared<storm::models::symbolic::MarkovAutomaton<Type, ValueType>>(variables.manager, variables.markovMarker, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates, modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables, variables.rowColumnMetaVariablePairs, variables.allNondeterminismVariables, modelComponents.labelToExpressionMap, modelComponents.rewardModels);
} else {
STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Model type '" << modelType << "' not supported.");
}
@ -1805,16 +1829,15 @@ namespace storm {
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 += deadlockStatesAdd * globalIdentity;
} else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS ) {
} else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS || modelType == storm::jani::ModelType::MA) {
// 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);
}
storm::dd::Add<Type, ValueType> action = encodeAction(boost::none, modelType == storm::jani::ModelType::MA ? boost::make_optional(true) : boost::none, variables);
for (auto const& variable : variables.localNondeterminismVariables) {
action *= variables.manager->template getIdentity<ValueType>(variable);
action *= variables.manager->getEncoding(variable, 0).template toAdd<ValueType>();
}
transitionMatrix += deadlockStatesAdd * globalIdentity * action;
}
} else {
@ -1959,7 +1982,7 @@ namespace storm {
// Perform reachability analysis to obtain reachable states.
storm::dd::Bdd<Type> transitionMatrixBdd = system.transitions.notZero();
if (preparedModel.getModelType() == storm::jani::ModelType::MDP || preparedModel.getModelType() == storm::jani::ModelType::LTS) {
if (preparedModel.getModelType() == storm::jani::ModelType::MDP || preparedModel.getModelType() == storm::jani::ModelType::LTS || preparedModel.getModelType() == storm::jani::ModelType::MA) {
transitionMatrixBdd = transitionMatrixBdd.existsAbstract(variables.allNondeterminismVariables);
}
modelComponents.reachableStates = storm::utility::dd::computeReachableStates(modelComponents.initialStates, transitionMatrixBdd, variables.rowMetaVariables, variables.columnMetaVariables);

4
src/storm/models/ModelBase.cpp

@ -18,6 +18,10 @@ namespace storm {
return this->getType() == modelType;
}
bool ModelBase::isNondeterministicModel() const {
return this->isOfType(storm::models::ModelType::Mdp) || this->isOfType(storm::models::ModelType::MarkovAutomaton);
}
bool ModelBase::supportsParameters() const {
return false;
}

7
src/storm/models/ModelBase.h

@ -108,6 +108,13 @@ namespace storm {
*/
bool isOfType(storm::models::ModelType const& modelType) const;
/*!
* Returns true if the model is a nondeterministic model.
*
* @return True iff the model is a nondeterministic model.
*/
bool isNondeterministicModel() const;
/*!
* Checks whether the model supports parameters.
*

128
src/storm/models/symbolic/MarkovAutomaton.cpp

@ -0,0 +1,128 @@
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/storage/dd/DdManager.h"
#include "storm/storage/dd/Add.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/adapters/RationalFunctionAdapter.h"
namespace storm {
namespace models {
namespace symbolic {
template<storm::dd::DdType Type, typename ValueType>
MarkovAutomaton<Type, ValueType>::MarkovAutomaton(std::shared_ptr<storm::dd::DdManager<Type>> manager,
storm::dd::Bdd<Type> markovianMarker,
storm::dd::Bdd<Type> reachableStates,
storm::dd::Bdd<Type> initialStates,
storm::dd::Bdd<Type> deadlockStates,
storm::dd::Add<Type, ValueType> transitionMatrix,
std::set<storm::expressions::Variable> const& rowVariables,
std::shared_ptr<storm::adapters::AddExpressionAdapter<Type, ValueType>> rowExpressionAdapter,
std::set<storm::expressions::Variable> const& columnVariables,
std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& rowColumnMetaVariablePairs,
std::set<storm::expressions::Variable> const& nondeterminismVariables,
std::map<std::string, storm::expressions::Expression> labelToExpressionMap,
std::unordered_map<std::string, RewardModelType> const& rewardModels)
: NondeterministicModel<Type, ValueType>(storm::models::ModelType::MarkovAutomaton, manager, reachableStates, initialStates, deadlockStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, rowColumnMetaVariablePairs, nondeterminismVariables, labelToExpressionMap, rewardModels), markovianMarker(markovianMarker) {
// Compute all Markovian info.
computeMarkovianInfo();
}
template<storm::dd::DdType Type, typename ValueType>
MarkovAutomaton<Type, ValueType>::MarkovAutomaton(std::shared_ptr<storm::dd::DdManager<Type>> manager,
storm::dd::Bdd<Type> markovianMarker,
storm::dd::Bdd<Type> reachableStates,
storm::dd::Bdd<Type> initialStates,
storm::dd::Bdd<Type> deadlockStates,
storm::dd::Add<Type, ValueType> transitionMatrix,
std::set<storm::expressions::Variable> const& rowVariables,
std::set<storm::expressions::Variable> const& columnVariables,
std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& rowColumnMetaVariablePairs,
std::set<storm::expressions::Variable> const& nondeterminismVariables,
std::map<std::string, storm::dd::Bdd<Type>> labelToBddMap,
std::unordered_map<std::string, RewardModelType> const& rewardModels)
: NondeterministicModel<Type, ValueType>(storm::models::ModelType::MarkovAutomaton, manager, reachableStates, initialStates, deadlockStates, transitionMatrix, rowVariables, columnVariables, rowColumnMetaVariablePairs, nondeterminismVariables, labelToBddMap, rewardModels), markovianMarker(markovianMarker) {
// Compute all Markovian info.
computeMarkovianInfo();
}
template<storm::dd::DdType Type, typename ValueType>
void MarkovAutomaton<Type, ValueType>::computeMarkovianInfo() {
// Compute the Markovian choices.
this->markovianChoices = this->getQualitativeTransitionMatrix() && this->markovianMarker;
// Compute the Markovian states.
this->markovianStates = markovianChoices.existsAbstract(this->getNondeterminismVariables());
// Compute the probabilistic states.
std::set<storm::expressions::Variable> columnAndNondeterminsmVariables;
std::set_union(this->getColumnVariables().begin(), this->getColumnVariables().end(), this->getNondeterminismVariables().begin(), this->getNondeterminismVariables().end(), std::inserter(columnAndNondeterminsmVariables, columnAndNondeterminsmVariables.begin()));
this->probabilisticStates = (this->getQualitativeTransitionMatrix() && !markovianMarker).existsAbstract(columnAndNondeterminsmVariables);
// Compute the vector of exit rates.
this->exitRateVector = (this->getTransitionMatrix() * this->markovianMarker.template toAdd<ValueType>()).sumAbstract(columnAndNondeterminsmVariables);
// Modify the transition matrix so all choices are probabilistic and the Markovian choices additionally
// have a rate.
this->transitionMatrix = this->transitionMatrix / this->markovianChoices.ite(this->exitRateVector, this->getManager().template getAddOne<ValueType>());
}
template<storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> const& MarkovAutomaton<Type, ValueType>::getMarkovianMarker() const {
return this->markovianMarker;
}
template<storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> const& MarkovAutomaton<Type, ValueType>::getMarkovianStates() const {
return this->markovianStates;
}
template<storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> const& MarkovAutomaton<Type, ValueType>::getMarkovianChoices() const {
return this->markovianChoices;
}
template<storm::dd::DdType Type, typename ValueType>
storm::dd::Bdd<Type> const& MarkovAutomaton<Type, ValueType>::getProbabilisticStates() const {
return this->markovianStates;
}
template<storm::dd::DdType Type, typename ValueType>
bool MarkovAutomaton<Type, ValueType>::hasHybridStates() const {
return !(this->probabilisticStates && this->markovianStates).isZero();
}
template<storm::dd::DdType Type, typename ValueType>
bool MarkovAutomaton<Type, ValueType>::isClosed() {
return !this->hasHybridStates();
}
template<storm::dd::DdType Type, typename ValueType>
MarkovAutomaton<Type, ValueType> MarkovAutomaton<Type, ValueType>::close() {
// Create the new transition matrix by deleting all Markovian transitions from probabilistic states.
storm::dd::Add<Type, ValueType> newTransitionMatrix = this->probabilisticStates.ite(this->getTransitionMatrix() * (!this->getMarkovianMarker()).template toAdd<ValueType>(), this->getTransitionMatrix());
return MarkovAutomaton<Type, ValueType>(this->getManagerAsSharedPointer(), this->getMarkovianMarker(), this->getReachableStates(), this->getInitialStates(), this->getDeadlockStates(), newTransitionMatrix, this->getRowVariables(), this->getRowExpressionAdapter(), this->getColumnVariables(), this->getRowColumnMetaVariablePairs(), this->getNondeterminismVariables(), this->getLabelToExpressionMap(), this->getRewardModels());
}
template<storm::dd::DdType Type, typename ValueType>
storm::dd::Add<Type, ValueType> const& MarkovAutomaton<Type, ValueType>::getExitRateVector() const {
return this->exitRateVector;
}
// Explicitly instantiate the template class.
template class MarkovAutomaton<storm::dd::DdType::CUDD, double>;
template class MarkovAutomaton<storm::dd::DdType::Sylvan, double>;
template class MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalFunction>;
} // namespace symbolic
} // namespace models
} // namespace storm

111
src/storm/models/symbolic/MarkovAutomaton.h

@ -0,0 +1,111 @@
#pragma once
#include "storm/models/symbolic/NondeterministicModel.h"
namespace storm {
namespace models {
namespace symbolic {
/*!
* This class represents a discrete-time Markov decision process.
*/
template<storm::dd::DdType Type, typename ValueType = double>
class MarkovAutomaton : public NondeterministicModel<Type, ValueType> {
public:
typedef typename NondeterministicModel<Type, ValueType>::RewardModelType RewardModelType;
MarkovAutomaton(MarkovAutomaton<Type, ValueType> const& other) = default;
MarkovAutomaton& operator=(MarkovAutomaton<Type, ValueType> const& other) = default;
MarkovAutomaton(MarkovAutomaton<Type, ValueType>&& other) = default;
MarkovAutomaton& operator=(MarkovAutomaton<Type, ValueType>&& other) = default;
/*!
* Constructs a model from the given data.
*
* @param manager The manager responsible for the decision diagrams.
* @param markovianMarker A DD that can be used to split the Markovian and probabilistic behavior.
* @param reachableStates A DD representing the reachable states.
* @param initialStates A DD representing the initial states of the model.
* @param deadlockStates A DD representing the deadlock states of the model.
* @param transitionMatrix The matrix representing the transitions in the model as a probabilistic matrix.
* @param rowVariables The set of row meta variables used in the DDs.
* @param rowExpressionAdapter An object that can be used to translate expressions in terms of the row
* meta variables.
* @param columVariables The set of column meta variables used in the DDs.
* @param rowColumnMetaVariablePairs All pairs of row/column meta variables.
* @param nondeterminismVariables The meta variables used to encode the nondeterminism in the model.
* @param labelToExpressionMap A mapping from label names to their defining expressions.
* @param rewardModels The reward models associated with the model.
*/
MarkovAutomaton(std::shared_ptr<storm::dd::DdManager<Type>> manager,
storm::dd::Bdd<Type> markovianMarker,
storm::dd::Bdd<Type> reachableStates,
storm::dd::Bdd<Type> initialStates,
storm::dd::Bdd<Type> deadlockStates,
storm::dd::Add<Type, ValueType> transitionMatrix,
std::set<storm::expressions::Variable> const& rowVariables,
std::shared_ptr<storm::adapters::AddExpressionAdapter<Type, ValueType>> rowExpressionAdapter,
std::set<storm::expressions::Variable> const& columnVariables,
std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& rowColumnMetaVariablePairs,
std::set<storm::expressions::Variable> const& nondeterminismVariables,
std::map<std::string, storm::expressions::Expression> labelToExpressionMap = std::map<std::string, storm::expressions::Expression>(),
std::unordered_map<std::string, RewardModelType> const& rewardModels = std::unordered_map<std::string, RewardModelType>());
/*!
* Constructs a model from the given data.
*
* @param manager The manager responsible for the decision diagrams.
* @param markovianMarker A DD that can be used to split the Markovian and probabilistic behavior.
* @param reachableStates A DD representing the reachable states.
* @param initialStates A DD representing the initial states of the model.
* @param deadlockStates A DD representing the deadlock states of the model.
* @param transitionMatrix The matrix representing the transitions in the model as a probabilistic matrix.
* @param rowVariables The set of row meta variables used in the DDs.
* @param columVariables The set of column meta variables used in the DDs.
* @param rowColumnMetaVariablePairs All pairs of row/column meta variables.
* @param nondeterminismVariables The meta variables used to encode the nondeterminism in the model.
* @param labelToBddMap A mapping from label names to their defining BDDs.
* @param rewardModels The reward models associated with the model.
*/
MarkovAutomaton(std::shared_ptr<storm::dd::DdManager<Type>> manager,
storm::dd::Bdd<Type> markovianMarker,
storm::dd::Bdd<Type> reachableStates,
storm::dd::Bdd<Type> initialStates,
storm::dd::Bdd<Type> deadlockStates,
storm::dd::Add<Type, ValueType> transitionMatrix,
std::set<storm::expressions::Variable> const& rowVariables,
std::set<storm::expressions::Variable> const& columnVariables,
std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& rowColumnMetaVariablePairs,
std::set<storm::expressions::Variable> const& nondeterminismVariables,
std::map<std::string, storm::dd::Bdd<Type>> labelToBddMap = std::map<std::string, storm::dd::Bdd<Type>>(),
std::unordered_map<std::string, RewardModelType> const& rewardModels = std::unordered_map<std::string, RewardModelType>());
storm::dd::Bdd<Type> const& getMarkovianMarker() const;
storm::dd::Bdd<Type> const& getMarkovianStates() const;
storm::dd::Bdd<Type> const& getMarkovianChoices() const;
storm::dd::Bdd<Type> const& getProbabilisticStates() const;
bool hasHybridStates() const;
bool isClosed();
MarkovAutomaton<Type, ValueType> close();
storm::dd::Add<Type, ValueType> const& getExitRateVector() const;
private:
/*!
* Computes the member data related to Markovian stuff.
*/
void computeMarkovianInfo();
storm::dd::Bdd<Type> markovianMarker;
storm::dd::Bdd<Type> markovianStates;
storm::dd::Bdd<Type> markovianChoices;
storm::dd::Bdd<Type> probabilisticStates;
storm::dd::Add<Type, ValueType> exitRateVector;
};
} // namespace symbolic
} // namespace models
} // namespace storm

4
src/storm/models/symbolic/Mdp.cpp

@ -28,7 +28,7 @@ namespace storm {
: NondeterministicModel<Type, ValueType>(storm::models::ModelType::Mdp, manager, reachableStates, initialStates, deadlockStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, rowColumnMetaVariablePairs, nondeterminismVariables, labelToExpressionMap, rewardModels) {
// Intentionally left empty.
}
template<storm::dd::DdType Type, typename ValueType>
Mdp<Type, ValueType>::Mdp(std::shared_ptr<storm::dd::DdManager<Type>> manager,
storm::dd::Bdd<Type> reachableStates,
@ -48,7 +48,7 @@ namespace storm {
// Explicitly instantiate the template class.
template class Mdp<storm::dd::DdType::CUDD, double>;
template class Mdp<storm::dd::DdType::Sylvan, double>;
template class Mdp<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class Mdp<storm::dd::DdType::Sylvan, storm::RationalFunction>;

5
src/storm/models/symbolic/Model.cpp

@ -342,6 +342,11 @@ namespace storm {
out << "Variables: \t" << "rows: " << this->rowVariables.size() << " meta variables (" << rowVariableCount << " DD variables)" << ", columns: " << this->columnVariables.size() << " meta variables (" << columnVariableCount << " DD variables)";
}
template<storm::dd::DdType Type, typename ValueType>
std::shared_ptr<storm::adapters::AddExpressionAdapter<Type, ValueType>> const& Model<Type, ValueType>::getRowExpressionAdapter() const {
return this->rowExpressionAdapter;
}
template<storm::dd::DdType Type, typename ValueType>
bool Model<Type, ValueType>::isSymbolicModel() const {
return true;

12
src/storm/models/symbolic/Model.h

@ -371,16 +371,26 @@ namespace storm {
*/
virtual void printDdVariableInformationToStream(std::ostream& out) const;
protected:
/*!
* Retrieves the expression adapter of this model.
*
* @return The expression adapter.
*/
std::shared_ptr<storm::adapters::AddExpressionAdapter<Type, ValueType>> const& getRowExpressionAdapter() const;
private:
// The manager responsible for the decision diagrams.
std::shared_ptr<storm::dd::DdManager<Type>> manager;
// A vector representing the reachable states of the model.
storm::dd::Bdd<Type> reachableStates;
protected:
// A matrix representing transition relation.
storm::dd::Add<Type, ValueType> transitionMatrix;
private:
// The meta variables used to encode the rows of the transition matrix.
std::set<storm::expressions::Variable> rowVariables;

6
src/storm/storage/dd/BisimulationDecomposition.cpp

@ -2,7 +2,7 @@
#include "storm/storage/dd/bisimulation/Partition.h"
#include "storm/storage/dd/bisimulation/PartitionRefiner.h"
#include "storm/storage/dd/bisimulation/MdpPartitionRefiner.h"
#include "storm/storage/dd/bisimulation/NondeterministicModelPartitionRefiner.h"
#include "storm/storage/dd/bisimulation/QuotientExtractor.h"
#include "storm/storage/dd/bisimulation/PartialQuotientExtractor.h"
@ -24,8 +24,8 @@ namespace storm {
template <storm::dd::DdType DdType, typename ValueType>
std::unique_ptr<PartitionRefiner<DdType, ValueType>> createRefiner(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& initialPartition) {
if (model.isOfType(storm::models::ModelType::Mdp)) {
return std::make_unique<MdpPartitionRefiner<DdType, ValueType>>(*model.template as<storm::models::symbolic::Mdp<DdType, ValueType>>(), initialPartition);
if (model.isOfType(storm::models::ModelType::Mdp) || model.isOfType(storm::models::ModelType::MarkovAutomaton)) {
return std::make_unique<NondeterministicModelPartitionRefiner<DdType, ValueType>>(*model.template as<storm::models::symbolic::NondeterministicModel<DdType, ValueType>>(), initialPartition);
} else {
return std::make_unique<PartitionRefiner<DdType, ValueType>>(model, initialPartition);
}

30
src/storm/storage/dd/bisimulation/MdpPartitionRefiner.cpp → src/storm/storage/dd/bisimulation/NondeterministicModelPartitionRefiner.cpp

@ -1,6 +1,6 @@
#include "storm/storage/dd/bisimulation/MdpPartitionRefiner.h"
#include "storm/storage/dd/bisimulation/NondeterministicModelPartitionRefiner.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/models/symbolic/StandardRewardModel.h"
namespace storm {
@ -8,12 +8,18 @@ namespace storm {
namespace bisimulation {
template<storm::dd::DdType DdType, typename ValueType>
MdpPartitionRefiner<DdType, ValueType>::MdpPartitionRefiner(storm::models::symbolic::Mdp<DdType, ValueType> const& mdp, Partition<DdType, ValueType> const& initialStatePartition) : PartitionRefiner<DdType, ValueType>(mdp, initialStatePartition), mdp(mdp), choicePartition(Partition<DdType, ValueType>::createTrivialChoicePartition(mdp, initialStatePartition.getBlockVariables())), stateSignatureRefiner(mdp.getManager(), this->statePartition.getBlockVariable(), mdp.getRowVariables(), mdp.getColumnVariables(), true) {
// Intentionally left empty.
NondeterministicModelPartitionRefiner<DdType, ValueType>::NondeterministicModelPartitionRefiner(storm::models::symbolic::NondeterministicModel<DdType, ValueType> const& model, Partition<DdType, ValueType> const& initialStatePartition) : PartitionRefiner<DdType, ValueType>(model, initialStatePartition), model(model), choicePartition(Partition<DdType, ValueType>::createTrivialChoicePartition(model, initialStatePartition.getBlockVariables())), stateSignatureRefiner(model.getManager(), this->statePartition.getBlockVariable(), model.getRowVariables(), model.getColumnVariables(), true) {
// For Markov automata, we refine the state partition wrt. to their exit rates.
if (model.isOfType(storm::models::ModelType::MarkovAutomaton)) {
STORM_LOG_TRACE("Refining with respect to exit rates.");
auto exitRateVector = this->model.template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>()->getExitRateVector();
this->statePartition = stateSignatureRefiner.refine(this->statePartition, Signature<DdType, ValueType>(exitRateVector));
}
}
template<storm::dd::DdType DdType, typename ValueType>
bool MdpPartitionRefiner<DdType, ValueType>::refine(bisimulation::SignatureMode const& mode) {
bool NondeterministicModelPartitionRefiner<DdType, ValueType>::refine(bisimulation::SignatureMode const& mode) {
// In this procedure, we will
// (1) refine the partition of nondeterministic choices based on the state partition. For this, we use
// the signature computer/refiner of the superclass. These objects use the full transition matrix.
@ -36,7 +42,7 @@ namespace storm {
} else {
choicePartitionAsBdd = this->choicePartition.asAdd().notZero();
}
Signature<DdType, ValueType> stateSignature(choicePartitionAsBdd.existsAbstract(mdp.getNondeterminismVariables()).template toAdd<ValueType>());
Signature<DdType, ValueType> stateSignature(choicePartitionAsBdd.existsAbstract(model.getNondeterminismVariables()).template toAdd<ValueType>());
// If the choice partition changed, refine the state partition.
STORM_LOG_TRACE("Refining state partition.");
@ -53,12 +59,12 @@ namespace storm {
}
template<storm::dd::DdType DdType, typename ValueType>
Partition<DdType, ValueType> const& MdpPartitionRefiner<DdType, ValueType>::getChoicePartition() const {
Partition<DdType, ValueType> const& NondeterministicModelPartitionRefiner<DdType, ValueType>::getChoicePartition() const {
return choicePartition;
}
template<storm::dd::DdType DdType, typename ValueType>
bool MdpPartitionRefiner<DdType, ValueType>::refineWrtStateActionRewards(storm::dd::Add<DdType, ValueType> const& stateActionRewards) {
bool NondeterministicModelPartitionRefiner<DdType, ValueType>::refineWrtStateActionRewards(storm::dd::Add<DdType, ValueType> const& stateActionRewards) {
STORM_LOG_TRACE("Refining with respect to state-action rewards.");
Partition<DdType, ValueType> newChoicePartition = this->signatureRefiner.refine(this->choicePartition, Signature<DdType, ValueType>(stateActionRewards));
if (newChoicePartition == this->choicePartition) {
@ -69,11 +75,11 @@ namespace storm {
}
}
template class MdpPartitionRefiner<storm::dd::DdType::CUDD, double>;
template class NondeterministicModelPartitionRefiner<storm::dd::DdType::CUDD, double>;
template class MdpPartitionRefiner<storm::dd::DdType::Sylvan, double>;
template class MdpPartitionRefiner<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class MdpPartitionRefiner<storm::dd::DdType::Sylvan, storm::RationalFunction>;
template class NondeterministicModelPartitionRefiner<storm::dd::DdType::Sylvan, double>;
template class NondeterministicModelPartitionRefiner<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class NondeterministicModelPartitionRefiner<storm::dd::DdType::Sylvan, storm::RationalFunction>;
}
}

6
src/storm/storage/dd/bisimulation/MdpPartitionRefiner.h → src/storm/storage/dd/bisimulation/NondeterministicModelPartitionRefiner.h

@ -14,9 +14,9 @@ namespace storm {
namespace bisimulation {
template <storm::dd::DdType DdType, typename ValueType>
class MdpPartitionRefiner : public PartitionRefiner<DdType, ValueType> {
class NondeterministicModelPartitionRefiner : public PartitionRefiner<DdType, ValueType> {
public:
MdpPartitionRefiner(storm::models::symbolic::Mdp<DdType, ValueType> const& mdp, Partition<DdType, ValueType> const& initialStatePartition);
NondeterministicModelPartitionRefiner(storm::models::symbolic::NondeterministicModel<DdType, ValueType> const& model, Partition<DdType, ValueType> const& initialStatePartition);
/*!
* Refines the partition.
@ -35,7 +35,7 @@ namespace storm {
virtual bool refineWrtStateActionRewards(storm::dd::Add<DdType, ValueType> const& stateActionRewards) override;
// The model to refine.
storm::models::symbolic::Mdp<DdType, ValueType> const& mdp;
storm::models::symbolic::NondeterministicModel<DdType, ValueType> const& model;
// The choice partition in the refinement process.
Partition<DdType, ValueType> choicePartition;

4
src/storm/storage/dd/bisimulation/PartitionRefiner.cpp

@ -2,6 +2,8 @@
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/storage/dd/DdManager.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/NotSupportedException.h"
@ -10,7 +12,7 @@ namespace storm {
namespace bisimulation {
template <storm::dd::DdType DdType, typename ValueType>
PartitionRefiner<DdType, ValueType>::PartitionRefiner(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& initialStatePartition) : status(Status::Initialized), refinements(0), statePartition(initialStatePartition), signatureComputer(model), signatureRefiner(model.getManager(), statePartition.getBlockVariable(), model.getRowAndNondeterminismVariables(), model.getColumnVariables(), !model.isOfType(storm::models::ModelType::Mdp), model.getNondeterminismVariables()) {
PartitionRefiner<DdType, ValueType>::PartitionRefiner(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& initialStatePartition) : status(Status::Initialized), refinements(0), statePartition(initialStatePartition), signatureComputer(model), signatureRefiner(model.getManager(), statePartition.getBlockVariable(), model.getRowAndNondeterminismVariables(), model.getColumnVariables(), !model.isNondeterministicModel(), model.getNondeterminismVariables()) {
// Intentionally left empty.
}

9
src/storm/storage/dd/bisimulation/QuotientExtractor.cpp

@ -7,6 +7,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Ctmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/models/sparse/Dtmc.h"
@ -919,8 +920,8 @@ namespace storm {
auto modelType = model.getType();
bool useRepresentativesForThisExtraction = this->useRepresentatives;
if (modelType == storm::models::ModelType::Dtmc || modelType == storm::models::ModelType::Ctmc || modelType == storm::models::ModelType::Mdp) {
if (modelType == storm::models::ModelType::Mdp) {
if (modelType == storm::models::ModelType::Dtmc || modelType == storm::models::ModelType::Ctmc || modelType == storm::models::ModelType::Mdp || modelType == storm::models::ModelType::MarkovAutomaton) {
if (modelType == storm::models::ModelType::Mdp || modelType == storm::models::ModelType::MarkovAutomaton) {
STORM_LOG_WARN_COND(!useRepresentativesForThisExtraction, "Using representatives is unsupported for MDPs, falling back to regular extraction.");
useRepresentativesForThisExtraction = false;
}
@ -979,7 +980,7 @@ namespace storm {
quotientTransitionMatrix = quotientTransitionMatrix.multiplyMatrix(partitionAsAdd, model.getRowVariables());
end = std::chrono::high_resolution_clock::now();
// Check quotient matrix for sanity.
if (std::is_same<ValueType, storm::RationalNumber>::value) {
STORM_LOG_ASSERT(quotientTransitionMatrix.greater(storm::utility::one<ValueType>()).isZero(), "Illegal entries in quotient matrix.");
@ -1020,7 +1021,7 @@ namespace storm {
} else if (modelType == storm::models::ModelType::Mdp) {
return std::shared_ptr<storm::models::symbolic::Mdp<DdType, ValueType>>(new storm::models::symbolic::Mdp<DdType, ValueType>(model.getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet, blockPrimeVariableSet, blockMetaVariablePairs, model.getNondeterminismVariables(), preservedLabelBdds, quotientRewardModels));
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Unsupported quotient type.");
return std::shared_ptr<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>(new storm::models::symbolic::MarkovAutomaton<DdType, ValueType>(model.getManager().asSharedPointer(), model. template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>()->getMarkovianMarker(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet, blockPrimeVariableSet, blockMetaVariablePairs, model.getNondeterminismVariables(), preservedLabelBdds, quotientRewardModels));
}
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Cannot extract quotient for this model type.");

2
src/storm/storage/dd/bisimulation/SignatureComputer.cpp

@ -97,7 +97,7 @@ namespace storm {
SignatureMode const& SignatureComputer<DdType, ValueType>::getSignatureMode() const {
return mode;
}
template<storm::dd::DdType DdType, typename ValueType>
Signature<DdType, ValueType> SignatureComputer<DdType, ValueType>::getFullSignature(Partition<DdType, ValueType> const& partition) const {
if (partition.storedAsBdd()) {

2
src/storm/storage/dd/cudd/InternalCuddAdd.cpp

@ -231,7 +231,7 @@ namespace storm {
for (auto const& ddVariable : summationDdVariables) {
summationAdds.push_back(ddVariable.toAdd<ValueType>().getCuddAdd());
}
// return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().TimesPlus(otherMatrix.getCuddAdd(), summationAdds));
// return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().Triangle(otherMatrix.getCuddAdd(), summationAdds));
return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().MatrixMultiply(otherMatrix.getCuddAdd(), summationAdds));

Loading…
Cancel
Save