Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DdPrismModelBuilder.cpp
Go to the documentation of this file.
2
3#include <boost/algorithm/string/join.hpp>
4
20#include "storm/utility/dd.h"
21
22namespace storm {
23namespace builder {
24
25template<storm::dd::DdType Type, typename ValueType>
26class ParameterCreator {
27 public:
28 void create(storm::prism::Program const& /*program*/, storm::adapters::AddExpressionAdapter<Type, ValueType>& /*rowExpressionAdapter*/) {
29 // Intentionally left empty: no support for parameters for this data type.
30 }
31
32 std::set<storm::RationalFunctionVariable> const& getParameters() const {
33 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Creating parameters for non-parametric model is not supported.");
34 }
35
36 private:
37};
38
39template<storm::dd::DdType Type>
40class ParameterCreator<Type, storm::RationalFunction> {
41 public:
42 ParameterCreator() : cache(std::make_shared<storm::RawPolynomialCache>()) {
43 // Intentionally left empty.
44 }
45
47 for (auto const& constant : program.getConstants()) {
48 if (!constant.isDefined()) {
49 storm::RationalFunctionVariable carlVariable = storm::createRFVariable(constant.getExpressionVariable().getName());
50 parameters.insert(carlVariable);
51 auto rf = convertVariableToPolynomial(carlVariable);
52 rowExpressionAdapter.setValue(constant.getExpressionVariable(), rf);
53 }
54 }
55 }
56
57 template<typename RationalFunctionType = storm::RationalFunction, typename TP = typename RationalFunctionType::PolyType,
58 carl::EnableIf<carl::needs_cache<TP>> = carl::dummy>
59 RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const& variable) {
60 return RationalFunctionType(typename RationalFunctionType::PolyType(typename RationalFunctionType::PolyType::PolyType(variable), cache));
61 }
62
63 template<typename RationalFunctionType = storm::RationalFunction, typename TP = typename RationalFunctionType::PolyType,
64 carl::DisableIf<carl::needs_cache<TP>> = carl::dummy>
65 RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const& variable) {
66 return RationalFunctionType(variable);
67 }
68
69 std::set<storm::RationalFunctionVariable> const& getParameters() const {
70 return parameters;
71 }
72
73 private:
74 // A mapping from our variables to carl's.
75 std::unordered_map<storm::expressions::Variable, storm::RationalFunctionVariable> variableToVariableMap;
76
77 // The cache that is used in case the underlying type needs a cache.
78 std::shared_ptr<storm::RawPolynomialCache> cache;
79
80 // All created parameters.
81 std::set<storm::RationalFunctionVariable> parameters;
82};
83
84template<storm::dd::DdType Type, typename ValueType>
86 public:
91 variableToRowMetaVariableMap(std::make_shared<std::map<storm::expressions::Variable, storm::expressions::Variable>>()),
92 rowExpressionAdapter(std::make_shared<storm::adapters::AddExpressionAdapter<Type, ValueType>>(manager, variableToRowMetaVariableMap)),
94 variableToColumnMetaVariableMap((std::make_shared<std::map<storm::expressions::Variable, storm::expressions::Variable>>())),
100 parameters() {
101 // Initializes variables and identity DDs.
102 createMetaVariablesAndIdentities();
103
104 // Initialize the parameters (if any).
105 ParameterCreator<Type, ValueType> parameterCreator;
106 parameterCreator.create(this->program, *this->rowExpressionAdapter);
107 if (std::is_same<ValueType, storm::RationalFunction>::value) {
108 this->parameters = parameterCreator.getParameters();
109 }
110 }
111
112 // The program that is currently translated.
114
115 // The manager used to build the decision diagrams.
116 std::shared_ptr<storm::dd::DdManager<Type>> manager;
118 // The meta variables for the row encoding.
119 std::set<storm::expressions::Variable> rowMetaVariables;
120 std::shared_ptr<std::map<storm::expressions::Variable, storm::expressions::Variable>> variableToRowMetaVariableMap;
121 std::shared_ptr<storm::adapters::AddExpressionAdapter<Type, ValueType>> rowExpressionAdapter;
122
123 // The meta variables for the column encoding.
124 std::set<storm::expressions::Variable> columnMetaVariables;
125 std::shared_ptr<std::map<storm::expressions::Variable, storm::expressions::Variable>> variableToColumnMetaVariableMap;
126
127 // All pairs of row/column meta variables.
128 std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> rowColumnMetaVariablePairs;
129
130 // The meta variables used to encode the nondeterminism.
131 std::vector<storm::expressions::Variable> nondeterminismMetaVariables;
132
133 // The meta variables used to encode the synchronization.
134 std::vector<storm::expressions::Variable> synchronizationMetaVariables;
135
136 // A set of all variables used for encoding the nondeterminism (i.e. nondetermism + synchronization
137 // variables). This is handy to abstract from this variable set.
138 std::set<storm::expressions::Variable> allNondeterminismVariables;
139
140 // As set of all variables used for encoding the synchronization.
141 std::set<storm::expressions::Variable> allSynchronizationMetaVariables;
142
143 // DDs representing the identity for each variable.
144 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> variableToIdentityMap;
145
146 // A set of all meta variables that correspond to global variables.
147 std::set<storm::expressions::Variable> allGlobalVariables;
148
149 // DDs representing the identity for each module.
150 std::map<std::string, storm::dd::Add<Type, ValueType>> moduleToIdentityMap;
151
152 // DDs representing the valid ranges of the variables of each module.
153 std::map<std::string, storm::dd::Add<Type, ValueType>> moduleToRangeMap;
154
155 // The parameters appearing in the model.
156 std::set<storm::RationalFunctionVariable> parameters;
157
158 private:
162 void createMetaVariablesAndIdentities() {
163 // Add synchronization variables.
164 for (auto const& actionIndex : program.getSynchronizingActionIndices()) {
165 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(program.getActionName(actionIndex));
166 synchronizationMetaVariables.push_back(variablePair.first);
167 allSynchronizationMetaVariables.insert(variablePair.first);
168 allNondeterminismVariables.insert(variablePair.first);
169 }
170
171 // Add nondeterminism variables (number of modules + number of commands).
172 uint_fast64_t numberOfNondeterminismVariables = program.getModules().size();
173 for (auto const& module : program.getModules()) {
174 numberOfNondeterminismVariables += module.getNumberOfCommands();
175 }
176 for (uint_fast64_t i = 0; i < numberOfNondeterminismVariables; ++i) {
177 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable("nondet" + std::to_string(i));
178 nondeterminismMetaVariables.push_back(variablePair.first);
179 allNondeterminismVariables.insert(variablePair.first);
180 }
181
182 // Create meta variables for global program variables.
183 for (storm::prism::IntegerVariable const& integerVariable : program.getGlobalIntegerVariables()) {
184 int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt();
185 int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt();
186 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(integerVariable.getName(), low, high);
187
188 STORM_LOG_TRACE("Created meta variables for global integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex()
189 << "] and " << variablePair.second.getName() << "["
190 << variablePair.second.getIndex() << "]");
191
192 rowMetaVariables.insert(variablePair.first);
193 variableToRowMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.first);
194
195 columnMetaVariables.insert(variablePair.second);
196 variableToColumnMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.second);
197
198 storm::dd::Bdd<Type> variableIdentity = manager->getIdentity(variablePair.first, variablePair.second);
199 variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>());
200 rowColumnMetaVariablePairs.push_back(variablePair);
201
202 allGlobalVariables.insert(integerVariable.getExpressionVariable());
203 }
204 for (storm::prism::BooleanVariable const& booleanVariable : program.getGlobalBooleanVariables()) {
205 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(booleanVariable.getName());
206
207 STORM_LOG_TRACE("Created meta variables for global boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex()
208 << "] and " << variablePair.second.getName() << "["
209 << variablePair.second.getIndex() << "]");
210
211 rowMetaVariables.insert(variablePair.first);
212 variableToRowMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.first);
213
214 columnMetaVariables.insert(variablePair.second);
215 variableToColumnMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.second);
216
217 storm::dd::Bdd<Type> variableIdentity = manager->getIdentity(variablePair.first, variablePair.second);
218 variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>());
219
220 rowColumnMetaVariablePairs.push_back(variablePair);
221 allGlobalVariables.insert(booleanVariable.getExpressionVariable());
222 }
223
224 // Create meta variables for each of the modules' variables.
225 for (storm::prism::Module const& module : program.getModules()) {
226 storm::dd::Bdd<Type> moduleIdentity = manager->getBddOne();
227 storm::dd::Bdd<Type> moduleRange = manager->getBddOne();
228
229 for (storm::prism::IntegerVariable const& integerVariable : module.getIntegerVariables()) {
230 int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt();
231 int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt();
232 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair =
233 manager->addMetaVariable(integerVariable.getName(), low, high);
234 STORM_LOG_TRACE("Created meta variables for integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex()
235 << "] and " << variablePair.second.getName() << "["
236 << variablePair.second.getIndex() << "]");
237
238 rowMetaVariables.insert(variablePair.first);
239 variableToRowMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.first);
240
241 columnMetaVariables.insert(variablePair.second);
242 variableToColumnMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.second);
243
244 storm::dd::Bdd<Type> variableIdentity = manager->getIdentity(variablePair.first, variablePair.second);
245 variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>());
246 moduleIdentity &= variableIdentity;
247 moduleRange &= manager->getRange(variablePair.first);
248
249 rowColumnMetaVariablePairs.push_back(variablePair);
250 }
251 for (storm::prism::BooleanVariable const& booleanVariable : module.getBooleanVariables()) {
252 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = manager->addMetaVariable(booleanVariable.getName());
253 STORM_LOG_TRACE("Created meta variables for boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex()
254 << "] and " << variablePair.second.getName() << "["
255 << variablePair.second.getIndex() << "]");
256
257 rowMetaVariables.insert(variablePair.first);
258 variableToRowMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.first);
259
260 columnMetaVariables.insert(variablePair.second);
261 variableToColumnMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.second);
262
263 storm::dd::Bdd<Type> variableIdentity = manager->getIdentity(variablePair.first, variablePair.second);
264 variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>());
265 moduleIdentity &= variableIdentity;
266 moduleRange &= manager->getRange(variablePair.first);
267
268 rowColumnMetaVariablePairs.push_back(variablePair);
269 }
270 moduleToIdentityMap[module.getName()] = moduleIdentity.template toAdd<ValueType>();
271 moduleToRangeMap[module.getName()] = moduleRange.template toAdd<ValueType>();
272 }
273 }
274};
275
276template<storm::dd::DdType Type, typename ValueType>
278 public:
279 ModuleComposer(typename DdPrismModelBuilder<Type, ValueType>::GenerationInformation& generationInfo) : generationInfo(generationInfo) {
280 // Intentionally left empty.
281 }
282
283 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram compose(storm::prism::Composition const& composition) {
284 return boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(
285 composition.accept(*this, newSynchronizingActionToOffsetMap()));
286 }
287
288 std::map<uint_fast64_t, uint_fast64_t> newSynchronizingActionToOffsetMap() const {
289 std::map<uint_fast64_t, uint_fast64_t> result;
290 for (auto const& actionIndex : generationInfo.program.getSynchronizingActionIndices()) {
291 result[actionIndex] = 0;
292 }
293 return result;
294 }
295
296 std::map<uint_fast64_t, uint_fast64_t> updateSynchronizingActionToOffsetMap(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram const& sub,
297 std::map<uint_fast64_t, uint_fast64_t> const& oldMapping) const {
298 std::map<uint_fast64_t, uint_fast64_t> result = oldMapping;
299 for (auto const& action : sub.synchronizingActionToDecisionDiagramMap) {
300 result[action.first] = action.second.numberOfUsedNondeterminismVariables;
301 }
302 return result;
303 }
304
305 virtual boost::any visit(storm::prism::ModuleComposition const& composition, boost::any const& data) override {
306 STORM_LOG_TRACE("Translating module '" << composition.getModuleName() << "'.");
307 std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
308
309 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram result = DdPrismModelBuilder<Type, ValueType>::createModuleDecisionDiagram(
310 generationInfo, generationInfo.program.getModule(composition.getModuleName()), synchronizingActionToOffsetMap);
311
312 return result;
313 }
314
315 virtual boost::any visit(storm::prism::RenamingComposition const& composition, boost::any const& data) override {
316 // Create the mapping from action indices to action indices.
317 std::map<uint_fast64_t, uint_fast64_t> renaming;
318 for (auto const& namePair : composition.getActionRenaming()) {
319 STORM_LOG_THROW(generationInfo.program.hasAction(namePair.first), storm::exceptions::InvalidArgumentException,
320 "Composition refers to unknown action '" << namePair.first << "'.");
321 STORM_LOG_THROW(generationInfo.program.hasAction(namePair.second), storm::exceptions::InvalidArgumentException,
322 "Composition refers to unknown action '" << namePair.second << "'.");
323 renaming.emplace(generationInfo.program.getActionIndex(namePair.first), generationInfo.program.getActionIndex(namePair.second));
324 }
325
326 // Prepare the new offset mapping.
327 std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
328 std::map<uint_fast64_t, uint_fast64_t> newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap;
329 for (auto const& indexPair : renaming) {
330 auto it = synchronizingActionToOffsetMap.find(indexPair.second);
331 STORM_LOG_THROW(it != synchronizingActionToOffsetMap.end(), storm::exceptions::InvalidArgumentException,
332 "Invalid action index " << indexPair.second << ".");
333 newSynchronizingActionToOffsetMap[indexPair.first] = it->second;
334 }
335
336 // Then, we translate the subcomposition.
337 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram sub =
338 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(
339 composition.getSubcomposition().accept(*this, newSynchronizingActionToOffsetMap));
340
341 // Perform the renaming and return result.
342 return rename(sub, renaming);
343 }
344
345 virtual boost::any visit(storm::prism::HidingComposition const& composition, boost::any const& data) override {
346 // Create the mapping from action indices to action indices.
347 std::set<uint_fast64_t> actionIndicesToHide;
348 for (auto const& action : composition.getActionsToHide()) {
349 STORM_LOG_THROW(generationInfo.program.hasAction(action), storm::exceptions::InvalidArgumentException,
350 "Composition refers to unknown action '" << action << "'.");
351 actionIndicesToHide.insert(generationInfo.program.getActionIndex(action));
352 }
353
354 // Prepare the new offset mapping.
355 std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
356 std::map<uint_fast64_t, uint_fast64_t> newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap;
357 for (auto const& index : actionIndicesToHide) {
359 }
360
361 // Then, we translate the subcomposition.
362 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram sub =
363 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(
364 composition.getSubcomposition().accept(*this, newSynchronizingActionToOffsetMap));
365
366 // Perform the hiding and return result.
367 hide(sub, actionIndicesToHide);
368 return sub;
369 }
370
371 virtual boost::any visit(storm::prism::SynchronizingParallelComposition const& composition, boost::any const& data) override {
372 // First, we translate the subcompositions.
373 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left =
374 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data));
375
376 // Prepare the new offset mapping.
377 std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
378 std::map<uint_fast64_t, uint_fast64_t> newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap;
379 for (auto const& action : left.synchronizingActionToDecisionDiagramMap) {
380 newSynchronizingActionToOffsetMap[action.first] = action.second.numberOfUsedNondeterminismVariables;
381 }
382
383 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right =
384 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(
385 composition.getRightSubcomposition().accept(*this, newSynchronizingActionToOffsetMap));
386
387 // Then, determine the action indices on which we need to synchronize.
388 std::set<uint_fast64_t> leftSynchronizationActionIndices = left.getSynchronizingActionIndices();
389 std::set<uint_fast64_t> rightSynchronizationActionIndices = right.getSynchronizingActionIndices();
390 std::set<uint_fast64_t> synchronizationActionIndices;
391 std::set_intersection(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(), rightSynchronizationActionIndices.begin(),
392 rightSynchronizationActionIndices.end(), std::inserter(synchronizationActionIndices, synchronizationActionIndices.begin()));
393
394 // Finally, we compose the subcompositions to create the result.
395 composeInParallel(left, right, synchronizationActionIndices);
396 return left;
397 }
398
399 virtual boost::any visit(storm::prism::InterleavingParallelComposition const& composition, boost::any const& data) override {
400 // First, we translate the subcompositions.
401 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left =
402 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data));
403
404 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right =
405 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this, data));
406
407 // Finally, we compose the subcompositions to create the result.
408 composeInParallel(left, right, std::set<uint_fast64_t>());
409 return left;
410 }
411
412 virtual boost::any visit(storm::prism::RestrictedParallelComposition const& composition, boost::any const& data) override {
413 // Construct the synchronizing action indices from the synchronizing action names.
414 std::set<uint_fast64_t> synchronizingActionIndices;
415 for (auto const& action : composition.getSynchronizingActions()) {
416 synchronizingActionIndices.insert(generationInfo.program.getActionIndex(action));
417 }
418
419 // Then, we translate the subcompositions.
420 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left =
421 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data));
422
423 // Prepare the new offset mapping.
424 std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap = boost::any_cast<std::map<uint_fast64_t, uint_fast64_t> const&>(data);
425 std::map<uint_fast64_t, uint_fast64_t> newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap;
426 for (auto const& actionIndex : synchronizingActionIndices) {
427 auto it = left.synchronizingActionToDecisionDiagramMap.find(actionIndex);
428 if (it != left.synchronizingActionToDecisionDiagramMap.end()) {
429 newSynchronizingActionToOffsetMap[actionIndex] = it->second.numberOfUsedNondeterminismVariables;
430 }
431 }
432
433 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right =
434 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(
435 composition.getRightSubcomposition().accept(*this, newSynchronizingActionToOffsetMap));
436
437 std::set<uint_fast64_t> leftSynchronizationActionIndices = left.getSynchronizingActionIndices();
438 bool isContainedInLeft = std::includes(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(),
439 synchronizingActionIndices.begin(), synchronizingActionIndices.end());
440 STORM_LOG_WARN_COND(isContainedInLeft,
441 "Left subcomposition of composition '" << composition << "' does not include all actions over which to synchronize.");
442
443 std::set<uint_fast64_t> rightSynchronizationActionIndices = right.getSynchronizingActionIndices();
444 bool isContainedInRight = std::includes(rightSynchronizationActionIndices.begin(), rightSynchronizationActionIndices.end(),
445 synchronizingActionIndices.begin(), synchronizingActionIndices.end());
446 STORM_LOG_WARN_COND(isContainedInRight,
447 "Right subcomposition of composition '" << composition << "' does not include all actions over which to synchronize.");
448
449 // Finally, we compose the subcompositions to create the result.
450 composeInParallel(left, right, synchronizingActionIndices);
451 return left;
452 }
453
454 private:
459 void hide(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& sub, std::set<uint_fast64_t> const& actionIndicesToHide) const {
460 STORM_LOG_TRACE("Hiding actions.");
461
462 for (auto const& actionIndex : actionIndicesToHide) {
463 auto it = sub.synchronizingActionToDecisionDiagramMap.find(actionIndex);
464 if (it != sub.synchronizingActionToDecisionDiagramMap.end()) {
465 sub.independentAction = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, sub.independentAction, it->second);
466 sub.numberOfUsedNondeterminismVariables =
467 std::max(sub.numberOfUsedNondeterminismVariables, sub.independentAction.numberOfUsedNondeterminismVariables);
468 sub.synchronizingActionToDecisionDiagramMap.erase(it);
469 }
470 }
471 }
472
476 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram rename(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& sub,
477 std::map<uint_fast64_t, uint_fast64_t> const& renaming) const {
478 STORM_LOG_TRACE("Renaming actions.");
479 std::map<uint_fast64_t, typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram> actionIndexToDdMap;
480
481 // Go through all action DDs with a synchronizing label and rename them if they appear in the renaming.
482 for (auto& action : sub.synchronizingActionToDecisionDiagramMap) {
483 auto renamingIt = renaming.find(action.first);
484 if (renamingIt != renaming.end()) {
485 // If the action is to be renamed and an action with the target index already exists, we need
486 // to combine the action DDs.
487 auto itNewActions = actionIndexToDdMap.find(renamingIt->second);
488 if (itNewActions != actionIndexToDdMap.end()) {
489 actionIndexToDdMap[renamingIt->second] =
490 DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, itNewActions->second);
491
492 } else {
493 // In this case, we can simply copy the action over.
494 actionIndexToDdMap[renamingIt->second] = action.second;
495 }
496 } else {
497 // If the action is not to be renamed, we need to copy it over. However, if some other action
498 // was renamed to the very same action name before, we need to combine the transitions.
499 auto itNewActions = actionIndexToDdMap.find(action.first);
500 if (itNewActions != actionIndexToDdMap.end()) {
501 actionIndexToDdMap[action.first] =
502 DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, itNewActions->second);
503 } else {
504 // In this case, we can simply copy the action over.
505 actionIndexToDdMap[action.first] = action.second;
506 }
507 }
508 }
509
510 return typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram(sub.independentAction, actionIndexToDdMap, sub.identity,
511 sub.numberOfUsedNondeterminismVariables);
512 }
513
518 void composeInParallel(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& left,
519 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram& right,
520 std::set<uint_fast64_t> const& synchronizationActionIndices) const {
521 STORM_LOG_TRACE("Composing two modules.");
522
523 // Combine the tau action.
524 uint_fast64_t numberOfUsedNondeterminismVariables = right.independentAction.numberOfUsedNondeterminismVariables;
525 left.independentAction = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, left.independentAction,
526 right.independentAction, left.identity, right.identity);
527 numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, left.independentAction.numberOfUsedNondeterminismVariables);
528
529 // Create an empty action for the case where one of the modules does not have a certain action.
530 typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram emptyAction(*generationInfo.manager);
531
532 // Treat all non-tau actions of the left module.
533 for (auto& action : left.synchronizingActionToDecisionDiagramMap) {
534 // If we need to synchronize over this action index, we try to do so now.
535 if (synchronizationActionIndices.find(action.first) != synchronizationActionIndices.end()) {
536 // If we are to synchronize over an action that does not exist in the second module, the result
537 // is that the synchronization is the empty action.
538 if (!right.hasSynchronizingAction(action.first)) {
539 action.second = emptyAction;
540 } else {
541 // Otherwise, the actions of the modules are synchronized.
542 action.second = DdPrismModelBuilder<Type, ValueType>::combineSynchronizingActions(
543 action.second, right.synchronizingActionToDecisionDiagramMap[action.first]);
544 }
545 } else {
546 // If we don't synchronize over this action, we need to construct the interleaving.
547
548 // If both modules contain the action, we need to mutually multiply the other identity.
549 if (right.hasSynchronizingAction(action.first)) {
550 action.second = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(
551 generationInfo, action.second, right.synchronizingActionToDecisionDiagramMap[action.first], left.identity, right.identity);
552 } else {
553 // If only the first module has this action, we need to use a dummy action decision diagram
554 // for the second module.
555 action.second = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(generationInfo, action.second, emptyAction,
556 left.identity, right.identity);
557 }
558 }
559 numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, action.second.numberOfUsedNondeterminismVariables);
560 }
561
562 // Treat all non-tau actions of the right module.
563 for (auto const& actionIndex : right.getSynchronizingActionIndices()) {
564 // Here, we only need to treat actions that the first module does not have, because we have handled
565 // this case earlier.
566 if (!left.hasSynchronizingAction(actionIndex)) {
567 if (synchronizationActionIndices.find(actionIndex) != synchronizationActionIndices.end()) {
568 // If we are to synchronize over this action that does not exist in the first module, the
569 // result is that the synchronization is the empty action.
570 left.synchronizingActionToDecisionDiagramMap[actionIndex] = emptyAction;
571 } else {
572 // If only the second module has this action, we need to use a dummy action decision diagram
573 // for the first module.
574 left.synchronizingActionToDecisionDiagramMap[actionIndex] = DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(
575 generationInfo, emptyAction, right.synchronizingActionToDecisionDiagramMap[actionIndex], left.identity, right.identity);
576 }
577 }
578 numberOfUsedNondeterminismVariables =
579 std::max(numberOfUsedNondeterminismVariables, left.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables);
580 }
581
582 // Combine identity matrices.
583 left.identity = left.identity * right.identity;
584
585 // Keep track of the number of nondeterminism variables used.
586 left.numberOfUsedNondeterminismVariables = std::max(left.numberOfUsedNondeterminismVariables, numberOfUsedNondeterminismVariables);
587 }
588
589 typename DdPrismModelBuilder<Type, ValueType>::GenerationInformation& generationInfo;
590};
591
592template<storm::dd::DdType Type, typename ValueType>
596
597template<storm::dd::DdType Type, typename ValueType>
602
603template<storm::dd::DdType Type, typename ValueType>
609
610template<storm::dd::DdType Type, typename ValueType>
611DdPrismModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas)
613 for (auto const& formula : formulas) {
614 this->preserveFormula(*formula);
615 }
616 if (formulas.size() == 1) {
617 this->setTerminalStatesFromFormula(*formulas.front());
618 }
619}
620
621template<storm::dd::DdType Type, typename ValueType>
623 // If we already had terminal states, we need to erase them.
624 terminalStates.clear();
625
626 // If we are not required to build all reward models, we determine the reward models we need to build.
628 std::set<std::string> referencedRewardModels = formula.getReferencedRewardModels();
629 rewardModelsToBuild.insert(referencedRewardModels.begin(), referencedRewardModels.end());
630 }
631
632 // Extract all the labels used in the formula.
633 std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>> atomicLabelFormulas = formula.getAtomicLabelFormulas();
634 for (auto const& formula : atomicLabelFormulas) {
635 if (!labelsToBuild) {
636 labelsToBuild = std::set<std::string>();
637 }
638 labelsToBuild.get().insert(formula->getLabel());
639 }
640}
641
642template<storm::dd::DdType Type, typename ValueType>
646
647template<storm::dd::DdType Type, typename ValueType>
648struct DdPrismModelBuilder<Type, ValueType>::SystemResult {
649 SystemResult(storm::dd::Add<Type, ValueType> const& allTransitionsDd, DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram const& globalModule,
650 boost::optional<storm::dd::Add<Type, ValueType>> const& stateActionDd)
652 // Intentionally left empty.
653 }
654
656 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram globalModule;
657 boost::optional<storm::dd::Add<Type, ValueType>> stateActionDd;
658};
659
660template<storm::dd::DdType Type, typename ValueType>
661typename DdPrismModelBuilder<Type, ValueType>::UpdateDecisionDiagram DdPrismModelBuilder<Type, ValueType>::createUpdateDecisionDiagram(
662 GenerationInformation& generationInfo, storm::prism::Module const& module, storm::dd::Add<Type, ValueType> const& guard,
663 storm::prism::Update const& update) {
664 storm::dd::Add<Type, ValueType> updateDd = generationInfo.manager->template getAddOne<ValueType>();
665
666 STORM_LOG_TRACE("Translating update " << update);
667
668 // Iterate over all assignments (boolean and integer) and build the DD for it.
669 std::vector<storm::prism::Assignment> assignments = update.getAssignments();
670 std::set<storm::expressions::Variable> assignedVariables;
671 for (auto const& assignment : assignments) {
672 // Record the variable as being written.
673 STORM_LOG_TRACE("Assigning to variable " << generationInfo.variableToRowMetaVariableMap->at(assignment.getVariable()).getName());
674 assignedVariables.insert(assignment.getVariable());
675
676 // Translate the written variable.
677 auto const& primedMetaVariable = generationInfo.variableToColumnMetaVariableMap->at(assignment.getVariable());
678 storm::dd::Add<Type, ValueType> writtenVariable = generationInfo.manager->template getIdentity<ValueType>(primedMetaVariable);
679
680 // Translate the expression that is being assigned.
681 storm::dd::Add<Type, ValueType> updateExpression = generationInfo.rowExpressionAdapter->translateExpression(assignment.getExpression());
682
683 // Combine the update expression with the guard.
684 storm::dd::Add<Type, ValueType> result = updateExpression * guard;
685
686 // Combine the variable and the assigned expression.
688 result = result.equals(writtenVariable).template toAdd<ValueType>();
689 result *= guard;
690
691 // Restrict the transitions to the range of the written variable.
692 result = result * generationInfo.manager->getRange(primedMetaVariable).template toAdd<ValueType>();
693
694 updateDd *= result;
695 }
696
697 // Compute the set of assigned global variables.
698 std::set<storm::expressions::Variable> assignedGlobalVariables;
699 std::set_intersection(assignedVariables.begin(), assignedVariables.end(), generationInfo.allGlobalVariables.begin(),
700 generationInfo.allGlobalVariables.end(), std::inserter(assignedGlobalVariables, assignedGlobalVariables.begin()));
701
702 // All unassigned boolean variables need to keep their value.
703 for (storm::prism::BooleanVariable const& booleanVariable : module.getBooleanVariables()) {
704 if (assignedVariables.find(booleanVariable.getExpressionVariable()) == assignedVariables.end()) {
705 STORM_LOG_TRACE("Multiplying identity of variable " << booleanVariable.getName());
706 updateDd *= generationInfo.variableToIdentityMap.at(booleanVariable.getExpressionVariable());
707 }
708 }
709
710 // All unassigned integer variables need to keep their value.
711 for (storm::prism::IntegerVariable const& integerVariable : module.getIntegerVariables()) {
712 if (assignedVariables.find(integerVariable.getExpressionVariable()) == assignedVariables.end()) {
713 STORM_LOG_TRACE("Multiplying identity of variable " << integerVariable.getName());
714 updateDd *= generationInfo.variableToIdentityMap.at(integerVariable.getExpressionVariable());
715 }
716 }
717
718 return UpdateDecisionDiagram(updateDd, assignedGlobalVariables);
719}
720
721template<storm::dd::DdType Type, typename ValueType>
722typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::createCommandDecisionDiagram(
723 GenerationInformation& generationInfo, storm::prism::Module const& module, storm::prism::Command const& command) {
724 STORM_LOG_TRACE("Translating guard " << command.getGuardExpression());
725 storm::dd::Bdd<Type> guard = generationInfo.rowExpressionAdapter->translateBooleanExpression(command.getGuardExpression()) &&
726 generationInfo.moduleToRangeMap[module.getName()].notZero();
727 STORM_LOG_WARN_COND(!guard.isZero(), "The guard '" << command.getGuardExpression() << "' is unsatisfiable.");
728
729 if (!guard.isZero()) {
730 // Create the DDs representing the individual updates.
731 std::vector<UpdateDecisionDiagram> updateResults;
732 for (storm::prism::Update const& update : command.getUpdates()) {
733 updateResults.push_back(createUpdateDecisionDiagram(generationInfo, module, guard.template toAdd<ValueType>(), update));
734
735 STORM_LOG_WARN_COND(!updateResults.back().updateDd.isZero(), "Update '" << update << "' does not have any effect.");
736 }
737
738 // Start by gathering all variables that were written in at least one update.
739 std::set<storm::expressions::Variable> globalVariablesInSomeUpdate;
740
741 // If the command is labeled, we have to analyze which portion of the global variables was written by
742 // any of the updates and make all update results equal w.r.t. this set. If the command is not labeled,
743 // we can already multiply the identities of all global variables.
744 if (command.isLabeled()) {
745 std::for_each(updateResults.begin(), updateResults.end(), [&globalVariablesInSomeUpdate](UpdateDecisionDiagram const& update) {
746 globalVariablesInSomeUpdate.insert(update.assignedGlobalVariables.begin(), update.assignedGlobalVariables.end());
747 });
748 } else {
749 globalVariablesInSomeUpdate = generationInfo.allGlobalVariables;
750 }
751
752 // Then, multiply the missing identities.
753 for (auto& updateResult : updateResults) {
754 std::set<storm::expressions::Variable> missingIdentities;
755 std::set_difference(globalVariablesInSomeUpdate.begin(), globalVariablesInSomeUpdate.end(), updateResult.assignedGlobalVariables.begin(),
756 updateResult.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin()));
757
758 for (auto const& variable : missingIdentities) {
759 STORM_LOG_TRACE("Multiplying identity for variable " << variable.getName() << "[" << variable.getIndex() << "] to update.");
760 updateResult.updateDd *= generationInfo.variableToIdentityMap.at(variable);
761 }
762 }
763
764 // Now combine the update DDs to the command DD.
765 storm::dd::Add<Type, ValueType> commandDd = generationInfo.manager->template getAddZero<ValueType>();
766 auto updateResultsIt = updateResults.begin();
767 for (auto updateIt = command.getUpdates().begin(), updateIte = command.getUpdates().end(); updateIt != updateIte; ++updateIt, ++updateResultsIt) {
768 storm::dd::Add<Type, ValueType> probabilityDd = generationInfo.rowExpressionAdapter->translateExpression(updateIt->getLikelihoodExpression());
769 commandDd += updateResultsIt->updateDd * probabilityDd;
770 }
771
772 return ActionDecisionDiagram(guard, guard.template toAdd<ValueType>() * commandDd, globalVariablesInSomeUpdate);
773 } else {
774 return ActionDecisionDiagram(*generationInfo.manager);
775 }
776}
777
778template<storm::dd::DdType Type, typename ValueType>
779typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::createActionDecisionDiagram(
780 GenerationInformation& generationInfo, storm::prism::Module const& module, uint_fast64_t synchronizationActionIndex,
781 uint_fast64_t nondeterminismVariableOffset) {
782 std::vector<ActionDecisionDiagram> commandDds;
783 for (storm::prism::Command const& command : module.getCommands()) {
784 // Determine whether the command is relevant for the selected action.
785 bool relevant = (synchronizationActionIndex == 0 && !command.isLabeled()) ||
786 (synchronizationActionIndex && command.isLabeled() && command.getActionIndex() == synchronizationActionIndex);
787
788 if (!relevant) {
789 continue;
790 }
791
792 STORM_LOG_TRACE("Translating command " << command);
793
794 // At this point, the command is known to be relevant for the action.
795 commandDds.push_back(createCommandDecisionDiagram(generationInfo, module, command));
796 }
797
798 ActionDecisionDiagram result(*generationInfo.manager);
799 if (!commandDds.empty()) {
800 switch (generationInfo.program.getModelType()) {
803 result = combineCommandsToActionMarkovChain(generationInfo, commandDds);
804 break;
806 result = combineCommandsToActionMDP(generationInfo, commandDds, nondeterminismVariableOffset);
807 break;
808 default:
809 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot translate model of this type.");
810 }
811 }
812
813 return result;
814}
815
816template<storm::dd::DdType Type, typename ValueType>
817std::set<storm::expressions::Variable> DdPrismModelBuilder<Type, ValueType>::equalizeAssignedGlobalVariables(GenerationInformation const& generationInfo,
818 ActionDecisionDiagram& action1,
819 ActionDecisionDiagram& action2) {
820 // Start by gathering all variables that were written in at least one action DD.
821 std::set<storm::expressions::Variable> globalVariablesInActionDd;
822 std::set_union(action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), action2.assignedGlobalVariables.begin(),
823 action2.assignedGlobalVariables.end(), std::inserter(globalVariablesInActionDd, globalVariablesInActionDd.begin()));
824
825 std::set<storm::expressions::Variable> missingIdentitiesInAction1;
826 std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), action1.assignedGlobalVariables.begin(),
827 action1.assignedGlobalVariables.end(), std::inserter(missingIdentitiesInAction1, missingIdentitiesInAction1.begin()));
828 for (auto const& variable : missingIdentitiesInAction1) {
829 action1.transitionsDd *= generationInfo.variableToIdentityMap.at(variable);
830 }
831
832 std::set<storm::expressions::Variable> missingIdentitiesInAction2;
833 std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), action1.assignedGlobalVariables.begin(),
834 action1.assignedGlobalVariables.end(), std::inserter(missingIdentitiesInAction2, missingIdentitiesInAction2.begin()));
835 for (auto const& variable : missingIdentitiesInAction2) {
836 action2.transitionsDd *= generationInfo.variableToIdentityMap.at(variable);
837 }
838
839 return globalVariablesInActionDd;
840}
841
842template<storm::dd::DdType Type, typename ValueType>
843std::set<storm::expressions::Variable> DdPrismModelBuilder<Type, ValueType>::equalizeAssignedGlobalVariables(GenerationInformation const& generationInfo,
844 std::vector<ActionDecisionDiagram>& actionDds) {
845 // Start by gathering all variables that were written in at least one action DD.
846 std::set<storm::expressions::Variable> globalVariablesInActionDd;
847 for (auto const& commandDd : actionDds) {
848 globalVariablesInActionDd.insert(commandDd.assignedGlobalVariables.begin(), commandDd.assignedGlobalVariables.end());
849 }
850
851 STORM_LOG_TRACE("Equalizing assigned global variables.");
852
853 // Then multiply the transitions of each action with the missing identities.
854 for (auto& actionDd : actionDds) {
855 STORM_LOG_TRACE("Equalizing next action.");
856 std::set<storm::expressions::Variable> missingIdentities;
857 std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), actionDd.assignedGlobalVariables.begin(),
858 actionDd.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin()));
859 for (auto const& variable : missingIdentities) {
860 STORM_LOG_TRACE("Multiplying identity of variable " << variable.getName() << ".");
861 actionDd.transitionsDd *= generationInfo.variableToIdentityMap.at(variable);
862 }
863 }
864 return globalVariablesInActionDd;
865}
866
867template<storm::dd::DdType Type, typename ValueType>
868typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineCommandsToActionMarkovChain(
869 GenerationInformation& generationInfo, std::vector<ActionDecisionDiagram>& commandDds) {
870 storm::dd::Bdd<Type> allGuards = generationInfo.manager->getBddZero();
871 storm::dd::Add<Type, ValueType> allCommands = generationInfo.manager->template getAddZero<ValueType>();
872 storm::dd::Bdd<Type> temporary;
873
874 // Make all command DDs assign to the same global variables.
875 std::set<storm::expressions::Variable> assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, commandDds);
876
877 // Then combine the commands to the full action DD and multiply missing identities along the way.
878 for (auto& commandDd : commandDds) {
879 // Check for overlapping guards.
880 temporary = commandDd.guardDd && allGuards;
881
882 // Issue a warning if there are overlapping guards in a non-CTMC model.
883 STORM_LOG_WARN_COND(temporary.isZero() || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC,
884 "Guard of a command overlaps with previous guards.");
885
886 allGuards |= commandDd.guardDd;
887 allCommands += commandDd.transitionsDd;
888 }
889
890 return ActionDecisionDiagram(allGuards, allCommands, assignedGlobalVariables);
891}
892
893template<storm::dd::DdType Type, typename ValueType>
894storm::dd::Add<Type, ValueType> DdPrismModelBuilder<Type, ValueType>::encodeChoice(GenerationInformation& generationInfo,
895 uint_fast64_t nondeterminismVariableOffset,
896 uint_fast64_t numberOfBinaryVariables, int_fast64_t value) {
897 storm::dd::Add<Type, ValueType> result = generationInfo.manager->template getAddZero<ValueType>();
898
899 STORM_LOG_TRACE("Encoding " << value << " with " << numberOfBinaryVariables << " binary variable(s) starting from offset " << nondeterminismVariableOffset
900 << ".");
901
902 std::map<storm::expressions::Variable, int_fast64_t> metaVariableNameToValueMap;
903 for (uint_fast64_t i = 0; i < numberOfBinaryVariables; ++i) {
904 if (value & (1ull << (numberOfBinaryVariables - i - 1))) {
905 metaVariableNameToValueMap.emplace(generationInfo.nondeterminismMetaVariables[nondeterminismVariableOffset + i], 1);
906 } else {
907 metaVariableNameToValueMap.emplace(generationInfo.nondeterminismMetaVariables[nondeterminismVariableOffset + i], 0);
908 }
909 }
910
911 result.setValue(metaVariableNameToValueMap, storm::utility::one<ValueType>());
912 return result;
913}
914
915template<storm::dd::DdType Type, typename ValueType>
916typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineCommandsToActionMDP(
917 GenerationInformation& generationInfo, std::vector<ActionDecisionDiagram>& commandDds, uint_fast64_t nondeterminismVariableOffset) {
918 storm::dd::Bdd<Type> allGuards = generationInfo.manager->getBddZero();
919 storm::dd::Add<Type, ValueType> allCommands = generationInfo.manager->template getAddZero<ValueType>();
920
921 // Make all command DDs assign to the same global variables.
922 std::set<storm::expressions::Variable> assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, commandDds);
923
924 // Sum all guards, so we can read off the maximal number of nondeterministic choices in any given state.
925 storm::dd::Add<Type, uint_fast64_t> sumOfGuards = generationInfo.manager->template getAddZero<uint_fast64_t>();
926 for (auto const& commandDd : commandDds) {
927 sumOfGuards += commandDd.guardDd.template toAdd<uint_fast64_t>();
928 allGuards |= commandDd.guardDd;
929 }
930 uint_fast64_t maxChoices = sumOfGuards.getMax();
931
932 STORM_LOG_TRACE("Found " << maxChoices << " local choices.");
933
934 // Depending on the maximal number of nondeterminstic choices, we need to use some variables to encode the nondeterminism.
935 if (maxChoices == 0) {
936 return ActionDecisionDiagram(*generationInfo.manager);
937 } else if (maxChoices == 1) {
938 // Sum up all commands.
939 for (auto const& commandDd : commandDds) {
940 allCommands += commandDd.transitionsDd;
941 }
942 return ActionDecisionDiagram(allGuards, allCommands, assignedGlobalVariables);
943 } else {
944 // Calculate number of required variables to encode the nondeterminism.
945 uint_fast64_t numberOfBinaryVariables = static_cast<uint_fast64_t>(std::ceil(std::log2(maxChoices)));
946
947 storm::dd::Bdd<Type> equalsNumberOfChoicesDd;
948 std::vector<storm::dd::Add<Type, ValueType>> choiceDds(maxChoices, generationInfo.manager->template getAddZero<ValueType>());
949 std::vector<storm::dd::Bdd<Type>> remainingDds(maxChoices, generationInfo.manager->getBddZero());
950
951 for (uint_fast64_t currentChoices = 1; currentChoices <= maxChoices; ++currentChoices) {
952 // Determine the set of states with exactly currentChoices choices.
953 equalsNumberOfChoicesDd = sumOfGuards.equals(generationInfo.manager->getConstant(currentChoices));
954
955 // If there is no such state, continue with the next possible number of choices.
956 if (equalsNumberOfChoicesDd.isZero()) {
957 continue;
958 }
959
960 // Reset the previously used intermediate storage.
961 for (uint_fast64_t j = 0; j < currentChoices; ++j) {
962 choiceDds[j] = generationInfo.manager->template getAddZero<ValueType>();
963 remainingDds[j] = equalsNumberOfChoicesDd;
964 }
965
966 for (std::size_t j = 0; j < commandDds.size(); ++j) {
967 // Check if command guard overlaps with equalsNumberOfChoicesDd. That is, there are states with exactly currentChoices
968 // choices such that one outgoing choice is given by the j-th command.
969 storm::dd::Bdd<Type> guardChoicesIntersection = commandDds[j].guardDd && equalsNumberOfChoicesDd;
970
971 // If there is no such state, continue with the next command.
972 if (guardChoicesIntersection.isZero()) {
973 continue;
974 }
975
976 // Split the nondeterministic choices.
977 for (uint_fast64_t k = 0; k < currentChoices; ++k) {
978 // Calculate the overlapping part of command guard and the remaining DD.
979 storm::dd::Bdd<Type> remainingGuardChoicesIntersection = guardChoicesIntersection && remainingDds[k];
980
981 // Check if we can add some overlapping parts to the current index.
982 if (!remainingGuardChoicesIntersection.isZero()) {
983 // Remove overlapping parts from the remaining DD.
984 remainingDds[k] = remainingDds[k] && !remainingGuardChoicesIntersection;
985
986 // Combine the overlapping part of the guard with command updates and add it to the resulting DD.
987 choiceDds[k] += remainingGuardChoicesIntersection.template toAdd<ValueType>() * commandDds[j].transitionsDd;
988 }
989
990 // Remove overlapping parts from the command guard DD
991 guardChoicesIntersection = guardChoicesIntersection && !remainingGuardChoicesIntersection;
992
993 // If the guard DD has become equivalent to false, we can stop here.
994 if (guardChoicesIntersection.isZero()) {
995 break;
996 }
997 }
998 }
999
1000 // Add the meta variables that encode the nondeterminisim to the different choices.
1001 for (uint_fast64_t j = 0; j < currentChoices; ++j) {
1002 allCommands += encodeChoice(generationInfo, nondeterminismVariableOffset, numberOfBinaryVariables, j) * choiceDds[j];
1003 }
1004
1005 // Delete currentChoices out of overlapping DD
1006 sumOfGuards = sumOfGuards * (!equalsNumberOfChoicesDd).template toAdd<uint_fast64_t>();
1007 }
1008
1009 return ActionDecisionDiagram(allGuards, allCommands, assignedGlobalVariables, nondeterminismVariableOffset + numberOfBinaryVariables);
1010 }
1011}
1012
1013template<storm::dd::DdType Type, typename ValueType>
1014typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineSynchronizingActions(
1015 ActionDecisionDiagram const& action1, ActionDecisionDiagram const& action2) {
1016 std::set<storm::expressions::Variable> assignedGlobalVariables;
1017 std::set_union(action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), action2.assignedGlobalVariables.begin(),
1018 action2.assignedGlobalVariables.end(), std::inserter(assignedGlobalVariables, assignedGlobalVariables.begin()));
1019 return ActionDecisionDiagram(action1.guardDd && action2.guardDd, action1.transitionsDd * action2.transitionsDd, assignedGlobalVariables,
1020 std::max(action1.numberOfUsedNondeterminismVariables, action2.numberOfUsedNondeterminismVariables));
1021}
1022
1023template<storm::dd::DdType Type, typename ValueType>
1024typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(
1025 GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2,
1026 storm::dd::Add<Type, ValueType> const& identityDd1, storm::dd::Add<Type, ValueType> const& identityDd2) {
1027 // First extend the action DDs by the other identities.
1028 STORM_LOG_TRACE("Multiplying identities to combine unsynchronized actions.");
1029 action1.transitionsDd = action1.transitionsDd * identityDd2;
1030 action2.transitionsDd = action2.transitionsDd * identityDd1;
1031
1032 // Then combine the extended action DDs.
1033 return combineUnsynchronizedActions(generationInfo, action1, action2);
1034}
1035
1036template<storm::dd::DdType Type, typename ValueType>
1037typename DdPrismModelBuilder<Type, ValueType>::ActionDecisionDiagram DdPrismModelBuilder<Type, ValueType>::combineUnsynchronizedActions(
1038 GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2) {
1039 STORM_LOG_TRACE("Combining unsynchronized actions.");
1040
1041 // Make both action DDs write to the same global variables.
1042 std::set<storm::expressions::Variable> assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, action1, action2);
1043
1044 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC ||
1045 generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) {
1046 return ActionDecisionDiagram(action1.guardDd || action2.guardDd, action1.transitionsDd + action2.transitionsDd, assignedGlobalVariables, 0);
1047 } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1048 if (action1.transitionsDd.isZero()) {
1049 return ActionDecisionDiagram(action2.guardDd, action2.transitionsDd, assignedGlobalVariables, action2.numberOfUsedNondeterminismVariables);
1050 } else if (action2.transitionsDd.isZero()) {
1051 return ActionDecisionDiagram(action1.guardDd, action1.transitionsDd, assignedGlobalVariables, action1.numberOfUsedNondeterminismVariables);
1052 }
1053
1054 // Bring both choices to the same number of variables that encode the nondeterminism.
1055 uint_fast64_t numberOfUsedNondeterminismVariables = std::max(action1.numberOfUsedNondeterminismVariables, action2.numberOfUsedNondeterminismVariables);
1056 if (action1.numberOfUsedNondeterminismVariables > action2.numberOfUsedNondeterminismVariables) {
1057 storm::dd::Add<Type, ValueType> nondeterminismEncoding = generationInfo.manager->template getAddOne<ValueType>();
1058
1059 for (uint_fast64_t i = action2.numberOfUsedNondeterminismVariables; i < action1.numberOfUsedNondeterminismVariables; ++i) {
1060 nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>();
1061 }
1062 action2.transitionsDd *= nondeterminismEncoding;
1063 } else if (action2.numberOfUsedNondeterminismVariables > action1.numberOfUsedNondeterminismVariables) {
1064 storm::dd::Add<Type, ValueType> nondeterminismEncoding = generationInfo.manager->template getAddOne<ValueType>();
1065
1066 for (uint_fast64_t i = action1.numberOfUsedNondeterminismVariables; i < action2.numberOfUsedNondeterminismVariables; ++i) {
1067 nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>();
1068 }
1069 action1.transitionsDd *= nondeterminismEncoding;
1070 }
1071
1072 // Add a new variable that resolves the nondeterminism between the two choices.
1073 storm::dd::Add<Type, ValueType> combinedTransitions =
1074 generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[numberOfUsedNondeterminismVariables], 1)
1075 .ite(action2.transitionsDd, action1.transitionsDd);
1076
1077 return ActionDecisionDiagram(action1.guardDd || action2.guardDd, combinedTransitions, assignedGlobalVariables, numberOfUsedNondeterminismVariables + 1);
1078 } else {
1079 STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Illegal model type.");
1080 }
1081}
1082
1083template<storm::dd::DdType Type, typename ValueType>
1084typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram DdPrismModelBuilder<Type, ValueType>::createModuleDecisionDiagram(
1085 GenerationInformation& generationInfo, storm::prism::Module const& module, std::map<uint_fast64_t, uint_fast64_t> const& synchronizingActionToOffsetMap) {
1086 // Start by creating the action DD for the independent action.
1087 ActionDecisionDiagram independentActionDd = createActionDecisionDiagram(generationInfo, module, 0, 0);
1088 uint_fast64_t numberOfUsedNondeterminismVariables = independentActionDd.numberOfUsedNondeterminismVariables;
1089
1090 // Create module DD for all synchronizing actions of the module.
1091 std::map<uint_fast64_t, ActionDecisionDiagram> actionIndexToDdMap;
1092 for (auto const& actionIndex : module.getSynchronizingActionIndices()) {
1093 STORM_LOG_TRACE("Creating DD for action '" << actionIndex << "'.");
1094 ActionDecisionDiagram tmp = createActionDecisionDiagram(generationInfo, module, actionIndex, synchronizingActionToOffsetMap.at(actionIndex));
1095 numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, tmp.numberOfUsedNondeterminismVariables);
1096 actionIndexToDdMap.emplace(actionIndex, tmp);
1097 }
1098
1099 return ModuleDecisionDiagram(independentActionDd, actionIndexToDdMap, generationInfo.moduleToIdentityMap.at(module.getName()),
1100 numberOfUsedNondeterminismVariables);
1101}
1102
1103template<storm::dd::DdType Type, typename ValueType>
1104storm::dd::Add<Type, ValueType> DdPrismModelBuilder<Type, ValueType>::getSynchronizationDecisionDiagram(GenerationInformation& generationInfo,
1105 uint_fast64_t actionIndex) {
1106 storm::dd::Add<Type, ValueType> synchronization = generationInfo.manager->template getAddOne<ValueType>();
1107 if (actionIndex != 0) {
1108 for (uint_fast64_t i = 0; i < generationInfo.synchronizationMetaVariables.size(); ++i) {
1109 if ((actionIndex - 1) == i) {
1110 synchronization *= generationInfo.manager->getEncoding(generationInfo.synchronizationMetaVariables[i], 1).template toAdd<ValueType>();
1111 } else {
1112 synchronization *= generationInfo.manager->getEncoding(generationInfo.synchronizationMetaVariables[i], 0).template toAdd<ValueType>();
1113 }
1114 }
1115 } else {
1116 for (uint_fast64_t i = 0; i < generationInfo.synchronizationMetaVariables.size(); ++i) {
1117 synchronization *= generationInfo.manager->getEncoding(generationInfo.synchronizationMetaVariables[i], 0).template toAdd<ValueType>();
1118 }
1119 }
1120 return synchronization;
1121}
1122
1123template<storm::dd::DdType Type, typename ValueType>
1124storm::dd::Add<Type, ValueType> DdPrismModelBuilder<Type, ValueType>::createSystemFromModule(GenerationInformation& generationInfo,
1125 ModuleDecisionDiagram& module) {
1126 storm::dd::Add<Type, ValueType> result;
1127
1128 // Make sure all actions contain all necessary meta variables.
1129 module.independentAction.ensureContainsVariables(generationInfo.rowMetaVariables, generationInfo.columnMetaVariables);
1130 for (auto& synchronizingAction : module.synchronizingActionToDecisionDiagramMap) {
1131 synchronizingAction.second.ensureContainsVariables(generationInfo.rowMetaVariables, generationInfo.columnMetaVariables);
1132 }
1133
1134 // If the model is an MDP, we need to encode the nondeterminism using additional variables.
1135 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1136 result = generationInfo.manager->template getAddZero<ValueType>();
1137
1138 // First, determine the highest number of nondeterminism variables that is used in any action and make
1139 // all actions use the same amout of nondeterminism variables.
1140 uint_fast64_t numberOfUsedNondeterminismVariables = module.numberOfUsedNondeterminismVariables;
1141
1142 // Compute missing global variable identities in independent action.
1143 std::set<storm::expressions::Variable> missingIdentities;
1144 std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(),
1145 module.independentAction.assignedGlobalVariables.begin(), module.independentAction.assignedGlobalVariables.end(),
1146 std::inserter(missingIdentities, missingIdentities.begin()));
1147 storm::dd::Add<Type, ValueType> identityEncoding = generationInfo.manager->template getAddOne<ValueType>();
1148 for (auto const& variable : missingIdentities) {
1149 STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to independent action.");
1150 identityEncoding *= generationInfo.variableToIdentityMap.at(variable);
1151 }
1152
1153 // Add variables to independent action DD.
1154 storm::dd::Add<Type, ValueType> nondeterminismEncoding = generationInfo.manager->template getAddOne<ValueType>();
1155 for (uint_fast64_t i = module.independentAction.numberOfUsedNondeterminismVariables; i < numberOfUsedNondeterminismVariables; ++i) {
1156 nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>();
1157 }
1158
1159 result = identityEncoding * module.independentAction.transitionsDd * nondeterminismEncoding;
1160
1161 // Add variables to synchronized action DDs.
1162 std::map<uint_fast64_t, storm::dd::Add<Type, ValueType>> synchronizingActionToDdMap;
1163 for (auto const& synchronizingAction : module.synchronizingActionToDecisionDiagramMap) {
1164 // Compute missing global variable identities in synchronizing actions.
1165 missingIdentities = std::set<storm::expressions::Variable>();
1166 std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(),
1167 synchronizingAction.second.assignedGlobalVariables.begin(), synchronizingAction.second.assignedGlobalVariables.end(),
1168 std::inserter(missingIdentities, missingIdentities.begin()));
1169 identityEncoding = generationInfo.manager->template getAddOne<ValueType>();
1170 for (auto const& variable : missingIdentities) {
1171 STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to synchronizing action '" << synchronizingAction.first
1172 << "'.");
1173 identityEncoding *= generationInfo.variableToIdentityMap.at(variable);
1174 }
1175
1176 nondeterminismEncoding = generationInfo.manager->template getAddOne<ValueType>();
1177 for (uint_fast64_t i = synchronizingAction.second.numberOfUsedNondeterminismVariables; i < numberOfUsedNondeterminismVariables; ++i) {
1178 nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd<ValueType>();
1179 }
1180 synchronizingActionToDdMap.emplace(synchronizingAction.first, identityEncoding * synchronizingAction.second.transitionsDd * nondeterminismEncoding);
1181 }
1182
1183 // Add variables for synchronization.
1184 result *= getSynchronizationDecisionDiagram(generationInfo);
1185
1186 for (auto& synchronizingAction : synchronizingActionToDdMap) {
1187 synchronizingAction.second *= getSynchronizationDecisionDiagram(generationInfo, synchronizingAction.first);
1188 }
1189
1190 // Now, we can simply add all synchronizing actions to the result.
1191 for (auto const& synchronizingAction : synchronizingActionToDdMap) {
1192 result += synchronizingAction.second;
1193 }
1194 } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC ||
1195 generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) {
1196 // Simply add all actions, but make sure to include the missing global variable identities.
1197
1198 // Compute missing global variable identities in independent action.
1199 std::set<storm::expressions::Variable> missingIdentities;
1200 std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(),
1201 module.independentAction.assignedGlobalVariables.begin(), module.independentAction.assignedGlobalVariables.end(),
1202 std::inserter(missingIdentities, missingIdentities.begin()));
1203 storm::dd::Add<Type, ValueType> identityEncoding = generationInfo.manager->template getAddOne<ValueType>();
1204 for (auto const& variable : missingIdentities) {
1205 STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to independent action.");
1206 identityEncoding *= generationInfo.variableToIdentityMap.at(variable);
1207 }
1208
1209 result = identityEncoding * module.independentAction.transitionsDd;
1210 for (auto const& synchronizingAction : module.synchronizingActionToDecisionDiagramMap) {
1211 // Compute missing global variable identities in synchronizing actions.
1212 missingIdentities = std::set<storm::expressions::Variable>();
1213 std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(),
1214 synchronizingAction.second.assignedGlobalVariables.begin(), synchronizingAction.second.assignedGlobalVariables.end(),
1215 std::inserter(missingIdentities, missingIdentities.begin()));
1216 identityEncoding = generationInfo.manager->template getAddOne<ValueType>();
1217 for (auto const& variable : missingIdentities) {
1218 STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to synchronizing action '" << synchronizingAction.first
1219 << "'.");
1220 identityEncoding *= generationInfo.variableToIdentityMap.at(variable);
1221 }
1222
1223 result += identityEncoding * synchronizingAction.second.transitionsDd;
1224 }
1225 } else {
1226 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal model type.");
1227 }
1228 return result;
1229}
1230
1231template<storm::dd::DdType Type, typename ValueType>
1232typename DdPrismModelBuilder<Type, ValueType>::SystemResult DdPrismModelBuilder<Type, ValueType>::createSystemDecisionDiagram(
1233 GenerationInformation& generationInfo) {
1234 ModuleComposer<Type, ValueType> composer(generationInfo);
1235 ModuleDecisionDiagram system =
1236 composer.compose(generationInfo.program.specifiesSystemComposition() ? generationInfo.program.getSystemCompositionConstruct().getSystemComposition()
1237 : *generationInfo.program.getDefaultSystemComposition());
1238
1239 storm::dd::Add<Type, ValueType> result = createSystemFromModule(generationInfo, system);
1240
1241 // Create an auxiliary DD that is used later during the construction of reward models.
1242 boost::optional<storm::dd::Add<Type, ValueType>> stateActionDd;
1243
1244 // For DTMCs, we normalize each row to 1 (to account for non-determinism).
1245 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) {
1246 stateActionDd = result.sumAbstract(generationInfo.columnMetaVariables);
1247 result = result / stateActionDd.get();
1248 } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1249 // For MDPs, we need to throw away the nondeterminism variables from the generation information that
1250 // were never used.
1251 for (uint_fast64_t index = system.numberOfUsedNondeterminismVariables; index < generationInfo.nondeterminismMetaVariables.size(); ++index) {
1252 generationInfo.allNondeterminismVariables.erase(generationInfo.nondeterminismMetaVariables[index]);
1253 }
1254 generationInfo.nondeterminismMetaVariables.resize(system.numberOfUsedNondeterminismVariables);
1255 }
1256
1257 return SystemResult(result, system, stateActionDd);
1258}
1259
1260template<storm::dd::DdType Type, typename ValueType>
1261std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>>
1262DdPrismModelBuilder<Type, ValueType>::createRewardModelDecisionDiagrams(
1263 std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, SystemResult& system,
1264 GenerationInformation& generationInfo, ModuleDecisionDiagram const& globalModule, storm::dd::Add<Type, ValueType> const& reachableStatesAdd,
1265 storm::dd::Add<Type, ValueType> const& transitionMatrix) {
1266 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> rewardModels;
1267 for (auto const& rewardModel : selectedRewardModels) {
1268 rewardModels.emplace(rewardModel.get().getName(), createRewardModelDecisionDiagrams(generationInfo, rewardModel.get(), globalModule, reachableStatesAdd,
1269 transitionMatrix, system.stateActionDd));
1270 }
1271 return rewardModels;
1272}
1273
1274template<storm::dd::DdType Type, typename ValueType>
1275void checkRewards(storm::dd::Add<Type, ValueType> const& rewards, std::string const& rewardType) {
1276 STORM_LOG_WARN_COND(rewards.getMin() >= 0, "The reward model assigns negative " << rewardType << " to some states.");
1277 STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model declares " << rewardType << " but does not assign any non-zero values.");
1278}
1279
1280template<storm::dd::DdType Type>
1281void checkRewards(storm::dd::Add<Type, storm::RationalFunction> const& rewards, std::string const& rewardType) {
1282 STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model declares " << rewardType << " but does not assign any non-zero values.");
1283}
1284
1285template<storm::dd::DdType Type, typename ValueType>
1286storm::models::symbolic::StandardRewardModel<Type, ValueType> DdPrismModelBuilder<Type, ValueType>::createRewardModelDecisionDiagrams(
1287 GenerationInformation& generationInfo, storm::prism::RewardModel const& rewardModel, ModuleDecisionDiagram const& globalModule,
1288 storm::dd::Add<Type, ValueType> const& reachableStatesAdd, storm::dd::Add<Type, ValueType> const& transitionMatrix,
1289 boost::optional<storm::dd::Add<Type, ValueType>>& stateActionDd) {
1290 // Start by creating the state reward vector.
1291 boost::optional<storm::dd::Add<Type, ValueType>> stateRewards;
1292 if (rewardModel.hasStateRewards()) {
1293 stateRewards = generationInfo.manager->template getAddZero<ValueType>();
1294
1295 for (auto const& stateReward : rewardModel.getStateRewards()) {
1296 storm::dd::Add<Type, ValueType> states = generationInfo.rowExpressionAdapter->translateExpression(stateReward.getStatePredicateExpression());
1297 storm::dd::Add<Type, ValueType> rewards = generationInfo.rowExpressionAdapter->translateExpression(stateReward.getRewardValueExpression());
1298
1299 // Restrict the rewards to those states that satisfy the condition.
1300 rewards = reachableStatesAdd * states * rewards;
1301
1302 // Add the rewards to the global state reward vector.
1303 stateRewards.get() += rewards;
1304 }
1305 // Perform some sanity checks.
1306 checkRewards(stateRewards.get(), "state rewards");
1307 }
1308
1309 // Next, build the state-action reward vector.
1310 boost::optional<storm::dd::Add<Type, ValueType>> stateActionRewards;
1311 if (rewardModel.hasStateActionRewards()) {
1312 stateActionRewards = generationInfo.manager->template getAddZero<ValueType>();
1313
1314 for (auto const& stateActionReward : rewardModel.getStateActionRewards()) {
1315 storm::dd::Add<Type, ValueType> states = generationInfo.rowExpressionAdapter->translateExpression(stateActionReward.getStatePredicateExpression());
1316 storm::dd::Add<Type, ValueType> rewards = generationInfo.rowExpressionAdapter->translateExpression(stateActionReward.getRewardValueExpression());
1317 storm::dd::Add<Type, ValueType> synchronization = generationInfo.manager->template getAddOne<ValueType>();
1318
1319 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1320 synchronization = getSynchronizationDecisionDiagram(generationInfo, stateActionReward.getActionIndex());
1321 }
1322 ActionDecisionDiagram const& actionDd = stateActionReward.isLabeled()
1323 ? globalModule.synchronizingActionToDecisionDiagramMap.at(stateActionReward.getActionIndex())
1324 : globalModule.independentAction;
1325 states *= actionDd.guardDd.template toAdd<ValueType>() * reachableStatesAdd;
1326 storm::dd::Add<Type, ValueType> stateActionRewardDd = synchronization * states * rewards;
1327
1328 // If we are building the state-action rewards for an MDP, we need to make sure that the reward is
1329 // only given on legal nondeterminism encodings, which is why we multiply with the state-action DD.
1330 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1331 if (!stateActionDd) {
1332 stateActionDd = transitionMatrix.notZero().existsAbstract(generationInfo.columnMetaVariables).template toAdd<ValueType>();
1333 }
1334 stateActionRewardDd *= stateActionDd.get();
1335 } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC ||
1336 generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) {
1337 // For DTMCs and CTMC, we need to multiply the entries with the multiplicity/exit rate of the corresponding action.
1338 stateActionRewardDd *= actionDd.transitionsDd.sumAbstract(generationInfo.columnMetaVariables);
1339 }
1340
1341 // Add the rewards to the global transition reward matrix.
1342 stateActionRewards.get() += stateActionRewardDd;
1343 }
1344
1345 // Scale state-action rewards for DTMCs and CTMCs.
1346 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC ||
1347 generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) {
1348 if (!stateActionDd) {
1349 stateActionDd = transitionMatrix.sumAbstract(generationInfo.columnMetaVariables);
1350 }
1351
1352 stateActionRewards.get() /= stateActionDd.get();
1353 }
1354
1355 // Perform some sanity checks.
1356 checkRewards(stateActionRewards.get(), "action rewards");
1357 }
1358
1359 // Then build the transition reward matrix.
1360 boost::optional<storm::dd::Add<Type, ValueType>> transitionRewards;
1361 if (rewardModel.hasTransitionRewards()) {
1362 transitionRewards = generationInfo.manager->template getAddZero<ValueType>();
1363
1364 for (auto const& transitionReward : rewardModel.getTransitionRewards()) {
1365 storm::dd::Add<Type, ValueType> sourceStates =
1366 generationInfo.rowExpressionAdapter->translateExpression(transitionReward.getSourceStatePredicateExpression());
1367 storm::dd::Add<Type, ValueType> targetStates =
1368 generationInfo.rowExpressionAdapter->translateExpression(transitionReward.getTargetStatePredicateExpression());
1369 storm::dd::Add<Type, ValueType> rewards = generationInfo.rowExpressionAdapter->translateExpression(transitionReward.getRewardValueExpression());
1370
1371 storm::dd::Add<Type, ValueType> synchronization = generationInfo.manager->template getAddOne<ValueType>();
1372
1373 storm::dd::Add<Type, ValueType> transitions;
1374 if (transitionReward.isLabeled()) {
1375 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1376 synchronization = getSynchronizationDecisionDiagram(generationInfo, transitionReward.getActionIndex());
1377 }
1378 transitions = globalModule.synchronizingActionToDecisionDiagramMap.at(transitionReward.getActionIndex()).transitionsDd;
1379 } else {
1380 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
1381 synchronization = getSynchronizationDecisionDiagram(generationInfo);
1382 }
1383 transitions = globalModule.independentAction.transitionsDd;
1384 }
1385
1386 storm::dd::Add<Type, ValueType> transitionRewardDd = synchronization * sourceStates * targetStates * rewards;
1387 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) {
1388 // For DTMCs we need to keep the weighting for the scaling that follows.
1389 transitionRewardDd = transitions * transitionRewardDd;
1390 } else {
1391 // For all other model types, we do not scale the rewards.
1392 transitionRewardDd = transitions.notZero().template toAdd<ValueType>() * transitionRewardDd;
1393 }
1394
1395 // Add the rewards to the global transition reward matrix.
1396 transitionRewards.get() += transitionRewardDd;
1397 }
1398
1399 // Perform some sanity checks.
1400 checkRewards(transitionRewards.get(), "transition rewards");
1401
1402 // Scale transition rewards for DTMCs.
1403 if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) {
1404 transitionRewards.get() /= stateActionDd.get();
1405 }
1406 }
1407
1408 return storm::models::symbolic::StandardRewardModel<Type, ValueType>(stateRewards, stateActionRewards, transitionRewards);
1409}
1410
1411template<storm::dd::DdType Type, typename ValueType>
1412std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdPrismModelBuilder<Type, ValueType>::buildInternal(
1413 storm::prism::Program const& program, Options const& options, std::shared_ptr<storm::dd::DdManager<Type>> const& manager) {
1414 // Start by initializing the structure used for storing all information needed during the model generation.
1415 // In particular, this creates the meta variables used to encode the model.
1416 GenerationInformation generationInfo(program, manager);
1417
1418 SystemResult system = createSystemDecisionDiagram(generationInfo);
1419 storm::dd::Add<Type, ValueType> transitionMatrix = system.allTransitionsDd;
1420
1421 ModuleDecisionDiagram const& globalModule = system.globalModule;
1422
1423 // If we were asked to treat some states as terminal states, we cut away their transitions now.
1424 storm::dd::Bdd<Type> terminalStatesBdd = generationInfo.manager->getBddZero();
1425 if (!options.terminalStates.empty()) {
1426 storm::expressions::Expression terminalExpression = options.terminalStates.asExpression([&program](std::string const& labelName) {
1427 if (program.hasLabel(labelName)) {
1428 return program.getLabelExpression(labelName);
1429 } else {
1430 STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException,
1431 "Terminal states refer to illegal label '" << labelName << "'.");
1432 // If the label name is "init" we can abort 'exploration' directly at the initial state. If it is deadlock, we do not have to abort.
1433 return program.getManager().boolean(labelName == "init");
1434 }
1435 });
1436 terminalExpression = terminalExpression.substitute(program.getConstantsSubstitution());
1437 terminalStatesBdd = generationInfo.rowExpressionAdapter->translateExpression(terminalExpression).toBdd();
1438 transitionMatrix *= (!terminalStatesBdd).template toAdd<ValueType>();
1439 }
1440
1441 // Cut the transitions and rewards to the reachable fragment of the state space.
1442 storm::dd::Bdd<Type> initialStates = createInitialStatesDecisionDiagram(generationInfo);
1443
1444 storm::dd::Bdd<Type> transitionMatrixBdd = transitionMatrix.notZero();
1446 transitionMatrixBdd = transitionMatrixBdd.existsAbstract(generationInfo.allNondeterminismVariables);
1447 }
1448
1449 storm::dd::Bdd<Type> reachableStates = storm::utility::dd::computeReachableStates<Type>(initialStates, transitionMatrixBdd, generationInfo.rowMetaVariables,
1450 generationInfo.columnMetaVariables)
1451 .first;
1452 storm::dd::Add<Type, ValueType> reachableStatesAdd = reachableStates.template toAdd<ValueType>();
1453 transitionMatrix *= reachableStatesAdd;
1454 if (system.stateActionDd) {
1455 system.stateActionDd.get() *= reachableStatesAdd;
1456 }
1457
1458 // Detect deadlocks and 1) fix them if requested 2) throw an error otherwise.
1459 storm::dd::Bdd<Type> statesWithTransition = transitionMatrixBdd.existsAbstract(generationInfo.columnMetaVariables);
1460 storm::dd::Bdd<Type> deadlockStates = reachableStates && !statesWithTransition;
1461
1462 // If there are deadlocks, either fix them or raise an error.
1463 if (!deadlockStates.isZero()) {
1464 // If we need to fix deadlocks, we do so now.
1465 if (options.fixDeadlocks) {
1466 STORM_LOG_INFO("Fixing deadlocks in " << deadlockStates.getNonZeroCount() << " states. The first three of these states are: ");
1467
1468 storm::dd::Add<Type, ValueType> deadlockStatesAdd = deadlockStates.template toAdd<ValueType>();
1469 uint_fast64_t count = 0;
1470 for (auto it = deadlockStatesAdd.begin(), ite = deadlockStatesAdd.end(); it != ite && count < 3; ++it, ++count) {
1471 STORM_LOG_INFO((*it).first.toPrettyString(generationInfo.rowMetaVariables) << '\n');
1472 }
1473
1475 storm::dd::Add<Type, ValueType> identity = globalModule.identity;
1476
1477 // Make sure that global variables do not change along the introduced self-loops.
1478 for (auto const& var : generationInfo.allGlobalVariables) {
1479 identity *= generationInfo.variableToIdentityMap.at(var);
1480 }
1481
1482 // For DTMCs, we can simply add the identity of the global module for all deadlock states.
1483 transitionMatrix += deadlockStatesAdd * identity;
1484 } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
1485 // For MDPs, however, we need to select an action associated with the self-loop, if we do not
1486 // want to attach a lot of self-loops to the deadlock states.
1487 storm::dd::Add<Type, ValueType> action = generationInfo.manager->template getAddOne<ValueType>();
1488 for (auto const& metaVariable : generationInfo.allNondeterminismVariables) {
1489 action *= generationInfo.manager->template getIdentity<ValueType>(metaVariable);
1490 }
1491 // Make sure that global variables do not change along the introduced self-loops.
1492 for (auto const& var : generationInfo.allGlobalVariables) {
1493 action *= generationInfo.variableToIdentityMap.at(var);
1494 }
1495 transitionMatrix += deadlockStatesAdd * globalModule.identity * action;
1496 }
1497 } else {
1498 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException,
1499 "The model contains " << deadlockStates.getNonZeroCount()
1500 << " deadlock states. Please unset the option to not fix deadlocks, if you want to fix them automatically.");
1501 }
1502 }
1503
1504 // Reduce the deadlock states by the states that we did simply not explore.
1505 deadlockStates = deadlockStates && !terminalStatesBdd;
1506
1507 // Now build the reward models.
1508 std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels;
1509
1510 // First, we make sure that all selected reward models actually exist.
1511 for (auto const& rewardModelName : options.rewardModelsToBuild) {
1512 STORM_LOG_THROW(rewardModelName.empty() || program.hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException,
1513 "Model does not possess a reward model with the name '" << rewardModelName << "'.");
1514 }
1515
1516 for (auto const& rewardModel : program.getRewardModels()) {
1517 if (options.buildAllRewardModels || options.rewardModelsToBuild.find(rewardModel.getName()) != options.rewardModelsToBuild.end()) {
1518 selectedRewardModels.push_back(rewardModel);
1519 }
1520 }
1521 // If no reward model was selected until now and a referenced reward model appears to be unique, we build
1522 // the only existing reward model (given that no explicit name was given for the referenced reward model).
1523 if (selectedRewardModels.empty() && program.getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 &&
1524 *options.rewardModelsToBuild.begin() == "") {
1525 selectedRewardModels.push_back(program.getRewardModel(0));
1526 }
1527
1528 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> rewardModels =
1529 createRewardModelDecisionDiagrams(selectedRewardModels, system, generationInfo, globalModule, reachableStatesAdd, transitionMatrix);
1530
1531 // Build the labels that can be accessed as a shortcut.
1532 std::map<std::string, storm::expressions::Expression> labelToExpressionMapping;
1533 for (auto const& label : program.getLabels()) {
1534 labelToExpressionMapping.emplace(label.getName(), label.getStatePredicateExpression());
1535 }
1536
1537 std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> result;
1539 result = std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>>(new storm::models::symbolic::Dtmc<Type, ValueType>(
1540 generationInfo.manager, reachableStates, initialStates, deadlockStates, transitionMatrix, generationInfo.rowMetaVariables,
1541 generationInfo.rowExpressionAdapter, generationInfo.columnMetaVariables, generationInfo.rowColumnMetaVariablePairs, labelToExpressionMapping,
1542 rewardModels));
1543 } else if (program.getModelType() == storm::prism::Program::ModelType::CTMC) {
1544 result = std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>>(new storm::models::symbolic::Ctmc<Type, ValueType>(
1545 generationInfo.manager, reachableStates, initialStates, deadlockStates, transitionMatrix, system.stateActionDd, generationInfo.rowMetaVariables,
1546 generationInfo.rowExpressionAdapter, generationInfo.columnMetaVariables, generationInfo.rowColumnMetaVariablePairs, labelToExpressionMapping,
1547 rewardModels));
1548 } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
1549 result = std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>>(new storm::models::symbolic::Mdp<Type, ValueType>(
1550 generationInfo.manager, reachableStates, initialStates, deadlockStates, transitionMatrix, generationInfo.rowMetaVariables,
1551 generationInfo.rowExpressionAdapter, generationInfo.columnMetaVariables, generationInfo.rowColumnMetaVariablePairs,
1552 generationInfo.allNondeterminismVariables, labelToExpressionMapping, rewardModels));
1553 } else {
1554 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Invalid model type.");
1555 }
1556
1557 if (std::is_same<ValueType, storm::RationalFunction>::value) {
1558 result->addParameters(generationInfo.parameters);
1559 }
1560
1561 return result;
1562}
1563
1564template<storm::dd::DdType Type, typename ValueType>
1565std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdPrismModelBuilder<Type, ValueType>::build(storm::Environment const& env,
1566 storm::prism::Program const& program,
1567 Options const& options) {
1568 if (!std::is_same<ValueType, storm::RationalFunction>::value && program.hasUndefinedConstants()) {
1569 std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = program.getUndefinedConstants();
1570 std::stringstream stream;
1571 bool printComma = false;
1572 for (auto const& constant : undefinedConstants) {
1573 if (printComma) {
1574 stream << ", ";
1575 } else {
1576 printComma = true;
1577 }
1578 stream << constant.get().getName() << " (" << constant.get().getType() << ")";
1579 }
1580 stream << ".";
1581 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str() + ".");
1582 }
1583 STORM_LOG_THROW(!program.hasUnboundedVariables(), storm::exceptions::InvalidArgumentException,
1584 "Program contains unbounded variables which is not supported by the DD engine.");
1585 STORM_LOG_THROW(!program.hasIntervalUpdates(), storm::exceptions::InvalidArgumentException,
1586 "Program contains interval updates which are not supported by the DD engnie.");
1587
1588 STORM_LOG_TRACE("Building representation of program:\n" << program << '\n');
1589
1590 auto manager = std::make_shared<storm::dd::DdManager<Type>>(env);
1591 std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> result;
1592 manager->execute([&program, &options, &manager, &result, this]() { result = this->buildInternal(program, options, manager); });
1593 return result;
1594}
1595
1596template<storm::dd::DdType Type, typename ValueType>
1597storm::dd::Bdd<Type> DdPrismModelBuilder<Type, ValueType>::createInitialStatesDecisionDiagram(GenerationInformation& generationInfo) {
1598 storm::dd::Bdd<Type> initialStates = generationInfo.rowExpressionAdapter->translateExpression(generationInfo.program.getInitialStatesExpression()).toBdd();
1599
1600 for (auto const& metaVariable : generationInfo.rowMetaVariables) {
1601 initialStates &= generationInfo.manager->getRange(metaVariable);
1602 }
1603
1604 return initialStates;
1605}
1606
1607// Explicitly instantiate the symbolic model builder.
1608template class DdPrismModelBuilder<storm::dd::DdType::CUDD>;
1609template class DdPrismModelBuilder<storm::dd::DdType::Sylvan>;
1610
1611template class DdPrismModelBuilder<storm::dd::DdType::Sylvan, storm::RationalNumber>;
1612template class DdPrismModelBuilder<storm::dd::DdType::Sylvan, storm::RationalFunction>;
1613
1614} // namespace builder
1615} // namespace storm
void setValue(storm::expressions::Variable const &variable, ValueType const &value)
std::shared_ptr< storm::adapters::AddExpressionAdapter< Type, ValueType > > rowExpressionAdapter
std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > rowColumnMetaVariablePairs
std::shared_ptr< std::map< storm::expressions::Variable, storm::expressions::Variable > > variableToColumnMetaVariableMap
std::vector< storm::expressions::Variable > nondeterminismMetaVariables
std::set< storm::expressions::Variable > allGlobalVariables
std::vector< storm::expressions::Variable > synchronizationMetaVariables
std::shared_ptr< std::map< storm::expressions::Variable, storm::expressions::Variable > > variableToRowMetaVariableMap
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > variableToIdentityMap
std::shared_ptr< storm::dd::DdManager< Type > > manager
std::set< storm::expressions::Variable > allNondeterminismVariables
std::set< storm::RationalFunctionVariable > parameters
std::map< std::string, storm::dd::Add< Type, ValueType > > moduleToRangeMap
std::map< std::string, storm::dd::Add< Type, ValueType > > moduleToIdentityMap
std::set< storm::expressions::Variable > columnMetaVariables
std::set< storm::expressions::Variable > allSynchronizationMetaVariables
GenerationInformation(storm::prism::Program const &program, std::shared_ptr< storm::dd::DdManager< Type > > const &manager)
std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > build(storm::Environment const &env, storm::prism::Program const &program, Options const &options=Options())
Translates the given program into a symbolic model (i.e.
static bool canHandle(storm::prism::Program const &program)
A quick check to detect whether the given model is not supported.
DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram compose(storm::prism::Composition const &composition)
virtual boost::any visit(storm::prism::ModuleComposition const &composition, boost::any const &data) override
std::map< uint_fast64_t, uint_fast64_t > newSynchronizingActionToOffsetMap() const
virtual boost::any visit(storm::prism::SynchronizingParallelComposition const &composition, boost::any const &data) override
virtual boost::any visit(storm::prism::RenamingComposition const &composition, boost::any const &data) override
virtual boost::any visit(storm::prism::HidingComposition const &composition, boost::any const &data) override
virtual boost::any visit(storm::prism::RestrictedParallelComposition const &composition, boost::any const &data) override
std::map< uint_fast64_t, uint_fast64_t > updateSynchronizingActionToOffsetMap(typename DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram const &sub, std::map< uint_fast64_t, uint_fast64_t > const &oldMapping) const
virtual boost::any visit(storm::prism::InterleavingParallelComposition const &composition, boost::any const &data) override
ModuleComposer(typename DdPrismModelBuilder< Type, ValueType >::GenerationInformation &generationInfo)
std::set< storm::RationalFunctionVariable > const & getParameters() const
RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const &variable)
void create(storm::prism::Program const &program, storm::adapters::AddExpressionAdapter< Type, storm::RationalFunction > &rowExpressionAdapter)
std::set< storm::RationalFunctionVariable > const & getParameters() const
void create(storm::prism::Program const &, storm::adapters::AddExpressionAdapter< Type, ValueType > &)
Bdd< LibraryType > equals(Add< LibraryType, ValueType > const &other) const
Retrieves the function that maps all evaluations to one that have identical function values.
Definition Add.cpp:89
ValueType getMax() const
Retrieves the highest function value of any encoding.
Definition Add.cpp:468
ValueType getMin() const
Retrieves the lowest function value of any encoding.
Definition Add.cpp:463
AddIterator< LibraryType, ValueType > begin(bool enumerateDontCareMetaVariables=true) const
Retrieves an iterator that points to the first meta variable assignment with a non-zero function valu...
Definition Add.cpp:1142
Add< LibraryType, ValueType > sumAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Sum-abstracts from the given meta variables.
Definition Add.cpp:171
AddIterator< LibraryType, ValueType > end() const
Retrieves an iterator that points past the end of the container.
Definition Add.cpp:1154
bool isZero() const
Retrieves whether this ADD represents the constant zero function.
Definition Add.cpp:525
void setValue(storm::expressions::Variable const &metaVariable, int_fast64_t variableValue, ValueType const &targetValue)
Sets the function values of all encodings that have the given value of the meta variable to the given...
Definition Add.cpp:473
Bdd< LibraryType > notZero() const
Computes a BDD that represents the function in which all assignments with a function value unequal to...
Definition Add.cpp:424
Bdd< LibraryType > existsAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Existentially abstracts from the given meta variables.
Definition Bdd.cpp:172
bool isZero() const
Retrieves whether this DD represents the constant zero function.
Definition Bdd.cpp:541
virtual uint_fast64_t getNonZeroCount() const override
Retrieves the number of encodings that are mapped to a non-zero value.
Definition Bdd.cpp:507
Expression substitute(std::map< Variable, Expression > const &variableToExpressionMap) const
Substitutes all occurrences of the variables according to the given map.
Expression boolean(bool value) const
Creates an expression that characterizes the given boolean literal.
std::vector< std::shared_ptr< AtomicLabelFormula const > > getAtomicLabelFormulas() const
Definition Formula.cpp:506
std::set< std::string > getReferencedRewardModels() const
Definition Formula.cpp:518
std::vector< storm::prism::Update > const & getUpdates() const
Retrieves a vector of all updates associated with this command.
Definition Command.cpp:48
bool isLabeled() const
Retrieves whether the command possesses a synchronization label.
Definition Command.cpp:82
storm::expressions::Expression const & getGuardExpression() const
Retrieves a reference to the guard of the command.
Definition Command.cpp:35
uint_fast64_t getActionIndex() const
Retrieves the action index of this command.
Definition Command.cpp:19
virtual boost::any accept(CompositionVisitor &visitor, boost::any const &data) const =0
std::set< std::string > const & getActionsToHide() const
Composition const & getSubcomposition() const
std::string const & getModuleName() const
std::vector< storm::prism::Command > const & getCommands() const
Retrieves the commands of the module.
Definition Module.cpp:133
std::vector< storm::prism::IntegerVariable > const & getIntegerVariables() const
Retrieves the integer variables of the module.
Definition Module.cpp:74
std::vector< storm::prism::BooleanVariable > const & getBooleanVariables() const
Retrieves the boolean variables of the module.
Definition Module.cpp:63
std::string const & getName() const
Retrieves the name of the module.
Definition Module.cpp:141
std::set< uint_fast64_t > const & getSynchronizingActionIndices() const
Retrieves the set of synchronizing action indices present in this module.
Definition Module.cpp:145
Composition const & getLeftSubcomposition() const
Composition const & getRightSubcomposition() const
bool hasIntervalUpdates() const
Retrieves whether the program considers at least one update with an interval probability/rate.
Definition Program.cpp:706
ModelType getModelType() const
Retrieves the model type of the model.
Definition Program.cpp:243
std::vector< RewardModel > const & getRewardModels() const
Retrieves the reward models of the program.
Definition Program.cpp:817
RewardModel const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name.
Definition Program.cpp:825
std::vector< std::reference_wrapper< Constant const > > getUndefinedConstants() const
Retrieves the undefined constants in the program.
Definition Program.cpp:364
std::map< storm::expressions::Variable, storm::expressions::Expression > getConstantsSubstitution() const
Retrieves a mapping of all defined constants to their defining expressions.
Definition Program.cpp:402
storm::expressions::Expression const & getLabelExpression(std::string const &label) const
Retrieves the expression associated with the given label, if it exists.
Definition Program.cpp:865
std::size_t getNumberOfRewardModels() const
Retrieves the number of reward models in the program.
Definition Program.cpp:821
std::vector< Constant > const & getConstants() const
Retrieves all constants defined in the program.
Definition Program.cpp:398
bool hasUnboundedVariables() const
Definition Program.cpp:267
storm::expressions::ExpressionManager & getManager() const
Retrieves the manager responsible for the expressions of this program.
Definition Program.cpp:2388
bool hasLabel(std::string const &labelName) const
Checks whether the program has a label with the given name.
Definition Program.cpp:837
bool hasUndefinedConstants() const
Retrieves whether there are undefined constants of any type in the program.
Definition Program.cpp:281
std::vector< Label > const & getLabels() const
Retrieves all labels that are defined by the probabilitic program.
Definition Program.cpp:842
bool hasRewardModel() const
Retrieves whether the program has reward models.
Definition Program.cpp:808
Composition const & getSubcomposition() const
std::map< std::string, std::string > const & getActionRenaming() const
std::set< std::string > const & getSynchronizingActions() const
std::vector< storm::prism::StateReward > const & getStateRewards() const
Retrieves all state rewards associated with this reward model.
bool hasStateRewards() const
Retrieves whether there are any state rewards.
bool hasTransitionRewards() const
Retrieves whether there are any transition rewards.
std::vector< storm::prism::TransitionReward > const & getTransitionRewards() const
Retrieves all transition rewards associated with this reward model.
std::string const & getName() const
Retrieves the name of the reward model.
bool hasStateActionRewards() const
Retrieves whether there are any state-action rewards.
std::vector< storm::prism::StateActionReward > const & getStateActionRewards() const
Retrieves all state-action rewards associated with this reward model.
std::vector< storm::prism::Assignment > const & getAssignments() const
Retrieves a reference to the map of variable names to their respective assignments.
Definition Update.cpp:75
#define STORM_LOG_INFO(message)
Definition logging.h:27
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:36
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
void getTerminalStatesFromFormula(storm::logic::Formula const &formula, std::function< void(storm::expressions::Expression const &, bool)> const &terminalExpressionCallback, std::function< void(std::string const &, bool)> const &terminalLabelCallback)
Traverses the formula.
void checkRewards(storm::dd::Add< Type, ValueType > const &rewards, std::string const &rewardType)
std::pair< storm::RationalNumber, storm::RationalNumber > count(std::vector< storm::storage::BitVector > const &origSets, std::vector< storm::storage::BitVector > const &intersects, std::vector< storm::storage::BitVector > const &intersectsInfo, storm::RationalNumber val, bool plus, uint64_t remdepth)
std::pair< storm::dd::Bdd< Type >, uint64_t > computeReachableStates(storm::dd::Bdd< Type > const &initialStates, storm::dd::Bdd< Type > const &transitions, std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables)
Definition dd.cpp:13
ValueType one()
Definition constants.cpp:19
carl::Cache< carl::PolynomialFactorizationPair< RawPolynomial > > RawPolynomialCache
RationalFunctionVariable createRFVariable(std::string const &name)
carl::Variable RationalFunctionVariable
carl::RationalFunction< Polynomial, true > RationalFunction
void preserveFormula(storm::logic::Formula const &formula)
Changes the options in a way that ensures that the given formula can be checked on the model once it ...
void setTerminalStatesFromFormula(storm::logic::Formula const &formula)
Analyzes the given formula and sets an expression for the states states of the model that can be trea...
Options()
Creates an object representing the default building options.
boost::optional< std::set< std::string > > labelsToBuild
DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram globalModule
SystemResult(storm::dd::Add< Type, ValueType > const &allTransitionsDd, DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram const &globalModule, boost::optional< storm::dd::Add< Type, ValueType > > const &stateActionDd)
boost::optional< storm::dd::Add< Type, ValueType > > stateActionDd