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