Browse Source

even closer to make synchronization vectors work with DD-based builder

Former-commit-id: befbb623de [formerly cdf63caac6]
Former-commit-id: 7645ece870
tempestpy_adaptions
dehnert 8 years ago
parent
commit
de6d03b2b6
  1. 171
      src/builder/DdJaniModelBuilder.cpp
  2. 133
      src/storage/jani/CompositionActionInformationVisitor.cpp
  3. 48
      src/storage/jani/CompositionActionInformationVisitor.h

171
src/builder/DdJaniModelBuilder.cpp

@ -10,6 +10,7 @@
#include "src/storage/jani/RenameComposition.h"
#include "src/storage/jani/AutomatonComposition.h"
#include "src/storage/jani/ParallelComposition.h"
#include "src/storage/jani/CompositionActionInformationVisitor.h"
#include "src/storage/dd/Add.h"
#include "src/storage/dd/Bdd.h"
@ -172,7 +173,7 @@ namespace storm {
template <storm::dd::DdType Type, typename ValueType>
class CompositionVariableCreator : public storm::jani::CompositionVisitor {
public:
CompositionVariableCreator(storm::jani::Model const& model) : model(model), automata() {
CompositionVariableCreator(storm::jani::Model const& model, storm::jani::ActionInformation const& actionInformation) : model(model), automata(), actionInformation(actionInformation) {
// Intentionally left empty.
}
@ -180,7 +181,7 @@ namespace storm {
// First, check whether every automaton appears exactly once in the system composition. Simultaneously,
// we determine the set of non-silent actions used by the composition.
automata.clear();
nonSilentActions = boost::any_cast<std::set<std::string>>(this->model.getSystemComposition().accept(*this, boost::none));
this->model.getSystemComposition().accept(*this, boost::none);
STORM_LOG_THROW(automata.size() == this->model.getNumberOfAutomata(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model whose system composition refers to a subset of automata.");
// Then, check that the model does not contain unbounded integer or non-transient real variables.
@ -194,78 +195,38 @@ namespace storm {
}
// Based on this assumption, we create the variables.
return createVariables(nonSilentActions);
return createVariables();
}
boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) override {
auto it = automata.find(composition.getAutomatonName());
STORM_LOG_THROW(it == automata.end(), storm::exceptions::InvalidArgumentException, "Cannot build symbolic model from JANI model whose system composition that refers to the automaton '" << composition.getAutomatonName() << "' multiple times.");
automata.insert(it, composition.getAutomatonName());
// Compute the set of actions used by this automaton.
std::set<uint64_t> actionIndices = model.getAutomaton(composition.getAutomatonName()).getUsedActionIndices();
std::set<std::string> result;
for (auto const& index : actionIndices) {
result.insert(this->model.getAction(index).getName());
}
return result;
return boost::none;
}
boost::any visit(storm::jani::RenameComposition const& composition, boost::any const& data) override {
std::set<std::string> usedActions = boost::any_cast<std::set<std::string>>(composition.getSubcomposition().accept(*this, boost::none));
std::set<std::string> newUsedActions;
for (auto const& action : usedActions) {
auto it = composition.getRenaming().find(action);
if (it != composition.getRenaming().end()) {
if (it->second) {
newUsedActions.insert(it->second.get());
}
} else {
newUsedActions.insert(action);
}
}
return newUsedActions;
boost::any_cast<std::set<std::string>>(composition.getSubcomposition().accept(*this, boost::none));
return boost::none;
}
boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override {
std::vector<std::set<std::string>> subresults;
for (auto const& subcomposition : composition.getSubcompositions()) {
subresults.push_back(boost::any_cast<std::set<std::string>>(subcomposition->accept(*this, boost::none)));
}
// Determine all actions that do not take part in synchronization vectors.
std::set<std::string> result;
for (uint64_t subresultIndex = 0; subresultIndex < subresults.size(); ++subresultIndex) {
std::set<std::string> actionsInSynch;
for (auto const& synchVector : composition.getSynchronizationVectors()) {
actionsInSynch.insert(synchVector.getInput()[subresultIndex]);
}
std::set_difference(subresults[subresultIndex].begin(), subresults[subresultIndex].end(), actionsInSynch.begin(), actionsInSynch.end(), std::inserter(result, result.begin()));
subcomposition->accept(*this, boost::none);
}
// Now add all outputs of synchronization vectors.
for (auto const& synchVector : composition.getSynchronizationVectors()) {
result.insert(synchVector.getOutput());
}
return result;
return boost::none;
}
private:
CompositionVariables<Type, ValueType> createVariables() {
CompositionVariables<Type, ValueType> result;
for (auto const& action : this->model.getActions()) {
if (this->model.getActionIndex(action.getName()) != this->model.getSilentActionIndex()) {
std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable(action.getName());
result.actionVariablesMap[this->model.getActionIndex(action.getName())] = variablePair.first;
result.allNondeterminismVariables.insert(variablePair.first);
}
for (auto const& nonSilentActionIndex : actionInformation.getNonSilentActionIndices()) {
std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable(actionInformation.getActionName(nonSilentActionIndex));
result.actionVariablesMap[nonSilentActionIndex] = variablePair.first;
result.allNondeterminismVariables.insert(variablePair.first);
}
// FIXME: check how many nondeterminism variables we should actually allocate.
uint64_t numberOfNondeterminismVariables = this->model.getNumberOfAutomata();
for (auto const& automaton : this->model.getAutomata()) {
@ -390,7 +351,7 @@ namespace storm {
storm::jani::Model const& model;
std::set<std::string> automata;
std::set<std::string> nonSilentActions;
storm::jani::ActionInformation actionInformation;
};
template <storm::dd::DdType Type, typename ValueType>
@ -1055,29 +1016,29 @@ namespace storm {
// The identity of the automaton's variables.
storm::dd::Add<Type, ValueType> identity;
// The local nondeterminism variables used by this action DD, given as the lowest
// The local nondeterminism variables used by this action DD, given as the lowest and highest variable index.
std::pair<uint64_t, uint64_t> localNondeterminismVariables;
};
CombinedEdgesSystemComposer(storm::jani::Model const& model, CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables) : SystemComposer<Type, ValueType>(model, variables, transientVariables), actionToIndex(model.getActionToIndexMap()) {
CombinedEdgesSystemComposer(storm::jani::Model const& model, storm::jani::ActionInformation const& actionInformation, CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables) : SystemComposer<Type, ValueType>(model, variables, transientVariables), actionInformation(actionInformation) {
// Intentionally left empty.
}
std::unordered_map<std::string, uint64_t> actionToIndex;
storm::jani::ActionInformation const& actionInformation;
ComposerResult<Type, ValueType> compose() override {
std::map<uint_fast64_t, uint_fast64_t> actionIndexToLocalNondeterminismVariableOffset;
for (auto const& action : this->model.getActions()) {
actionIndexToLocalNondeterminismVariableOffset[this->model.getActionIndex(action.getName())] = 0;
std::map<uint64_t, uint64_t> actionIndexToLocalNondeterminismVariableOffset;
for (auto const& actionIndex : actionInformation.getNonSilentActionIndices()) {
actionIndexToLocalNondeterminismVariableOffset[actionIndex] = 0;
}
AutomatonDd globalAutomaton = boost::any_cast<AutomatonDd>(this->model.getSystemComposition().accept(*this, actionIndexToLocalNondeterminismVariableOffset));
return buildSystemFromAutomaton(globalAutomaton);
}
boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) override {
std::map<uint_fast64_t, uint_fast64_t> const& actionIndexToLocalNondeterminismVariableOffset = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
std::map<uint64_t, uint64_t> const& actionIndexToLocalNondeterminismVariableOffset = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
return buildAutomatonDd(composition.getAutomatonName(), actionIndexToLocalNondeterminismVariableOffset);
}
@ -1085,12 +1046,12 @@ namespace storm {
// Build a mapping from indices to indices for the renaming.
std::map<uint64_t, uint64_t> renamingIndexToIndex;
for (auto const& entry : composition.getRenaming()) {
if (this->model.getActionIndex(entry.first) != this->model.getSilentActionIndex()) {
if (actionInformation.getActionIndex(entry.first) != this->model.getSilentActionIndex()) {
// Distinguish the cases where we hide the action or properly rename it.
if (entry.second) {
renamingIndexToIndex.emplace(this->model.getActionIndex(entry.first), this->model.getActionIndex(entry.second.get()));
renamingIndexToIndex.emplace(actionInformation.getActionIndex(entry.first), actionInformation.getActionIndex(entry.second.get()));
} else {
renamingIndexToIndex.emplace(this->model.getActionIndex(entry.first), this->model.getSilentActionIndex());
renamingIndexToIndex.emplace(actionInformation.getActionIndex(entry.first), this->model.getSilentActionIndex());
}
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Renaming composition must not rename the silent action.");
@ -1098,9 +1059,10 @@ namespace storm {
}
// Prepare the new offset mapping.
std::map<uint_fast64_t, uint_fast64_t> const& actionIndexToLocalNondeterminismVariableOffset = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
std::map<uint_fast64_t, uint_fast64_t> newSynchronizingActionToOffsetMap = actionIndexToLocalNondeterminismVariableOffset;
std::map<uint64_t, uint64_t> const& actionIndexToLocalNondeterminismVariableOffset = boost::any_cast<std::map<uint64_t, uint64_t> const&>(data);
std::map<uint64_t, uint64_t> newSynchronizingActionToOffsetMap = actionIndexToLocalNondeterminismVariableOffset;
for (auto const& indexPair : renamingIndexToIndex) {
// FIXME: Check correct? Introduce zero if not contained?
auto it = actionIndexToLocalNondeterminismVariableOffset.find(indexPair.second);
STORM_LOG_THROW(it != actionIndexToLocalNondeterminismVariableOffset.end(), storm::exceptions::InvalidArgumentException, "Invalid action index " << indexPair.second << ".");
newSynchronizingActionToOffsetMap[indexPair.first] = it->second;
@ -1114,38 +1076,52 @@ namespace storm {
}
boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override {
// Build the set of synchronizing action indices.
std::set<uint64_t> synchronizingActionIndices;
for (auto const& entry : composition.getSynchronizationAlphabet()) {
if (this->model.getActionIndex(entry) != this->model.getSilentActionIndex()) {
synchronizingActionIndices.insert(this->model.getActionIndex(entry));
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Parallel composition must not synchronize over silent action.");
}
}
// Then translate the left subcomposition.
AutomatonDd left = boost::any_cast<AutomatonDd>(composition.getLeftSubcomposition().accept(*this, data));
// Prepare the new offset mapping.
std::map<uint64_t, uint64_t> const& actionIndexToLocalNondeterminismVariableOffset = boost::any_cast<std::map<uint64_t, uint64_t> const&>(data);
std::map<uint64_t, uint64_t> newActionIndexToLocalNondeterminismVariableOffset = actionIndexToLocalNondeterminismVariableOffset;
for (auto const& actionIndex : synchronizingActionIndices) {
auto it = left.actionIndexToAction.find(actionIndex);
if (it != left.actionIndexToAction.end()) {
newActionIndexToLocalNondeterminismVariableOffset[actionIndex] = it->second.getHighestLocalNondeterminismVariable();
std::vector<AutomatonDd> subautomata;
for (uint64_t subcompositionIndex = 0; subcompositionIndex < composition.getNumberOfSubcompositions(); ++subcompositionIndex) {
// Prepare the new offset mapping.
std::map<uint64_t, uint64_t> newSynchronizingActionToOffsetMap = actionIndexToLocalNondeterminismVariableOffset;
if (subcompositionIndex == 0) {
for (auto const& synchVector : composition.getSynchronizationVectors()) {
auto it = actionIndexToLocalNondeterminismVariableOffset.find(actionInformation.getActionIndex(synchVector.getOutput()));
STORM_LOG_THROW(it != actionIndexToLocalNondeterminismVariableOffset.end(), storm::exceptions::InvalidArgumentException, "Invalid action " << synchVector.getOutput() << ".");
newSynchronizingActionToOffsetMap[actionInformation.getActionIndex(synchVector.getInput(0))] = it->second;
}
} else {
AutomatonDd const& previousAutomatonDd = subautomata.back();
// Based on the previous results, we need to update the offsets.
for (auto const& synchVector : composition.getSynchronizationVectors()) {
auto it = previousAutomatonDd.actionIndexToAction.find(actionInformation.getActionIndex(synchVector.getInput(subcompositionIndex - 1)));
if (it != previousAutomatonDd.actionIndexToAction.end()) {
newSynchronizingActionToOffsetMap[actionInformation.getActionIndex(synchVector.getInput(subcompositionIndex))] = it->second.getHighestLocalNondeterminismVariable();
} else {
STORM_LOG_ASSERT(false, "Subcomposition does not have action that is mentioned in parallel composition.");
}
}
}
// Build the DD for the next element of the composition wrt. to the current offset mapping.
subautomata.push_back(boost::any_cast<AutomatonDd>(composition.getSubcomposition(subcompositionIndex).accept(*this, newSynchronizingActionToOffsetMap)));
}
// Then translate the right subcomposition.
AutomatonDd right = boost::any_cast<AutomatonDd>(composition.getRightSubcomposition().accept(*this, newActionIndexToLocalNondeterminismVariableOffset));
return composeInParallel(left, right, synchronizingActionIndices);
return composeInParallel(subautomata, composition.getSynchronizationVectors());
}
private:
AutomatonDd composeInParallel(AutomatonDd const& automaton1, AutomatonDd const& automaton2, std::set<uint64_t> const& synchronizingActionIndices) {
AutomatonDd result(automaton1);
AutomatonDd composeInParallel(std::vector<AutomatonDd> const& subautomata, std::vector<storm::jani::SynchronizationVector> const& synchronizationVectors) {
AutomatonDd result(this->variables.manager->template getAddOne<ValueType>());
for (uint64_t automatonIndex = 0; automatonIndex < subautomata.size(); ++automatonIndex) {
AutomatonDd const& subautomaton = subautomata[automatonIndex];
// Construct combined identity.
result.identity *= subautomaton.identity;
addToTransientAssignmentMap(result.transientLocationAssignments, subautomaton.transientLocationAssignments);
}
result.transientLocationAssignments = joinTransientAssignmentMaps(automaton1.transientLocationAssignments, automaton2.transientLocationAssignments);
// Treat all actions of the first automaton.
@ -1958,16 +1934,19 @@ namespace storm {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Model still contains these undefined constants: " << boost::join(strings, ", ") << ".");
}
// Determine the actions that will appear in the parallel composition.
storm::jani::CompositionActionInformationVisitor actionInformationVisitor(model);
storm::jani::ActionInformation actionInformation = actionInformationVisitor.getActionInformation();
// Create all necessary variables.
CompositionVariableCreator<Type, ValueType> variableCreator(model);
CompositionVariableCreator<Type, ValueType> variableCreator(model, actionInformation);
CompositionVariables<Type, ValueType> variables = variableCreator.create();
// Determine which transient assignments need to be considered in the building process.
std::vector<storm::expressions::Variable> rewardVariables = selectRewardVariables<Type, ValueType>(model, options);
// Create a builder to compose and build the model.
// SeparateEdgesSystemComposer<Type, ValueType> composer(model, variables);
CombinedEdgesSystemComposer<Type, ValueType> composer(model, variables, rewardVariables);
CombinedEdgesSystemComposer<Type, ValueType> composer(model, actionInformation, variables, rewardVariables);
ComposerResult<Type, ValueType> system = composer.compose();
// Postprocess the variables in place.
@ -2013,4 +1992,4 @@ namespace storm {
template class DdJaniModelBuilder<storm::dd::DdType::CUDD, double>;
template class DdJaniModelBuilder<storm::dd::DdType::Sylvan, double>;
}
}
}

133
src/storage/jani/CompositionActionInformationVisitor.cpp

@ -0,0 +1,133 @@
#include "src/storage/jani/CompositionActionInformationVisitor.h"
#include "src/storage/jani/Model.h"
#include "src/storage/jani/Compositions.h"
namespace storm {
namespace jani {
ActionInformation::ActionInformation(std::set<uint64_t> const& nonsilentActionIndices, std::map<uint64_t, std::string> const& indexToNameMap, std::map<std::string, uint64_t> const& nameToIndexMap) : nonsilentActionIndices(nonsilentActionIndices), indexToNameMap(indexToNameMap), nameToIndexMap(nameToIndexMap) {
// Intentionally left empty.
}
std::string const& ActionInformation::getActionName(uint64_t index) const {
return indexToNameMap.at(index);
}
uint64_t ActionInformation::getActionIndex(std::string const& name) const {
return nameToIndexMap.at(name);
}
std::set<uint64_t> const& ActionInformation::getNonSilentActionIndices() const {
return nonsilentActionIndices;
}
CompositionActionInformationVisitor::CompositionActionInformationVisitor(storm::jani::Model const& model) : model(model), nextFreeActionIndex(0), nameToIndexMap(), indexToNameMap() {
// Intentionally left empty.
}
ActionInformation CompositionActionInformationVisitor::getActionInformation() {
indexToNameMap.clear();
nameToIndexMap.clear();
// Determine the next free index we can give out to a new action.
for (auto const& action : model.getActions()) {
nextFreeActionIndex = std::max(nextFreeActionIndex, model.getActionIndex(action.getName()));
}
++nextFreeActionIndex;
std::set<uint64_t> nonSilentActionIndices = boost::any_cast<std::set<uint64_t>>(model.getSystemComposition().accept(*this, boost::none));
return ActionInformation(nonSilentActionIndices, indexToNameMap, nameToIndexMap);
}
boost::any CompositionActionInformationVisitor::visit(AutomatonComposition const& composition, boost::any const& data) {
return model.getAutomaton(composition.getAutomatonName()).getUsedActionIndices();
}
boost::any CompositionActionInformationVisitor::visit(RenameComposition const& composition, boost::any const& data) {
std::set<uint64_t> usedActions = boost::any_cast<std::set<uint64_t>>(composition.getSubcomposition().accept(*this, boost::none));
std::set<uint64_t> newUsedActions;
for (auto const& index : usedActions) {
auto renamingIt = composition.getRenaming().find(indexToNameMap.at(index));
if (renamingIt != composition.getRenaming().end()) {
if (renamingIt->second) {
newUsedActions.insert(addOrGetActionIndex(renamingIt->second.get()));
auto actionIndexIt = nameToIndexMap.find(renamingIt->second.get());
if (actionIndexIt != nameToIndexMap.end()) {
newUsedActions.insert(actionIndexIt->second);
} else {
nameToIndexMap[renamingIt->second.get()] = nextFreeActionIndex;
indexToNameMap[nextFreeActionIndex] = renamingIt->second.get();
++nextFreeActionIndex;
}
}
} else {
newUsedActions.insert(index);
}
}
return newUsedActions;
}
boost::any CompositionActionInformationVisitor::visit(ParallelComposition const& composition, boost::any const& data) {
std::vector<std::set<uint64_t>> subresults;
for (auto const& subcomposition : composition.getSubcompositions()) {
subresults.push_back(boost::any_cast<std::set<uint64_t>>(subcomposition->accept(*this, boost::none)));
}
std::set<uint64_t> effectiveSynchronizationVectors;
for (uint64_t index = 0; index < composition.getNumberOfSynchronizationVectors(); ++index) {
effectiveSynchronizationVectors.insert(index);
}
// Determine all actions that do not take part in synchronization vectors.
std::set<uint64_t> result;
for (uint64_t subresultIndex = 0; subresultIndex < subresults.size(); ++subresultIndex) {
std::set<uint64_t> actionsInSynch;
std::set<uint64_t> localEffectiveSynchVectors;
for (uint64_t synchVectorIndex = 0; synchVectorIndex < composition.getNumberOfSynchronizationVectors(); ++synchVectorIndex) {
auto const& synchVector = composition.getSynchronizationVector(synchVectorIndex);
uint64_t synchVectorActionIndex = nameToIndexMap.at(synchVector.getInput(subresultIndex));
actionsInSynch.insert(synchVectorActionIndex);
// If the action of they synchronization vector at this position is one that is actually contained
// in the corresponding subcomposition, the synchronization vector is effective.
if (subresults[subresultIndex].find(synchVectorActionIndex) != subresults[subresultIndex].end()) {
effectiveSynchronizationVectors.insert(synchVectorIndex);
}
}
std::set_difference(subresults[subresultIndex].begin(), subresults[subresultIndex].end(), actionsInSynch.begin(), actionsInSynch.end(), std::inserter(result, result.begin()));
// Intersect the previously effective synchronization vectors with the ones that were derived to be
// effective for the current subcomposition.
std::set<uint64_t> newEffectiveSynchVectors;
std::set_intersection(effectiveSynchronizationVectors.begin(), effectiveSynchronizationVectors.end(), newEffectiveSynchVectors.begin(), newEffectiveSynchVectors.end(), std::inserter(newEffectiveSynchVectors, newEffectiveSynchVectors.begin()));
effectiveSynchronizationVectors = std::move(newEffectiveSynchVectors);
}
// Now add all outputs of synchronization vectors.
for (auto const& synchVector : composition.getSynchronizationVectors()) {
result.insert(addOrGetActionIndex(synchVector.getOutput()));
}
return result;
}
uint64_t CompositionActionInformationVisitor::addOrGetActionIndex(std::string const& name) {
auto it = nameToIndexMap.find(name);
if (it != nameToIndexMap.end()) {
return it->second;
} else {
nameToIndexMap[name] = nextFreeActionIndex;
indexToNameMap[nextFreeActionIndex] = name;
return nextFreeActionIndex++;
}
}
}
}

48
src/storage/jani/CompositionActionInformationVisitor.h

@ -0,0 +1,48 @@
#pragma once
#include <set>
#include <map>
#include "src/storage/jani/CompositionVisitor.h"
namespace storm {
namespace jani {
class Model;
class ActionInformation {
public:
ActionInformation(std::set<uint64_t> const& nonsilentActionIndices, std::map<uint64_t, std::string> const& indexToNameMap, std::map<std::string, uint64_t> const& nameToIndexMap);
std::string const& getActionName(uint64_t index) const;
uint64_t getActionIndex(std::string const& name) const;
std::set<uint64_t> const& getNonSilentActionIndices() const;
private:
std::set<uint64_t> nonsilentActionIndices;
std::map<uint64_t, std::string> indexToNameMap;
std::map<std::string, uint64_t> nameToIndexMap;
};
class CompositionActionInformationVisitor : public CompositionVisitor {
public:
CompositionActionInformationVisitor(storm::jani::Model const& model);
ActionInformation getActionInformation();
virtual boost::any visit(AutomatonComposition const& composition, boost::any const& data) override;
virtual boost::any visit(RenameComposition const& composition, boost::any const& data) override;
virtual boost::any visit(ParallelComposition const& composition, boost::any const& data) override;
private:
uint64_t addOrGetActionIndex(std::string const& name);
storm::jani::Model const& model;
uint64_t nextFreeActionIndex;
std::map<std::string, uint64_t> nameToIndexMap;
std::map<uint64_t, std::string> indexToNameMap;
};
}
}
Loading…
Cancel
Save