463 lines
21 KiB

#include "src/storage/jani/Model.h"
#include "src/storage/expressions/ExpressionManager.h"
#include "src/storage/jani/Compositions.h"
#include "src/storage/jani/CompositionInformationVisitor.h"
#include "src/utility/macros.h"
#include "src/exceptions/WrongFormatException.h"
#include "src/exceptions/InvalidOperationException.h"
#include "src/exceptions/InvalidTypeException.h"
namespace storm {
namespace jani {
static const std::string SILENT_ACTION_NAME = "";
Model::Model() {
// Intentionally left empty.
}
Model::Model(std::string const& name, ModelType const& modelType, uint64_t version, boost::optional<std::shared_ptr<storm::expressions::ExpressionManager>> const& expressionManager) : name(name), modelType(modelType), version(version), composition(nullptr) {
// Use the provided manager or create a new one.
if (expressionManager) {
this->expressionManager = expressionManager.get();
} else {
this->expressionManager = std::make_shared<storm::expressions::ExpressionManager>();
}
// Create an initial restriction.
initialStatesRestriction = this->expressionManager->boolean(true);
// Add a prefined action that represents the silent action.
silentActionIndex = addAction(storm::jani::Action(SILENT_ACTION_NAME));
}
storm::expressions::ExpressionManager& Model::getManager() const {
return *expressionManager;
}
uint64_t Model::getJaniVersion() const {
return version;
}
ModelType const& Model::getModelType() const {
return modelType;
}
std::string const& Model::getName() const {
return name;
}
uint64_t Model::addAction(Action const& action) {
auto it = actionToIndex.find(action.getName());
STORM_LOG_THROW(it == actionToIndex.end(), storm::exceptions::WrongFormatException, "Action with name '" << action.getName() << "' already exists");
actionToIndex.emplace(action.getName(), actions.size());
actions.push_back(action);
if (action.getName() != SILENT_ACTION_NAME) {
nonsilentActionIndices.insert(actions.size() - 1);
}
return actions.size() - 1;
}
Action const& Model::getAction(uint64_t index) const {
return actions[index];
}
bool Model::hasAction(std::string const& name) const {
return actionToIndex.find(name) != actionToIndex.end();
}
uint64_t Model::getActionIndex(std::string const& name) const {
auto it = actionToIndex.find(name);
STORM_LOG_THROW(it != actionToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve index of unknown action '" << name << "'.");
return it->second;
}
std::vector<Action> const& Model::getActions() const {
return actions;
}
boost::container::flat_set<uint64_t> const& Model::getNonsilentActionIndices() const {
return nonsilentActionIndices;
}
uint64_t Model::addConstant(Constant const& constant) {
auto it = constantToIndex.find(constant.getName());
STORM_LOG_THROW(it == constantToIndex.end(), storm::exceptions::WrongFormatException, "Cannot add constant with name '" << constant.getName() << "', because a constant with that name already exists.");
constantToIndex.emplace(constant.getName(), constants.size());
constants.push_back(constant);
return constants.size() - 1;
}
bool Model::hasConstant(std::string const& name) const {
return constantToIndex.find(name) != constantToIndex.end();
}
Constant const& Model::getConstant(std::string const& name) const {
auto it = constantToIndex.find(name);
STORM_LOG_THROW(it != constantToIndex.end(), storm::exceptions::WrongFormatException, "Unable to retrieve unknown constant '" << name << "'.");
return constants[it->second];
}
std::vector<Constant> const& Model::getConstants() const {
return constants;
}
std::vector<Constant>& Model::getConstants() {
return constants;
}
Variable const& Model::addVariable(Variable const& variable) {
if (variable.isBooleanVariable()) {
return addVariable(variable.asBooleanVariable());
} else if (variable.isBoundedIntegerVariable()) {
return addVariable(variable.asBoundedIntegerVariable());
} else if (variable.isUnboundedIntegerVariable()) {
return addVariable(variable.asUnboundedIntegerVariable());
} else if (variable.isRealVariable()) {
return addVariable(variable.asRealVariable());
} else {
STORM_LOG_THROW(false, storm::exceptions::InvalidTypeException, "Variable has invalid type.");
}
}
BooleanVariable const& Model::addVariable(BooleanVariable const& variable) {
return globalVariables.addVariable(variable);
}
BoundedIntegerVariable const& Model::addVariable(BoundedIntegerVariable const& variable) {
return globalVariables.addVariable(variable);
}
UnboundedIntegerVariable const& Model::addVariable(UnboundedIntegerVariable const& variable) {
return globalVariables.addVariable(variable);
}
RealVariable const& Model::addVariable(RealVariable const& variable) {
return globalVariables.addVariable(variable);
}
VariableSet& Model::getGlobalVariables() {
return globalVariables;
}
VariableSet const& Model::getGlobalVariables() const {
return globalVariables;
}
bool Model::hasGlobalVariable(std::string const& name) const {
return globalVariables.hasVariable(name);
}
Variable const& Model::getGlobalVariable(std::string const& name) const {
return globalVariables.getVariable(name);
}
bool Model::hasNonGlobalTransientVariable() const {
for (auto const& automaton : automata) {
if (automaton.hasTransientVariable()) {
return true;
}
}
return false;
}
storm::expressions::ExpressionManager& Model::getExpressionManager() {
return *expressionManager;
}
storm::expressions::ExpressionManager const& Model::getExpressionManager() const {
return *expressionManager;
}
uint64_t Model::addAutomaton(Automaton const& automaton) {
auto it = automatonToIndex.find(automaton.getName());
STORM_LOG_THROW(it == automatonToIndex.end(), storm::exceptions::WrongFormatException, "Automaton with name '" << automaton.getName() << "' already exists.");
automatonToIndex.emplace(automaton.getName(), automata.size());
automata.push_back(automaton);
return automata.size() - 1;
}
std::vector<Automaton>& Model::getAutomata() {
return automata;
}
std::vector<Automaton> const& Model::getAutomata() const {
return automata;
}
Automaton& Model::getAutomaton(std::string const& name) {
auto it = automatonToIndex.find(name);
STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'.");
return automata[it->second];
}
Automaton const& Model::getAutomaton(std::string const& name) const {
auto it = automatonToIndex.find(name);
STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'.");
return automata[it->second];
}
uint64_t Model::getAutomatonIndex(std::string const& name) const {
auto it = automatonToIndex.find(name);
STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'.");
return it->second;
}
std::size_t Model::getNumberOfAutomata() const {
return automata.size();
}
std::shared_ptr<Composition> Model::getStandardSystemComposition() const {
std::shared_ptr<Composition> current;
current = std::make_shared<AutomatonComposition>(this->automata.front().getName());
std::set<uint64_t> leftHandActionIndices = this->automata.front().getActionIndices();
for (uint64_t index = 1; index < automata.size(); ++index) {
std::set<uint64_t> newActionIndices = automata[index].getActionIndices();
// Compute the intersection of actions of the left- and right-hand side.
std::set<uint64_t> intersectionActions;
std::set_intersection(leftHandActionIndices.begin(), leftHandActionIndices.end(), newActionIndices.begin(), newActionIndices.end(), std::inserter(intersectionActions, intersectionActions.begin()));
// If the silent action is in the intersection, we remove it since we cannot synchronize over it.
auto it = intersectionActions.find(this->getSilentActionIndex());
if (it != intersectionActions.end()) {
intersectionActions.erase(it);
}
// Then join the actions to reflect the actions of the new left-hand side.
leftHandActionIndices.insert(newActionIndices.begin(), newActionIndices.end());
// Create the set of strings that represents the actions over which to synchronize.
std::set<std::string> intersectionActionNames;
for (auto const& actionIndex : intersectionActions) {
intersectionActionNames.insert(this->getAction(actionIndex).getName());
}
current = std::make_shared<ParallelComposition>(current, std::make_shared<AutomatonComposition>(automata[index].getName()), intersectionActionNames);
}
return current;
}
Composition const& Model::getSystemComposition() const {
return *composition;
}
void Model::setSystemComposition(std::shared_ptr<Composition> const& composition) {
this->composition = composition;
}
std::set<std::string> Model::getActionNames(bool includeSilent) const {
std::set<std::string> result;
for (auto const& entry : actionToIndex) {
if (includeSilent || entry.second != silentActionIndex) {
result.insert(entry.first);
}
}
return result;
}
std::string const& Model::getSilentActionName() const {
return actions[silentActionIndex].getName();
}
uint64_t Model::getSilentActionIndex() const {
return silentActionIndex;
}
Model Model::defineUndefinedConstants(std::map<storm::expressions::Variable, storm::expressions::Expression> const& constantDefinitions) const {
Model result(*this);
std::set<storm::expressions::Variable> definedUndefinedConstants;
for (auto& constant : result.constants) {
// If the constant is already defined, we need to replace the appearances of undefined constants in its
// defining expression
if (constant.isDefined()) {
// Make sure we are not trying to define an already defined constant.
STORM_LOG_THROW(constantDefinitions.find(constant.getExpressionVariable()) == constantDefinitions.end(), storm::exceptions::InvalidOperationException, "Illegally defining already defined constant '" << constant.getName() << "'.");
} else {
auto const& variableExpressionPair = constantDefinitions.find(constant.getExpressionVariable());
if (variableExpressionPair != constantDefinitions.end()) {
// If we need to define it, we add it to the defined constants and assign it the appropriate expression.
definedUndefinedConstants.insert(constant.getExpressionVariable());
// Make sure the type of the constant is correct.
STORM_LOG_THROW(variableExpressionPair->second.getType() == constant.getType(), storm::exceptions::InvalidOperationException, "Illegal type of expression defining constant '" << constant.getName() << "'.");
// Now define the constant.
constant.define(variableExpressionPair->second);
}
}
}
// As a sanity check, we make sure that the given mapping does not contain any definitions for identifiers
// that are not undefined constants.
for (auto const& constantExpressionPair : constantDefinitions) {
STORM_LOG_THROW(definedUndefinedConstants.find(constantExpressionPair.first) != definedUndefinedConstants.end(), storm::exceptions::InvalidOperationException, "Unable to define non-existant constant '" << constantExpressionPair.first.getName() << "'.");
}
return result;
}
bool Model::hasUndefinedConstants() const {
for (auto const& constant : constants) {
if (!constant.isDefined()) {
return true;
}
}
return false;
}
std::vector<std::reference_wrapper<Constant const>> Model::getUndefinedConstants() const {
std::vector<std::reference_wrapper<Constant const>> result;
for (auto const& constant : constants) {
if (!constant.isDefined()) {
result.push_back(constant);
}
}
return result;
}
Model Model::substituteConstants() const {
Model result(*this);
// Gather all defining expressions of constants.
std::map<storm::expressions::Variable, storm::expressions::Expression> constantSubstitution;
for (auto& constant : result.getConstants()) {
if (constant.isDefined()) {
constant.define(constant.getExpression().substitute(constantSubstitution));
constantSubstitution[constant.getExpressionVariable()] = constant.getExpression();
}
}
// Substitute constants in all global variables.
for (auto& variable : result.getGlobalVariables().getBoundedIntegerVariables()) {
variable.substitute(constantSubstitution);
}
// Substitute constants in initial states expression.
result.setInitialStatesRestriction(this->getInitialStatesRestriction().substitute(constantSubstitution));
// Substitute constants in variables of automata and their edges.
for (auto& automaton : result.getAutomata()) {
automaton.substitute(constantSubstitution);
}
return result;
}
std::map<storm::expressions::Variable, storm::expressions::Expression> Model::getConstantsSubstitution() const {
std::map<storm::expressions::Variable, storm::expressions::Expression> result;
for (auto const& constant : constants) {
if (constant.isDefined()) {
result.emplace(constant.getExpressionVariable(), constant.getExpression());
}
}
return result;
}
void Model::setInitialStatesRestriction(storm::expressions::Expression const& initialStatesRestriction) {
this->initialStatesRestriction = initialStatesRestriction;
}
storm::expressions::Expression const& Model::getInitialStatesRestriction() const {
return initialStatesRestriction;
}
storm::expressions::Expression Model::getInitialStatesExpression(bool includeAutomataInitialStatesExpressions) const {
// Start with the restriction of variables.
storm::expressions::Expression result = initialStatesRestriction;
// Then add initial values for those variables that have one.
for (auto const& variable : globalVariables) {
if (variable.hasInitExpression()) {
result = result && (variable.isBooleanVariable() ? storm::expressions::iff(variable.getExpressionVariable(), variable.getInitExpression()) : variable.getExpressionVariable() == variable.getInitExpression());
}
}
// If we are to include the expressions for the automata, do so now.
if (includeAutomataInitialStatesExpressions) {
for (auto const& automaton : automata) {
if (!automaton.getVariables().empty()) {
storm::expressions::Expression automatonInitialStatesExpression = automaton.getInitialStatesExpression();
if (automatonInitialStatesExpression.isInitialized() && !automatonInitialStatesExpression.isTrue()) {
result = result && automatonInitialStatesExpression;
}
}
}
}
return result;
}
bool Model::isDeterministicModel() const {
return this->getModelType() == ModelType::DTMC || this->getModelType() == ModelType::CTMC;
}
bool Model::isDiscreteTimeModel() const {
return this->getModelType() == ModelType::DTMC || this->getModelType() == ModelType::MDP;
}
std::vector<storm::expressions::Expression> Model::getAllRangeExpressions() const {
std::vector<storm::expressions::Expression> result;
for (auto const& variable : this->getGlobalVariables().getBoundedIntegerVariables()) {
result.push_back(variable.getRangeExpression());
}
for (auto const& automaton : automata) {
std::vector<storm::expressions::Expression> automatonRangeExpressions = automaton.getAllRangeExpressions();
result.insert(result.end(), automatonRangeExpressions.begin(), automatonRangeExpressions.end());
}
return result;
}
void Model::finalize() {
for (auto& automaton : getAutomata()) {
automaton.finalize(*this);
}
}
bool Model::checkValidity(bool logdbg) const {
// TODO switch to exception based return value.
if (version == 0) {
if(logdbg) STORM_LOG_DEBUG("Jani version is unspecified");
return false;
}
if(modelType == ModelType::UNDEFINED) {
if(logdbg) STORM_LOG_DEBUG("Model type is unspecified");
return false;
}
if(automata.empty()) {
if(logdbg) STORM_LOG_DEBUG("No automata specified");
return false;
}
// All checks passed.
return true;
}
bool Model::hasDefaultComposition() const {
CompositionInformationVisitor visitor;
CompositionInformation info = visitor.getInformation(this->getSystemComposition(), *this);
if (info.containsRestrictedParallelComposition() || info.containsRenameComposition()) {
return false;
}
for (auto const& multiplicity : info.getAutomatonToMultiplicityMap()) {
if (multiplicity.second > 1) {
return false;
}
}
return true;
}
}
}