You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

498 lines
30 KiB

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. #include "src/generator/JaniNextStateGenerator.h"
  2. #include "src/models/sparse/StateLabeling.h"
  3. #include "src/storage/expressions/SimpleValuation.h"
  4. #include "src/solver/SmtSolver.h"
  5. #include "src/utility/constants.h"
  6. #include "src/utility/macros.h"
  7. #include "src/utility/solver.h"
  8. #include "src/exceptions/InvalidSettingsException.h"
  9. #include "src/exceptions/WrongFormatException.h"
  10. namespace storm {
  11. namespace generator {
  12. template<typename ValueType, typename StateType>
  13. JaniNextStateGenerator<ValueType, StateType>::JaniNextStateGenerator(storm::jani::Model const& model, NextStateGeneratorOptions const& options) : JaniNextStateGenerator(model.substituteConstants(), options, false) {
  14. // Intentionally left empty.
  15. }
  16. template<typename ValueType, typename StateType>
  17. JaniNextStateGenerator<ValueType, StateType>::JaniNextStateGenerator(storm::jani::Model const& model, NextStateGeneratorOptions const& options, bool flag) : NextStateGenerator<ValueType, StateType>(model.getExpressionManager(), VariableInformation(model), options), model(model) {
  18. STORM_LOG_THROW(model.hasDefaultComposition(), storm::exceptions::WrongFormatException, "The explicit next-state generator currently does not support custom system compositions.");
  19. STORM_LOG_THROW(!this->options.isBuildAllRewardModelsSet() && this->options.getRewardModelNames().empty(), storm::exceptions::InvalidSettingsException, "The explicit next-state generator currently does not support building reward models.");
  20. STORM_LOG_THROW(!this->options.isBuildChoiceLabelsSet(), storm::exceptions::InvalidSettingsException, "JANI next-state generator cannot generate choice labels.");
  21. // If there are terminal states we need to handle, we now need to translate all labels to expressions.
  22. if (this->options.hasTerminalStates()) {
  23. for (auto const& expressionOrLabelAndBool : this->options.getTerminalStates()) {
  24. if (expressionOrLabelAndBool.first.isExpression()) {
  25. this->terminalStates.push_back(std::make_pair(expressionOrLabelAndBool.first.getExpression(), expressionOrLabelAndBool.second));
  26. } else {
  27. STORM_LOG_THROW(expressionOrLabelAndBool.first.getLabel() == "init" || expressionOrLabelAndBool.first.getLabel() == "deadlock", storm::exceptions::InvalidSettingsException, "Terminal states refer to illegal label '" << expressionOrLabelAndBool.first.getLabel() << "'.");
  28. }
  29. }
  30. }
  31. }
  32. template<typename ValueType, typename StateType>
  33. ModelType JaniNextStateGenerator<ValueType, StateType>::getModelType() const {
  34. switch (model.getModelType()) {
  35. case storm::jani::ModelType::DTMC: return ModelType::DTMC;
  36. case storm::jani::ModelType::CTMC: return ModelType::CTMC;
  37. case storm::jani::ModelType::MDP: return ModelType::MDP;
  38. case storm::jani::ModelType::MA: return ModelType::MA;
  39. default:
  40. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Invalid model type.");
  41. }
  42. }
  43. template<typename ValueType, typename StateType>
  44. bool JaniNextStateGenerator<ValueType, StateType>::isDeterministicModel() const {
  45. return model.isDeterministicModel();
  46. }
  47. template<typename ValueType, typename StateType>
  48. bool JaniNextStateGenerator<ValueType, StateType>::isDiscreteTimeModel() const {
  49. return model.isDiscreteTimeModel();
  50. }
  51. template<typename ValueType, typename StateType>
  52. uint64_t JaniNextStateGenerator<ValueType, StateType>::getLocation(CompressedState const& state, LocationVariableInformation const& locationVariable) const {
  53. if (locationVariable.bitWidth == 0) {
  54. return 0;
  55. } else {
  56. return state.getAsInt(locationVariable.bitOffset, locationVariable.bitWidth);
  57. }
  58. }
  59. template<typename ValueType, typename StateType>
  60. void JaniNextStateGenerator<ValueType, StateType>::setLocation(CompressedState& state, LocationVariableInformation const& locationVariable, uint64_t locationIndex) const {
  61. if (locationVariable.bitWidth != 0) {
  62. state.setFromInt(locationVariable.bitOffset, locationVariable.bitWidth, locationIndex);
  63. }
  64. }
  65. template<typename ValueType, typename StateType>
  66. std::vector<uint64_t> JaniNextStateGenerator<ValueType, StateType>::getLocations(CompressedState const& state) const {
  67. std::vector<uint64_t> result(this->variableInformation.locationVariables.size());
  68. auto resultIt = result.begin();
  69. for (auto it = this->variableInformation.locationVariables.begin(), ite = this->variableInformation.locationVariables.end(); it != ite; ++it, ++resultIt) {
  70. *resultIt = getLocation(state, *it);
  71. }
  72. return result;
  73. }
  74. template<typename ValueType, typename StateType>
  75. std::vector<StateType> JaniNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) {
  76. // Prepare an SMT solver to enumerate all initial states.
  77. storm::utility::solver::SmtSolverFactory factory;
  78. std::unique_ptr<storm::solver::SmtSolver> solver = factory.create(model.getExpressionManager());
  79. std::vector<storm::expressions::Expression> rangeExpressions = model.getAllRangeExpressions();
  80. for (auto const& expression : rangeExpressions) {
  81. solver->add(expression);
  82. }
  83. solver->add(model.getInitialStatesExpression(true));
  84. // Proceed as long as the solver can still enumerate initial states.
  85. std::vector<StateType> initialStateIndices;
  86. while (solver->check() == storm::solver::SmtSolver::CheckResult::Sat) {
  87. // Create fresh state.
  88. CompressedState initialState(this->variableInformation.getTotalBitOffset());
  89. // Read variable assignment from the solution of the solver. Also, create an expression we can use to
  90. // prevent the variable assignment from being enumerated again.
  91. storm::expressions::Expression blockingExpression;
  92. std::shared_ptr<storm::solver::SmtSolver::ModelReference> model = solver->getModel();
  93. for (auto const& booleanVariable : this->variableInformation.booleanVariables) {
  94. bool variableValue = model->getBooleanValue(booleanVariable.variable);
  95. storm::expressions::Expression localBlockingExpression = variableValue ? !booleanVariable.variable : booleanVariable.variable;
  96. blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
  97. initialState.set(booleanVariable.bitOffset, variableValue);
  98. }
  99. for (auto const& integerVariable : this->variableInformation.integerVariables) {
  100. int_fast64_t variableValue = model->getIntegerValue(integerVariable.variable);
  101. storm::expressions::Expression localBlockingExpression = integerVariable.variable != model->getManager().integer(variableValue);
  102. blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression;
  103. initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth, static_cast<uint_fast64_t>(variableValue - integerVariable.lowerBound));
  104. }
  105. // Gather iterators to the initial locations of all the automata.
  106. std::vector<std::set<uint64_t>::const_iterator> initialLocationsIterators;
  107. uint64_t currentLocationVariable = 0;
  108. for (auto const& automaton : this->model.getAutomata()) {
  109. initialLocationsIterators.push_back(automaton.getInitialLocationIndices().cbegin());
  110. // Initialize the locations to the first possible combination.
  111. setLocation(initialState, this->variableInformation.locationVariables[currentLocationVariable], *initialLocationsIterators.back());
  112. ++currentLocationVariable;
  113. }
  114. // Now iterate through all combinations of initial locations.
  115. while (true) {
  116. // Register initial state.
  117. StateType id = stateToIdCallback(initialState);
  118. initialStateIndices.push_back(id);
  119. uint64_t index = 0;
  120. for (; index < initialLocationsIterators.size(); ++index) {
  121. ++initialLocationsIterators[index];
  122. if (initialLocationsIterators[index] == this->model.getAutomata()[index].getInitialLocationIndices().cend()) {
  123. initialLocationsIterators[index] = this->model.getAutomata()[index].getInitialLocationIndices().cbegin();
  124. } else {
  125. break;
  126. }
  127. }
  128. // If we are at the end, leave the loop. Otherwise, create the next initial state.
  129. if (index == initialLocationsIterators.size()) {
  130. break;
  131. } else {
  132. for (uint64_t j = 0; j <= index; ++j) {
  133. setLocation(initialState, this->variableInformation.locationVariables[j], *initialLocationsIterators[j]);
  134. }
  135. }
  136. }
  137. // Block the current initial state to search for the next one.
  138. solver->add(blockingExpression);
  139. }
  140. return initialStateIndices;
  141. }
  142. template<typename ValueType, typename StateType>
  143. CompressedState JaniNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::jani::EdgeDestination const& destination) {
  144. CompressedState newState(state);
  145. // NOTE: the following process assumes that the assignments of the destination are ordered in such a way
  146. // that the assignments to boolean variables precede the assignments to all integer variables and that
  147. // within the types, the assignments to variables are ordered (in ascending order) by the expression variables.
  148. // This is guaranteed for JANI models, by sorting the assignments as soon as an edge destination is created.
  149. auto assignmentIt = destination.getAssignments().begin();
  150. auto assignmentIte = destination.getAssignments().end();
  151. // Iterate over all boolean assignments and carry them out.
  152. auto boolIt = this->variableInformation.booleanVariables.begin();
  153. for (; assignmentIt != assignmentIte && assignmentIt->getAssignedExpression().hasBooleanType(); ++assignmentIt) {
  154. while (assignmentIt->getExpressionVariable() != boolIt->variable) {
  155. ++boolIt;
  156. }
  157. newState.set(boolIt->bitOffset, this->evaluator.asBool(assignmentIt->getAssignedExpression()));
  158. }
  159. // Iterate over all integer assignments and carry them out.
  160. auto integerIt = this->variableInformation.integerVariables.begin();
  161. for (; assignmentIt != assignmentIte && assignmentIt->getAssignedExpression().hasIntegerType(); ++assignmentIt) {
  162. while (assignmentIt->getExpressionVariable() != integerIt->variable) {
  163. ++integerIt;
  164. }
  165. int_fast64_t assignedValue = this->evaluator.asInt(assignmentIt->getAssignedExpression());
  166. STORM_LOG_THROW(assignedValue <= integerIt->upperBound, storm::exceptions::WrongFormatException, "The update " << assignmentIt->getExpressionVariable().getName() << " := " << assignmentIt->getAssignedExpression() << " leads to an out-of-bounds value (" << assignedValue << ") for the variable '" << assignmentIt->getExpressionVariable().getName() << "'.");
  167. newState.setFromInt(integerIt->bitOffset, integerIt->bitWidth, assignedValue - integerIt->lowerBound);
  168. STORM_LOG_ASSERT(static_cast<int_fast64_t>(newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth)) + integerIt->lowerBound == assignedValue, "Writing to the bit vector bucket failed (read " << newState.getAsInt(integerIt->bitOffset, integerIt->bitWidth) << " but wrote " << assignedValue << ").");
  169. }
  170. // Check that we processed all assignments.
  171. STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed.");
  172. return newState;
  173. }
  174. template<typename ValueType, typename StateType>
  175. StateBehavior<ValueType, StateType> JaniNextStateGenerator<ValueType, StateType>::expand(StateToIdCallback const& stateToIdCallback) {
  176. // Prepare the result, in case we return early.
  177. StateBehavior<ValueType, StateType> result;
  178. // If a terminal expression was set and we must not expand this state, return now.
  179. if (!this->terminalStates.empty()) {
  180. for (auto const& expressionBool : this->terminalStates) {
  181. if (this->evaluator.asBool(expressionBool.first) == expressionBool.second) {
  182. return result;
  183. }
  184. }
  185. }
  186. // Retrieve the locations from the state.
  187. std::vector<uint64_t> locations = getLocations(*this->state);
  188. // Get all choices for the state.
  189. std::vector<Choice<ValueType>> allChoices = getSilentActionChoices(locations, *this->state, stateToIdCallback);
  190. std::vector<Choice<ValueType>> allLabeledChoices = getNonsilentActionChoices(locations, *this->state, stateToIdCallback);
  191. for (auto& choice : allLabeledChoices) {
  192. allChoices.push_back(std::move(choice));
  193. }
  194. std::size_t totalNumberOfChoices = allChoices.size();
  195. // If there is not a single choice, we return immediately, because the state has no behavior (other than
  196. // the state reward).
  197. if (totalNumberOfChoices == 0) {
  198. return result;
  199. }
  200. // If the model is a deterministic model, we need to fuse the choices into one.
  201. if (this->isDeterministicModel() && totalNumberOfChoices > 1) {
  202. Choice<ValueType> globalChoice;
  203. // Iterate over all choices and combine the probabilities/rates into one choice.
  204. for (auto const& choice : allChoices) {
  205. for (auto const& stateProbabilityPair : choice) {
  206. if (this->isDiscreteTimeModel()) {
  207. globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices);
  208. } else {
  209. globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second);
  210. }
  211. }
  212. if (this->options.isBuildChoiceLabelsSet()) {
  213. globalChoice.addChoiceLabels(choice.getChoiceLabels());
  214. }
  215. }
  216. // Move the newly fused choice in place.
  217. allChoices.clear();
  218. allChoices.push_back(std::move(globalChoice));
  219. }
  220. // Move all remaining choices in place.
  221. for (auto& choice : allChoices) {
  222. result.addChoice(std::move(choice));
  223. }
  224. result.setExpanded();
  225. return result;
  226. }
  227. template<typename ValueType, typename StateType>
  228. std::vector<Choice<ValueType>> JaniNextStateGenerator<ValueType, StateType>::getSilentActionChoices(std::vector<uint64_t> const& locations, CompressedState const& state, StateToIdCallback stateToIdCallback) {
  229. std::vector<Choice<ValueType>> result;
  230. // Iterate over all automata.
  231. uint64_t automatonIndex = 0;
  232. for (auto const& automaton : model.getAutomata()) {
  233. uint64_t location = locations[automatonIndex];
  234. // Iterate over all edges from the source location.
  235. for (auto const& edge : automaton.getEdgesFromLocation(location)) {
  236. // Skip the edge if it is labeled with a non-silent action.
  237. if (edge.getActionIndex() != model.getSilentActionIndex()) {
  238. continue;
  239. }
  240. // Skip the command, if it is not enabled.
  241. if (!this->evaluator.asBool(edge.getGuard())) {
  242. continue;
  243. }
  244. result.push_back(Choice<ValueType>(edge.getActionIndex()));
  245. Choice<ValueType>& choice = result.back();
  246. // Iterate over all updates of the current command.
  247. ValueType probabilitySum = storm::utility::zero<ValueType>();
  248. for (auto const& destination : edge.getDestinations()) {
  249. // Obtain target state index and add it to the list of known states. If it has not yet been
  250. // seen, we also add it to the set of states that have yet to be explored.
  251. StateType stateIndex = stateToIdCallback(applyUpdate(state, destination));
  252. // Update the choice by adding the probability/target state to it.
  253. ValueType probability = this->evaluator.asRational(destination.getProbability());
  254. choice.addProbability(stateIndex, probability);
  255. probabilitySum += probability;
  256. }
  257. // Check that the resulting distribution is in fact a distribution.
  258. STORM_LOG_THROW(!this->isDiscreteTimeModel() || this->comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for edge (actually sum to " << probabilitySum << ").");
  259. }
  260. ++automatonIndex;
  261. }
  262. return result;
  263. }
  264. template<typename ValueType, typename StateType>
  265. std::vector<Choice<ValueType>> JaniNextStateGenerator<ValueType, StateType>::getNonsilentActionChoices(std::vector<uint64_t> const& locations, CompressedState const& state, StateToIdCallback stateToIdCallback) {
  266. std::vector<Choice<ValueType>> result;
  267. for (uint64_t actionIndex : model.getNonsilentActionIndices()) {
  268. std::vector<std::vector<storm::jani::Edge const*>> enabledEdges = getEnabledEdges(locations, actionIndex);
  269. // Only process this action, if there is at least one feasible solution.
  270. if (!enabledEdges.empty()) {
  271. // Check whether a global variable is written multiple times in any combination.
  272. checkGlobalVariableWritesValid(enabledEdges);
  273. std::vector<std::vector<storm::jani::Edge const*>::const_iterator> iteratorList(enabledEdges.size());
  274. // Initialize the list of iterators.
  275. for (size_t i = 0; i < enabledEdges.size(); ++i) {
  276. iteratorList[i] = enabledEdges[i].cbegin();
  277. }
  278. // As long as there is one feasible combination of commands, keep on expanding it.
  279. bool done = false;
  280. while (!done) {
  281. boost::container::flat_map<CompressedState, ValueType>* currentTargetStates = new boost::container::flat_map<CompressedState, ValueType>();
  282. boost::container::flat_map<CompressedState, ValueType>* newTargetStates = new boost::container::flat_map<CompressedState, ValueType>();
  283. currentTargetStates->emplace(state, storm::utility::one<ValueType>());
  284. for (uint_fast64_t i = 0; i < iteratorList.size(); ++i) {
  285. storm::jani::Edge const& edge = **iteratorList[i];
  286. for (auto const& destination : edge.getDestinations()) {
  287. for (auto const& stateProbabilityPair : *currentTargetStates) {
  288. // Compute the new state under the current update and add it to the set of new target states.
  289. CompressedState newTargetState = applyUpdate(stateProbabilityPair.first, destination);
  290. // If the new state was already found as a successor state, update the probability
  291. // and otherwise insert it.
  292. auto targetStateIt = newTargetStates->find(newTargetState);
  293. if (targetStateIt != newTargetStates->end()) {
  294. targetStateIt->second += stateProbabilityPair.second * this->evaluator.asRational(destination.getProbability());
  295. } else {
  296. newTargetStates->emplace(newTargetState, stateProbabilityPair.second * this->evaluator.asRational(destination.getProbability()));
  297. }
  298. }
  299. }
  300. // If there is one more command to come, shift the target states one time step back.
  301. if (i < iteratorList.size() - 1) {
  302. delete currentTargetStates;
  303. currentTargetStates = newTargetStates;
  304. newTargetStates = new boost::container::flat_map<CompressedState, ValueType>();
  305. }
  306. }
  307. // At this point, we applied all commands of the current command combination and newTargetStates
  308. // contains all target states and their respective probabilities. That means we are now ready to
  309. // add the choice to the list of transitions.
  310. result.push_back(Choice<ValueType>(actionIndex));
  311. // Now create the actual distribution.
  312. Choice<ValueType>& choice = result.back();
  313. // Add the probabilities/rates to the newly created choice.
  314. ValueType probabilitySum = storm::utility::zero<ValueType>();
  315. for (auto const& stateProbabilityPair : *newTargetStates) {
  316. StateType actualIndex = stateToIdCallback(stateProbabilityPair.first);
  317. choice.addProbability(actualIndex, stateProbabilityPair.second);
  318. probabilitySum += stateProbabilityPair.second;
  319. }
  320. // Check that the resulting distribution is in fact a distribution.
  321. STORM_LOG_THROW(!this->isDiscreteTimeModel() || !this->comparator.isConstant(probabilitySum) || this->comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ").");
  322. // Dispose of the temporary maps.
  323. delete currentTargetStates;
  324. delete newTargetStates;
  325. // Now, check whether there is one more command combination to consider.
  326. bool movedIterator = false;
  327. for (uint64_t j = 0; !movedIterator && j < iteratorList.size(); ++j) {
  328. ++iteratorList[j];
  329. if (iteratorList[j] != enabledEdges[j].end()) {
  330. movedIterator = true;
  331. } else {
  332. // Reset the iterator to the beginning of the list.
  333. iteratorList[j] = enabledEdges[j].begin();
  334. }
  335. }
  336. done = !movedIterator;
  337. }
  338. }
  339. }
  340. return result;
  341. }
  342. template<typename ValueType, typename StateType>
  343. std::vector<std::vector<storm::jani::Edge const*>> JaniNextStateGenerator<ValueType, StateType>::getEnabledEdges(std::vector<uint64_t> const& locationIndices, uint64_t actionIndex) {
  344. std::vector<std::vector<storm::jani::Edge const*>> result;
  345. // Iterate over all automata.
  346. uint64_t automatonIndex = 0;
  347. for (auto const& automaton : model.getAutomata()) {
  348. // If the automaton has no edge labeled with the given action, we can skip it.
  349. if (!automaton.hasEdgeLabeledWithActionIndex(actionIndex)) {
  350. continue;
  351. }
  352. auto edges = automaton.getEdgesFromLocation(locationIndices[automatonIndex], actionIndex);
  353. // If the automaton contains the action, but there is no edge available labeled with
  354. // this action, we don't have any feasible command combinations.
  355. if (edges.empty()) {
  356. return std::vector<std::vector<storm::jani::Edge const*>>();
  357. }
  358. std::vector<storm::jani::Edge const*> edgePointers;
  359. for (auto const& edge : edges) {
  360. if (this->evaluator.asBool(edge.getGuard())) {
  361. edgePointers.push_back(&edge);
  362. }
  363. }
  364. // If there was no enabled edge although the automaton has some edge with the required action, we must
  365. // not return anything.
  366. if (edgePointers.empty()) {
  367. return std::vector<std::vector<storm::jani::Edge const*>>();
  368. }
  369. result.emplace_back(std::move(edgePointers));
  370. ++automatonIndex;
  371. }
  372. return result;
  373. }
  374. template<typename ValueType, typename StateType>
  375. void JaniNextStateGenerator<ValueType, StateType>::checkGlobalVariableWritesValid(std::vector<std::vector<storm::jani::Edge const*>> const& enabledEdges) const {
  376. std::map<storm::expressions::Variable, uint64_t> writtenGlobalVariables;
  377. for (auto edgeSetIt = enabledEdges.begin(), edgeSetIte = enabledEdges.end(); edgeSetIt != edgeSetIte; ++edgeSetIt) {
  378. for (auto const& edge : *edgeSetIt) {
  379. for (auto const& globalVariable : edge->getWrittenGlobalVariables()) {
  380. auto it = writtenGlobalVariables.find(globalVariable);
  381. auto index = std::distance(enabledEdges.begin(), edgeSetIt);
  382. if (it != writtenGlobalVariables.end()) {
  383. STORM_LOG_THROW(it->second == index, storm::exceptions::WrongFormatException, "Multiple writes to global variable '" << globalVariable.getName() << "' in synchronizing edges.");
  384. } else {
  385. writtenGlobalVariables.emplace(globalVariable, index);
  386. }
  387. }
  388. }
  389. }
  390. }
  391. template<typename ValueType, typename StateType>
  392. std::size_t JaniNextStateGenerator<ValueType, StateType>::getNumberOfRewardModels() const {
  393. return 0;
  394. }
  395. template<typename ValueType, typename StateType>
  396. RewardModelInformation JaniNextStateGenerator<ValueType, StateType>::getRewardModelInformation(uint64_t const& index) const {
  397. STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Cannot retrieve reward model information.");
  398. return RewardModelInformation("", false, false, false);
  399. }
  400. template<typename ValueType, typename StateType>
  401. storm::models::sparse::StateLabeling JaniNextStateGenerator<ValueType, StateType>::label(storm::storage::BitVectorHashMap<StateType> const& states, std::vector<StateType> const& initialStateIndices, std::vector<StateType> const& deadlockStateIndices) {
  402. return NextStateGenerator<ValueType, StateType>::label(states, initialStateIndices, deadlockStateIndices, {});
  403. }
  404. template class JaniNextStateGenerator<double>;
  405. template class JaniNextStateGenerator<storm::RationalNumber>;
  406. template class JaniNextStateGenerator<storm::RationalFunction>;
  407. }
  408. }