|
@ -1078,22 +1078,62 @@ namespace storm { |
|
|
|
|
|
|
|
|
storm::dd::Bdd<Type> inputEnabledGuard = this->variables.manager->getBddOne(); |
|
|
storm::dd::Bdd<Type> inputEnabledGuard = this->variables.manager->getBddOne(); |
|
|
storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddOne<ValueType>(); |
|
|
storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddOne<ValueType>(); |
|
|
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; |
|
|
|
|
|
|
|
|
|
|
|
uint64_t lowestNondeterminismVariable = actions.front().second.get().getLowestLocalNondeterminismVariable(); |
|
|
uint64_t lowestNondeterminismVariable = actions.front().second.get().getLowestLocalNondeterminismVariable(); |
|
|
uint64_t highestNondeterminismVariable = actions.front().second.get().getHighestLocalNondeterminismVariable(); |
|
|
uint64_t highestNondeterminismVariable = actions.front().second.get().getHighestLocalNondeterminismVariable(); |
|
|
|
|
|
|
|
|
|
|
|
bool hasTransientEdgeAssignments = false; |
|
|
|
|
|
for (auto const& actionIndexPair : actions) { |
|
|
|
|
|
auto const& action = actionIndexPair.second.get(); |
|
|
|
|
|
if (!action.transientEdgeAssignments.empty()) { |
|
|
|
|
|
hasTransientEdgeAssignments = true; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
boost::optional<storm::dd::Add<Type, ValueType>> exitRates; |
|
|
|
|
|
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; |
|
|
|
|
|
if (this->model.getModelType() == storm::jani::ModelType::CTMC && hasTransientEdgeAssignments) { |
|
|
|
|
|
// For CTMCs, we need to weigh the transient assignments with the exit rates.
|
|
|
|
|
|
exitRates = this->variables.manager->template getAddOne<ValueType>(); |
|
|
|
|
|
for (auto const& actionIndexPair : actions) { |
|
|
|
|
|
auto const& action = actionIndexPair.second.get(); |
|
|
|
|
|
|
|
|
|
|
|
std::set<storm::expressions::Variable> columnVariablesToAbstract; |
|
|
|
|
|
std::set_intersection(action.transitions.getContainedMetaVariables().begin(), action.transitions.getContainedMetaVariables().end(), this->variables.columnMetaVariables.begin(), this->variables.columnMetaVariables.end(), std::inserter(columnVariablesToAbstract, columnVariablesToAbstract.begin())); |
|
|
|
|
|
auto actionExitRates = action.transitions.sumAbstract(columnVariablesToAbstract); |
|
|
|
|
|
exitRates = exitRates.get() * actionExitRates; |
|
|
|
|
|
|
|
|
|
|
|
if (!action.transientEdgeAssignments.empty()) { |
|
|
|
|
|
for (auto const& entry : action.transientEdgeAssignments) { |
|
|
|
|
|
auto transientEdgeAssignmentIt = transientEdgeAssignments.find(entry.first); |
|
|
|
|
|
if (transientEdgeAssignmentIt != transientEdgeAssignments.end()) { |
|
|
|
|
|
transientEdgeAssignmentIt->second *= entry.second / actionExitRates; |
|
|
|
|
|
} else { |
|
|
|
|
|
transientEdgeAssignments.emplace(entry.first, entry.second / actionExitRates); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else if (hasTransientEdgeAssignments) { |
|
|
|
|
|
// Otherwise, just join the assignments.
|
|
|
|
|
|
for (auto const& actionIndexPair : actions) { |
|
|
|
|
|
auto const& action = actionIndexPair.second.get(); |
|
|
|
|
|
joinTransientAssignmentMapsInPlace(transientEdgeAssignments, action.transientEdgeAssignments); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
storm::dd::Bdd<Type> newIllegalFragment = this->variables.manager->getBddZero(); |
|
|
storm::dd::Bdd<Type> newIllegalFragment = this->variables.manager->getBddZero(); |
|
|
for (auto const& actionIndexPair : actions) { |
|
|
for (auto const& actionIndexPair : actions) { |
|
|
auto componentIndex = actionIndexPair.first; |
|
|
auto componentIndex = actionIndexPair.first; |
|
|
auto const& action = actionIndexPair.second.get(); |
|
|
auto const& action = actionIndexPair.second.get(); |
|
|
|
|
|
|
|
|
if (guardDisjunction) { |
|
|
if (guardDisjunction) { |
|
|
guardDisjunction.get() |= action.guard; |
|
|
guardDisjunction.get() |= action.guard; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
lowestNondeterminismVariable = std::min(lowestNondeterminismVariable, action.getLowestLocalNondeterminismVariable()); |
|
|
lowestNondeterminismVariable = std::min(lowestNondeterminismVariable, action.getLowestLocalNondeterminismVariable()); |
|
|
highestNondeterminismVariable = std::max(highestNondeterminismVariable, action.getHighestLocalNondeterminismVariable()); |
|
|
highestNondeterminismVariable = std::max(highestNondeterminismVariable, action.getHighestLocalNondeterminismVariable()); |
|
|
transientEdgeAssignments = joinTransientAssignmentMaps(transientEdgeAssignments, action.transientEdgeAssignments); |
|
|
|
|
|
|
|
|
|
|
|
if (action.isInputEnabled()) { |
|
|
if (action.isInputEnabled()) { |
|
|
// If the action is input-enabled, we add self-loops to all states.
|
|
|
// If the action is input-enabled, we add self-loops to all states.
|
|
@ -1153,6 +1193,18 @@ namespace storm { |
|
|
// such a combined transition.
|
|
|
// such a combined transition.
|
|
|
illegalFragment &= inputEnabledGuard; |
|
|
illegalFragment &= inputEnabledGuard; |
|
|
|
|
|
|
|
|
|
|
|
storm::dd::Add<Type, ValueType> transientEdgeAssignmentWeights; |
|
|
|
|
|
if (hasTransientEdgeAssignments) { |
|
|
|
|
|
transientEdgeAssignmentWeights = inputEnabledGuard.template toAdd<ValueType>(); |
|
|
|
|
|
if (exitRates) { |
|
|
|
|
|
transientEdgeAssignmentWeights *= exitRates.get(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (auto& entry : transientEdgeAssignments) { |
|
|
|
|
|
entry.second *= transientEdgeAssignmentWeights; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return ActionDd(inputEnabledGuard, transitions * nonSynchronizingIdentity, transientEdgeAssignments, std::make_pair(lowestNondeterminismVariable, highestNondeterminismVariable), globalVariableToWritingFragment, illegalFragment); |
|
|
return ActionDd(inputEnabledGuard, transitions * nonSynchronizingIdentity, transientEdgeAssignments, std::make_pair(lowestNondeterminismVariable, highestNondeterminismVariable), globalVariableToWritingFragment, illegalFragment); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -1223,7 +1275,7 @@ namespace storm { |
|
|
storm::dd::Add<Type, ValueType> nondeterminismEncoding = encodeIndex(actionIndex, highestLocalNondeterminismVariable, numberOfLocalNondeterminismVariables, this->variables); |
|
|
storm::dd::Add<Type, ValueType> nondeterminismEncoding = encodeIndex(actionIndex, highestLocalNondeterminismVariable, numberOfLocalNondeterminismVariables, this->variables); |
|
|
transitions += nondeterminismEncoding * action.transitions; |
|
|
transitions += nondeterminismEncoding * action.transitions; |
|
|
|
|
|
|
|
|
transientEdgeAssignments = joinTransientAssignmentMaps(transientEdgeAssignments, action.transientEdgeAssignments); |
|
|
|
|
|
|
|
|
joinTransientAssignmentMapsInPlace(transientEdgeAssignments, action.transientEdgeAssignments, nondeterminismEncoding); |
|
|
|
|
|
|
|
|
storm::dd::Bdd<Type> nondeterminismEncodingBdd = nondeterminismEncoding.toBdd(); |
|
|
storm::dd::Bdd<Type> nondeterminismEncodingBdd = nondeterminismEncoding.toBdd(); |
|
|
for (auto& entry : action.variableToWritingFragment) { |
|
|
for (auto& entry : action.variableToWritingFragment) { |
|
@ -1319,16 +1371,22 @@ namespace storm { |
|
|
|
|
|
|
|
|
// If the edge has a rate, we multiply it to the DD.
|
|
|
// If the edge has a rate, we multiply it to the DD.
|
|
|
bool isMarkovian = false; |
|
|
bool isMarkovian = false; |
|
|
|
|
|
boost::optional<storm::dd::Add<Type, ValueType>> exitRates; |
|
|
if (edge.hasRate()) { |
|
|
if (edge.hasRate()) { |
|
|
transitions *= this->variables.rowExpressionAdapter->translateExpression(edge.getRate()); |
|
|
|
|
|
|
|
|
exitRates = this->variables.rowExpressionAdapter->translateExpression(edge.getRate()); |
|
|
|
|
|
transitions *= exitRates.get(); |
|
|
isMarkovian = true; |
|
|
isMarkovian = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Finally treat the transient assignments.
|
|
|
// Finally treat the transient assignments.
|
|
|
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; |
|
|
std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments; |
|
|
if (!this->transientVariables.empty()) { |
|
|
if (!this->transientVariables.empty()) { |
|
|
performTransientAssignments(edge.getAssignments().getTransientAssignments(), [this, &transientEdgeAssignments, &sourceLocationAndGuard] (storm::jani::Assignment const& assignment) { |
|
|
|
|
|
transientEdgeAssignments[assignment.getExpressionVariable()] = sourceLocationAndGuard * this->variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression()); |
|
|
|
|
|
|
|
|
performTransientAssignments(edge.getAssignments().getTransientAssignments(), [this, &transientEdgeAssignments, &sourceLocationAndGuard, &exitRates] (storm::jani::Assignment const& assignment) { |
|
|
|
|
|
auto newTransientEdgeAssignments = sourceLocationAndGuard * this->variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression()); |
|
|
|
|
|
if (exitRates) { |
|
|
|
|
|
newTransientEdgeAssignments *= exitRates.get(); |
|
|
|
|
|
} |
|
|
|
|
|
transientEdgeAssignments[assignment.getExpressionVariable()] = newTransientEdgeAssignments; |
|
|
} ); |
|
|
} ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -1355,7 +1413,7 @@ namespace storm { |
|
|
guard |= edge.guard; |
|
|
guard |= edge.guard; |
|
|
transitions += edge.transitions; |
|
|
transitions += edge.transitions; |
|
|
variableToWritingFragment = joinVariableWritingFragmentMaps(variableToWritingFragment, edge.variableToWritingFragment); |
|
|
variableToWritingFragment = joinVariableWritingFragmentMaps(variableToWritingFragment, edge.variableToWritingFragment); |
|
|
transientEdgeAssignments = joinTransientAssignmentMaps(transientEdgeAssignments, edge.transientEdgeAssignments); |
|
|
|
|
|
|
|
|
joinTransientAssignmentMapsInPlace(transientEdgeAssignments, edge.transientEdgeAssignments); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Currently, we can only combine the transient edge assignments if there is no overlap of the guards of the edges.
|
|
|
// Currently, we can only combine the transient edge assignments if there is no overlap of the guards of the edges.
|
|
@ -1426,13 +1484,25 @@ namespace storm { |
|
|
if (resultIt != result.end()) { |
|
|
if (resultIt != result.end()) { |
|
|
resultIt->second += entry.second; |
|
|
resultIt->second += entry.second; |
|
|
} else { |
|
|
} else { |
|
|
result[entry.first] = entry.second; |
|
|
|
|
|
|
|
|
result.emplace(entry); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void joinTransientAssignmentMapsInPlace(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>& target, std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& newTransientAssignments, boost::optional<storm::dd::Add<Type, ValueType>> const& factor = boost::none) { |
|
|
|
|
|
|
|
|
|
|
|
for (auto const& entry : newTransientAssignments) { |
|
|
|
|
|
auto targetIt = target.find(entry.first); |
|
|
|
|
|
if (targetIt != target.end()) { |
|
|
|
|
|
targetIt->second += factor ? factor.get() * entry.second : entry.second; |
|
|
|
|
|
} else { |
|
|
|
|
|
target[entry.first] = factor ? factor.get() * entry.second : entry.second; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
ActionDd combineEdgesToActionDeterministic(std::vector<EdgeDd> const& edgeDds) { |
|
|
ActionDd combineEdgesToActionDeterministic(std::vector<EdgeDd> const& edgeDds) { |
|
|
storm::dd::Bdd<Type> allGuards = this->variables.manager->getBddZero(); |
|
|
storm::dd::Bdd<Type> allGuards = this->variables.manager->getBddZero(); |
|
|
storm::dd::Add<Type, ValueType> allTransitions = this->variables.manager->template getAddZero<ValueType>(); |
|
|
storm::dd::Add<Type, ValueType> allTransitions = this->variables.manager->template getAddZero<ValueType>(); |
|
@ -1922,9 +1992,15 @@ namespace storm { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
template <storm::dd::DdType Type, typename ValueType> |
|
|
template <storm::dd::DdType Type, typename ValueType> |
|
|
std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> buildRewardModels(ComposerResult<Type, ValueType> const& system, std::vector<storm::expressions::Variable> const& rewardVariables) { |
|
|
|
|
|
|
|
|
std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> buildRewardModels(storm::dd::Add<Type, ValueType> const& reachableStates, storm::dd::Add<Type, ValueType> const& transitionMatrix, storm::jani::ModelType const& modelType, CompositionVariables<Type, ValueType> const& variables, ComposerResult<Type, ValueType> const& system, std::vector<storm::expressions::Variable> const& rewardVariables) { |
|
|
std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> result; |
|
|
std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> result; |
|
|
|
|
|
|
|
|
|
|
|
// For CTMCs, we need to scale the state-action rewards with the total exit rates.
|
|
|
|
|
|
boost::optional<storm::dd::Add<Type, ValueType>> exitRates; |
|
|
|
|
|
if (modelType == storm::jani::ModelType::CTMC || modelType == storm::jani::ModelType::DTMC) { |
|
|
|
|
|
exitRates = transitionMatrix.sumAbstract(variables.columnMetaVariables); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (auto const& variable : rewardVariables) { |
|
|
for (auto const& variable : rewardVariables) { |
|
|
boost::optional<storm::dd::Add<Type, ValueType>> stateRewards = boost::none; |
|
|
boost::optional<storm::dd::Add<Type, ValueType>> stateRewards = boost::none; |
|
|
boost::optional<storm::dd::Add<Type, ValueType>> stateActionRewards = boost::none; |
|
|
boost::optional<storm::dd::Add<Type, ValueType>> stateActionRewards = boost::none; |
|
@ -1932,12 +2008,15 @@ namespace storm { |
|
|
|
|
|
|
|
|
auto it = system.transientLocationAssignments.find(variable); |
|
|
auto it = system.transientLocationAssignments.find(variable); |
|
|
if (it != system.transientLocationAssignments.end()) { |
|
|
if (it != system.transientLocationAssignments.end()) { |
|
|
stateRewards = it->second; |
|
|
|
|
|
|
|
|
stateRewards = reachableStates * it->second; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
it = system.transientEdgeAssignments.find(variable); |
|
|
it = system.transientEdgeAssignments.find(variable); |
|
|
if (it != system.transientEdgeAssignments.end()) { |
|
|
if (it != system.transientEdgeAssignments.end()) { |
|
|
stateActionRewards = it->second; |
|
|
|
|
|
|
|
|
stateActionRewards = reachableStates * it->second; |
|
|
|
|
|
if (exitRates) { |
|
|
|
|
|
stateActionRewards.get() = stateActionRewards.get() / exitRates.get(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
result.emplace(variable.getName(), storm::models::symbolic::StandardRewardModel<Type, ValueType>(stateRewards, stateActionRewards, transitionRewards)); |
|
|
result.emplace(variable.getName(), storm::models::symbolic::StandardRewardModel<Type, ValueType>(stateRewards, stateActionRewards, transitionRewards)); |
|
@ -2045,7 +2124,7 @@ namespace storm { |
|
|
modelComponents.deadlockStates = modelComponents.deadlockStates && !terminalStates; |
|
|
modelComponents.deadlockStates = modelComponents.deadlockStates && !terminalStates; |
|
|
|
|
|
|
|
|
// Build the reward models.
|
|
|
// Build the reward models.
|
|
|
modelComponents.rewardModels = buildRewardModels(system, rewardVariables); |
|
|
|
|
|
|
|
|
modelComponents.rewardModels = buildRewardModels(reachableStatesAdd, modelComponents.transitionMatrix, preparedModel.getModelType(), variables, system, rewardVariables); |
|
|
|
|
|
|
|
|
// Build the label to expressions mapping.
|
|
|
// Build the label to expressions mapping.
|
|
|
modelComponents.labelToExpressionMap = buildLabelExpressions(preparedModel, variables, options); |
|
|
modelComponents.labelToExpressionMap = buildLabelExpressions(preparedModel, variables, options); |
|
|