462 lines
21 KiB

  1. #include "src/storage/jani/Model.h"
  2. #include "src/storage/expressions/ExpressionManager.h"
  3. #include "src/storage/jani/Compositions.h"
  4. #include "src/storage/jani/CompositionInformationVisitor.h"
  5. #include "src/utility/macros.h"
  6. #include "src/exceptions/WrongFormatException.h"
  7. #include "src/exceptions/InvalidOperationException.h"
  8. #include "src/exceptions/InvalidTypeException.h"
  9. namespace storm {
  10. namespace jani {
  11. static const std::string SILENT_ACTION_NAME = "";
  12. Model::Model() {
  13. // Intentionally left empty.
  14. }
  15. 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) {
  16. // Use the provided manager or create a new one.
  17. if (expressionManager) {
  18. this->expressionManager = expressionManager.get();
  19. } else {
  20. this->expressionManager = std::make_shared<storm::expressions::ExpressionManager>();
  21. }
  22. // Create an initial restriction.
  23. initialStatesRestriction = this->expressionManager->boolean(true);
  24. // Add a prefined action that represents the silent action.
  25. silentActionIndex = addAction(storm::jani::Action(SILENT_ACTION_NAME));
  26. }
  27. storm::expressions::ExpressionManager& Model::getManager() const {
  28. return *expressionManager;
  29. }
  30. uint64_t Model::getJaniVersion() const {
  31. return version;
  32. }
  33. ModelType const& Model::getModelType() const {
  34. return modelType;
  35. }
  36. std::string const& Model::getName() const {
  37. return name;
  38. }
  39. uint64_t Model::addAction(Action const& action) {
  40. auto it = actionToIndex.find(action.getName());
  41. STORM_LOG_THROW(it == actionToIndex.end(), storm::exceptions::WrongFormatException, "Action with name '" << action.getName() << "' already exists");
  42. actionToIndex.emplace(action.getName(), actions.size());
  43. actions.push_back(action);
  44. if (action.getName() != SILENT_ACTION_NAME) {
  45. nonsilentActionIndices.insert(actions.size() - 1);
  46. }
  47. return actions.size() - 1;
  48. }
  49. Action const& Model::getAction(uint64_t index) const {
  50. return actions[index];
  51. }
  52. bool Model::hasAction(std::string const& name) const {
  53. return actionToIndex.find(name) != actionToIndex.end();
  54. }
  55. uint64_t Model::getActionIndex(std::string const& name) const {
  56. auto it = actionToIndex.find(name);
  57. STORM_LOG_THROW(it != actionToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve index of unknown action '" << name << "'.");
  58. return it->second;
  59. }
  60. std::vector<Action> const& Model::getActions() const {
  61. return actions;
  62. }
  63. boost::container::flat_set<uint64_t> const& Model::getNonsilentActionIndices() const {
  64. return nonsilentActionIndices;
  65. }
  66. uint64_t Model::addConstant(Constant const& constant) {
  67. auto it = constantToIndex.find(constant.getName());
  68. STORM_LOG_THROW(it == constantToIndex.end(), storm::exceptions::WrongFormatException, "Cannot add constant with name '" << constant.getName() << "', because a constant with that name already exists.");
  69. constantToIndex.emplace(constant.getName(), constants.size());
  70. constants.push_back(constant);
  71. return constants.size() - 1;
  72. }
  73. bool Model::hasConstant(std::string const& name) const {
  74. return constantToIndex.find(name) != constantToIndex.end();
  75. }
  76. Constant const& Model::getConstant(std::string const& name) const {
  77. auto it = constantToIndex.find(name);
  78. STORM_LOG_THROW(it != constantToIndex.end(), storm::exceptions::WrongFormatException, "Unable to retrieve unknown constant '" << name << "'.");
  79. return constants[it->second];
  80. }
  81. std::vector<Constant> const& Model::getConstants() const {
  82. return constants;
  83. }
  84. std::vector<Constant>& Model::getConstants() {
  85. return constants;
  86. }
  87. Variable const& Model::addVariable(Variable const& variable) {
  88. if (variable.isBooleanVariable()) {
  89. return addVariable(variable.asBooleanVariable());
  90. } else if (variable.isBoundedIntegerVariable()) {
  91. return addVariable(variable.asBoundedIntegerVariable());
  92. } else if (variable.isUnboundedIntegerVariable()) {
  93. return addVariable(variable.asUnboundedIntegerVariable());
  94. } else if (variable.isRealVariable()) {
  95. return addVariable(variable.asRealVariable());
  96. } else {
  97. STORM_LOG_THROW(false, storm::exceptions::InvalidTypeException, "Variable has invalid type.");
  98. }
  99. }
  100. BooleanVariable const& Model::addVariable(BooleanVariable const& variable) {
  101. return globalVariables.addVariable(variable);
  102. }
  103. BoundedIntegerVariable const& Model::addVariable(BoundedIntegerVariable const& variable) {
  104. return globalVariables.addVariable(variable);
  105. }
  106. UnboundedIntegerVariable const& Model::addVariable(UnboundedIntegerVariable const& variable) {
  107. return globalVariables.addVariable(variable);
  108. }
  109. RealVariable const& Model::addVariable(RealVariable const& variable) {
  110. return globalVariables.addVariable(variable);
  111. }
  112. VariableSet& Model::getGlobalVariables() {
  113. return globalVariables;
  114. }
  115. VariableSet const& Model::getGlobalVariables() const {
  116. return globalVariables;
  117. }
  118. bool Model::hasGlobalVariable(std::string const& name) const {
  119. return globalVariables.hasVariable(name);
  120. }
  121. Variable const& Model::getGlobalVariable(std::string const& name) const {
  122. return globalVariables.getVariable(name);
  123. }
  124. bool Model::hasNonGlobalTransientVariable() const {
  125. for (auto const& automaton : automata) {
  126. if (automaton.hasTransientVariable()) {
  127. return true;
  128. }
  129. }
  130. return false;
  131. }
  132. storm::expressions::ExpressionManager& Model::getExpressionManager() {
  133. return *expressionManager;
  134. }
  135. storm::expressions::ExpressionManager const& Model::getExpressionManager() const {
  136. return *expressionManager;
  137. }
  138. uint64_t Model::addAutomaton(Automaton const& automaton) {
  139. auto it = automatonToIndex.find(automaton.getName());
  140. STORM_LOG_THROW(it == automatonToIndex.end(), storm::exceptions::WrongFormatException, "Automaton with name '" << automaton.getName() << "' already exists.");
  141. automatonToIndex.emplace(automaton.getName(), automata.size());
  142. automata.push_back(automaton);
  143. return automata.size() - 1;
  144. }
  145. std::vector<Automaton>& Model::getAutomata() {
  146. return automata;
  147. }
  148. std::vector<Automaton> const& Model::getAutomata() const {
  149. return automata;
  150. }
  151. Automaton& Model::getAutomaton(std::string const& name) {
  152. auto it = automatonToIndex.find(name);
  153. STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'.");
  154. return automata[it->second];
  155. }
  156. Automaton const& Model::getAutomaton(std::string const& name) const {
  157. auto it = automatonToIndex.find(name);
  158. STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'.");
  159. return automata[it->second];
  160. }
  161. uint64_t Model::getAutomatonIndex(std::string const& name) const {
  162. auto it = automatonToIndex.find(name);
  163. STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'.");
  164. return it->second;
  165. }
  166. std::size_t Model::getNumberOfAutomata() const {
  167. return automata.size();
  168. }
  169. std::shared_ptr<Composition> Model::getStandardSystemComposition() const {
  170. std::shared_ptr<Composition> current;
  171. current = std::make_shared<AutomatonComposition>(this->automata.front().getName());
  172. std::set<uint64_t> leftHandActionIndices = this->automata.front().getActionIndices();
  173. for (uint64_t index = 1; index < automata.size(); ++index) {
  174. std::set<uint64_t> newActionIndices = automata[index].getActionIndices();
  175. // Compute the intersection of actions of the left- and right-hand side.
  176. std::set<uint64_t> intersectionActions;
  177. std::set_intersection(leftHandActionIndices.begin(), leftHandActionIndices.end(), newActionIndices.begin(), newActionIndices.end(), std::inserter(intersectionActions, intersectionActions.begin()));
  178. // If the silent action is in the intersection, we remove it since we cannot synchronize over it.
  179. auto it = intersectionActions.find(this->getSilentActionIndex());
  180. if (it != intersectionActions.end()) {
  181. intersectionActions.erase(it);
  182. }
  183. // Then join the actions to reflect the actions of the new left-hand side.
  184. leftHandActionIndices.insert(newActionIndices.begin(), newActionIndices.end());
  185. // Create the set of strings that represents the actions over which to synchronize.
  186. std::set<std::string> intersectionActionNames;
  187. for (auto const& actionIndex : intersectionActions) {
  188. intersectionActionNames.insert(this->getAction(actionIndex).getName());
  189. }
  190. current = std::make_shared<ParallelComposition>(current, std::make_shared<AutomatonComposition>(automata[index].getName()), intersectionActionNames);
  191. }
  192. return current;
  193. }
  194. Composition const& Model::getSystemComposition() const {
  195. return *composition;
  196. }
  197. void Model::setSystemComposition(std::shared_ptr<Composition> const& composition) {
  198. this->composition = composition;
  199. }
  200. std::set<std::string> Model::getActionNames(bool includeSilent) const {
  201. std::set<std::string> result;
  202. for (auto const& entry : actionToIndex) {
  203. if (includeSilent || entry.second != silentActionIndex) {
  204. result.insert(entry.first);
  205. }
  206. }
  207. return result;
  208. }
  209. std::string const& Model::getSilentActionName() const {
  210. return actions[silentActionIndex].getName();
  211. }
  212. uint64_t Model::getSilentActionIndex() const {
  213. return silentActionIndex;
  214. }
  215. Model Model::defineUndefinedConstants(std::map<storm::expressions::Variable, storm::expressions::Expression> const& constantDefinitions) const {
  216. Model result(*this);
  217. std::set<storm::expressions::Variable> definedUndefinedConstants;
  218. for (auto& constant : result.constants) {
  219. // If the constant is already defined, we need to replace the appearances of undefined constants in its
  220. // defining expression
  221. if (constant.isDefined()) {
  222. // Make sure we are not trying to define an already defined constant.
  223. STORM_LOG_THROW(constantDefinitions.find(constant.getExpressionVariable()) == constantDefinitions.end(), storm::exceptions::InvalidOperationException, "Illegally defining already defined constant '" << constant.getName() << "'.");
  224. } else {
  225. auto const& variableExpressionPair = constantDefinitions.find(constant.getExpressionVariable());
  226. if (variableExpressionPair != constantDefinitions.end()) {
  227. // If we need to define it, we add it to the defined constants and assign it the appropriate expression.
  228. definedUndefinedConstants.insert(constant.getExpressionVariable());
  229. // Make sure the type of the constant is correct.
  230. STORM_LOG_THROW(variableExpressionPair->second.getType() == constant.getType(), storm::exceptions::InvalidOperationException, "Illegal type of expression defining constant '" << constant.getName() << "'.");
  231. // Now define the constant.
  232. constant.define(variableExpressionPair->second);
  233. }
  234. }
  235. }
  236. // As a sanity check, we make sure that the given mapping does not contain any definitions for identifiers
  237. // that are not undefined constants.
  238. for (auto const& constantExpressionPair : constantDefinitions) {
  239. STORM_LOG_THROW(definedUndefinedConstants.find(constantExpressionPair.first) != definedUndefinedConstants.end(), storm::exceptions::InvalidOperationException, "Unable to define non-existant constant '" << constantExpressionPair.first.getName() << "'.");
  240. }
  241. return result;
  242. }
  243. bool Model::hasUndefinedConstants() const {
  244. for (auto const& constant : constants) {
  245. if (!constant.isDefined()) {
  246. return true;
  247. }
  248. }
  249. return false;
  250. }
  251. std::vector<std::reference_wrapper<Constant const>> Model::getUndefinedConstants() const {
  252. std::vector<std::reference_wrapper<Constant const>> result;
  253. for (auto const& constant : constants) {
  254. if (!constant.isDefined()) {
  255. result.push_back(constant);
  256. }
  257. }
  258. return result;
  259. }
  260. Model Model::substituteConstants() const {
  261. Model result(*this);
  262. // Gather all defining expressions of constants.
  263. std::map<storm::expressions::Variable, storm::expressions::Expression> constantSubstitution;
  264. for (auto& constant : result.getConstants()) {
  265. if (constant.isDefined()) {
  266. constant.define(constant.getExpression().substitute(constantSubstitution));
  267. constantSubstitution[constant.getExpressionVariable()] = constant.getExpression();
  268. }
  269. }
  270. // Substitute constants in all global variables.
  271. for (auto& variable : result.getGlobalVariables().getBoundedIntegerVariables()) {
  272. variable.substitute(constantSubstitution);
  273. }
  274. // Substitute constants in initial states expression.
  275. result.setInitialStatesRestriction(this->getInitialStatesRestriction().substitute(constantSubstitution));
  276. // Substitute constants in variables of automata and their edges.
  277. for (auto& automaton : result.getAutomata()) {
  278. automaton.substitute(constantSubstitution);
  279. }
  280. return result;
  281. }
  282. std::map<storm::expressions::Variable, storm::expressions::Expression> Model::getConstantsSubstitution() const {
  283. std::map<storm::expressions::Variable, storm::expressions::Expression> result;
  284. for (auto const& constant : constants) {
  285. if (constant.isDefined()) {
  286. result.emplace(constant.getExpressionVariable(), constant.getExpression());
  287. }
  288. }
  289. return result;
  290. }
  291. void Model::setInitialStatesRestriction(storm::expressions::Expression const& initialStatesRestriction) {
  292. this->initialStatesRestriction = initialStatesRestriction;
  293. }
  294. storm::expressions::Expression const& Model::getInitialStatesRestriction() const {
  295. return initialStatesRestriction;
  296. }
  297. storm::expressions::Expression Model::getInitialStatesExpression(bool includeAutomataInitialStatesExpressions) const {
  298. // Start with the restriction of variables.
  299. storm::expressions::Expression result = initialStatesRestriction;
  300. // Then add initial values for those variables that have one.
  301. for (auto const& variable : globalVariables) {
  302. if (variable.hasInitExpression()) {
  303. result = result && (variable.isBooleanVariable() ? storm::expressions::iff(variable.getExpressionVariable(), variable.getInitExpression()) : variable.getExpressionVariable() == variable.getInitExpression());
  304. }
  305. }
  306. // If we are to include the expressions for the automata, do so now.
  307. if (includeAutomataInitialStatesExpressions) {
  308. for (auto const& automaton : automata) {
  309. if (!automaton.getVariables().empty()) {
  310. storm::expressions::Expression automatonInitialStatesExpression = automaton.getInitialStatesExpression();
  311. if (automatonInitialStatesExpression.isInitialized() && !automatonInitialStatesExpression.isTrue()) {
  312. result = result && automatonInitialStatesExpression;
  313. }
  314. }
  315. }
  316. }
  317. return result;
  318. }
  319. bool Model::isDeterministicModel() const {
  320. return this->getModelType() == ModelType::DTMC || this->getModelType() == ModelType::CTMC;
  321. }
  322. bool Model::isDiscreteTimeModel() const {
  323. return this->getModelType() == ModelType::DTMC || this->getModelType() == ModelType::MDP;
  324. }
  325. std::vector<storm::expressions::Expression> Model::getAllRangeExpressions() const {
  326. std::vector<storm::expressions::Expression> result;
  327. for (auto const& variable : this->getGlobalVariables().getBoundedIntegerVariables()) {
  328. result.push_back(variable.getRangeExpression());
  329. }
  330. for (auto const& automaton : automata) {
  331. std::vector<storm::expressions::Expression> automatonRangeExpressions = automaton.getAllRangeExpressions();
  332. result.insert(result.end(), automatonRangeExpressions.begin(), automatonRangeExpressions.end());
  333. }
  334. return result;
  335. }
  336. void Model::finalize() {
  337. for (auto& automaton : getAutomata()) {
  338. automaton.finalize(*this);
  339. }
  340. }
  341. bool Model::checkValidity(bool logdbg) const {
  342. // TODO switch to exception based return value.
  343. if (version == 0) {
  344. if(logdbg) STORM_LOG_DEBUG("Jani version is unspecified");
  345. return false;
  346. }
  347. if(modelType == ModelType::UNDEFINED) {
  348. if(logdbg) STORM_LOG_DEBUG("Model type is unspecified");
  349. return false;
  350. }
  351. if(automata.empty()) {
  352. if(logdbg) STORM_LOG_DEBUG("No automata specified");
  353. return false;
  354. }
  355. // All checks passed.
  356. return true;
  357. }
  358. bool Model::hasDefaultComposition() const {
  359. CompositionInformationVisitor visitor;
  360. CompositionInformation info = visitor.getInformation(this->getSystemComposition(), *this);
  361. if (info.containsRestrictedParallelComposition() || info.containsRenameComposition()) {
  362. return false;
  363. }
  364. for (auto const& multiplicity : info.getAutomatonToMultiplicityMap()) {
  365. if (multiplicity.second > 1) {
  366. return false;
  367. }
  368. }
  369. return true;
  370. }
  371. }
  372. }