Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DdPrismModelBuilder.cpp
Go to the documentation of this file.
2
3#include <boost/algorithm/string/join.hpp>
4
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:
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)),
102 variableToColumnMetaVariableMap((std::make_shared<std::map<storm::expressions::Variable, storm::expressions::Variable>>())),
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
291 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram compose(storm::prism::Composition const& composition) {
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
304 std::map<uint_fast64_t, uint_fast64_t> updateSynchronizingActionToOffsetMap(typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram const& sub,
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
317 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram result = DdPrismModelBuilder<Type, ValueType>::createModuleDecisionDiagram(
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.
345 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram sub =
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) {
367 }
368
369 // Then, we translate the subcomposition.
370 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram sub =
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.
381 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left =
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
391 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right =
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.
409 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left =
410 boost::any_cast<typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data));
411
412 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right =
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.
428 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram left =
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
441 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram right =
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>
610
611template<storm::dd::DdType Type, typename ValueType>
617
618template<storm::dd::DdType Type, typename ValueType>
619DdPrismModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas)
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.
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 {
657 SystemResult(storm::dd::Add<Type, ValueType> const& allTransitionsDd, DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram const& globalModule,
658 boost::optional<storm::dd::Add<Type, ValueType>> const& stateActionDd)
660 // Intentionally left empty.
661 }
662
664 typename DdPrismModelBuilder<Type, ValueType>::ModuleDecisionDiagram globalModule;
665 boost::optional<storm::dd::Add<Type, ValueType>> stateActionDd;
666};
667
668template<storm::dd::DdType Type, typename ValueType>
669typename DdPrismModelBuilder<Type, ValueType>::UpdateDecisionDiagram DdPrismModelBuilder<Type, ValueType>::createUpdateDecisionDiagram(
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) {
1134 storm::dd::Add<Type, ValueType> result;
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
1381 storm::dd::Add<Type, ValueType> transitions;
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.
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>
1604storm::dd::Bdd<Type> DdPrismModelBuilder<Type, ValueType>::createInitialStatesDecisionDiagram(GenerationInformation& generationInfo) {
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.
static bool canHandle(storm::prism::Program const &program)
A quick check to detect whether the given model is not supported.
DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram compose(storm::prism::Composition const &composition)
virtual boost::any visit(storm::prism::ModuleComposition const &composition, boost::any const &data) override
std::map< uint_fast64_t, uint_fast64_t > newSynchronizingActionToOffsetMap() const
virtual boost::any visit(storm::prism::SynchronizingParallelComposition const &composition, boost::any const &data) override
virtual boost::any visit(storm::prism::RenamingComposition const &composition, boost::any const &data) override
virtual boost::any visit(storm::prism::HidingComposition const &composition, boost::any const &data) override
virtual boost::any visit(storm::prism::RestrictedParallelComposition const &composition, boost::any const &data) override
std::map< uint_fast64_t, uint_fast64_t > updateSynchronizingActionToOffsetMap(typename DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram const &sub, std::map< uint_fast64_t, uint_fast64_t > const &oldMapping) const
virtual boost::any visit(storm::prism::InterleavingParallelComposition const &composition, boost::any const &data) override
ModuleComposer(typename DdPrismModelBuilder< Type, ValueType >::GenerationInformation &generationInfo)
std::set< storm::RationalFunctionVariable > const & getParameters() const
RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const &variable)
void create(storm::prism::Program const &program, storm::adapters::AddExpressionAdapter< Type, storm::RationalFunction > &rowExpressionAdapter)
std::set< storm::RationalFunctionVariable > const & getParameters() const
void create(storm::prism::Program const &, storm::adapters::AddExpressionAdapter< Type, ValueType > &)
Bdd< LibraryType > equals(Add< LibraryType, ValueType > const &other) const
Retrieves the function that maps all evaluations to one that have identical function values.
Definition Add.cpp:89
ValueType getMax() const
Retrieves the highest function value of any encoding.
Definition Add.cpp:468
ValueType getMin() const
Retrieves the lowest function value of any encoding.
Definition Add.cpp:463
AddIterator< LibraryType, ValueType > begin(bool enumerateDontCareMetaVariables=true) const
Retrieves an iterator that points to the first meta variable assignment with a non-zero function valu...
Definition Add.cpp:1142
Add< LibraryType, ValueType > sumAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Sum-abstracts from the given meta variables.
Definition Add.cpp:171
AddIterator< LibraryType, ValueType > end() const
Retrieves an iterator that points past the end of the container.
Definition Add.cpp:1154
bool isZero() const
Retrieves whether this ADD represents the constant zero function.
Definition Add.cpp:525
void setValue(storm::expressions::Variable const &metaVariable, int_fast64_t variableValue, ValueType const &targetValue)
Sets the function values of all encodings that have the given value of the meta variable to the given...
Definition Add.cpp:473
Bdd< LibraryType > notZero() const
Computes a BDD that represents the function in which all assignments with a function value unequal to...
Definition Add.cpp:424
Bdd< LibraryType > existsAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Existentially abstracts from the given meta variables.
Definition Bdd.cpp:172
bool isZero() const
Retrieves whether this DD represents the constant zero function.
Definition Bdd.cpp:541
virtual uint_fast64_t getNonZeroCount() const override
Retrieves the number of encodings that are mapped to a non-zero value.
Definition Bdd.cpp:507
Expression substitute(std::map< Variable, Expression > const &variableToExpressionMap) const
Substitutes all occurrences of the variables according to the given map.
Expression boolean(bool value) const
Creates an expression that characterizes the given boolean literal.
std::vector< std::shared_ptr< AtomicLabelFormula const > > getAtomicLabelFormulas() const
Definition Formula.cpp:506
std::set< std::string > getReferencedRewardModels() const
Definition Formula.cpp:518
std::vector< storm::prism::Update > const & getUpdates() const
Retrieves a vector of all updates associated with this command.
Definition Command.cpp:48
bool isLabeled() const
Retrieves whether the command possesses a synchronization label.
Definition Command.cpp:82
storm::expressions::Expression const & getGuardExpression() const
Retrieves a reference to the guard of the command.
Definition Command.cpp:35
uint_fast64_t getActionIndex() const
Retrieves the action index of this command.
Definition Command.cpp:19
virtual boost::any accept(CompositionVisitor &visitor, boost::any const &data) const =0
std::set< std::string > const & getActionsToHide() const
Composition const & getSubcomposition() const
std::string const & getModuleName() const
std::vector< storm::prism::Command > const & getCommands() const
Retrieves the commands of the module.
Definition Module.cpp:133
std::vector< storm::prism::IntegerVariable > const & getIntegerVariables() const
Retrieves the integer variables of the module.
Definition Module.cpp:74
std::vector< storm::prism::BooleanVariable > const & getBooleanVariables() const
Retrieves the boolean variables of the module.
Definition Module.cpp:63
std::string const & getName() const
Retrieves the name of the module.
Definition Module.cpp:141
std::set< uint_fast64_t > const & getSynchronizingActionIndices() const
Retrieves the set of synchronizing action indices present in this module.
Definition Module.cpp:145
Composition const & getLeftSubcomposition() const
Composition const & getRightSubcomposition() const
bool hasIntervalUpdates() const
Retrieves whether the program considers at least one update with an interval probability/rate.
Definition Program.cpp:710
ModelType getModelType() const
Retrieves the model type of the model.
Definition Program.cpp:247
std::vector< RewardModel > const & getRewardModels() const
Retrieves the reward models of the program.
Definition Program.cpp:821
RewardModel const & getRewardModel(std::string const &rewardModelName) const
Retrieves the reward model with the given name.
Definition Program.cpp:829
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
storm::expressions::Expression const & getLabelExpression(std::string const &label) const
Retrieves the expression associated with the given label, if it exists.
Definition Program.cpp:860
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
storm::expressions::ExpressionManager & getManager() const
Retrieves the manager responsible for the expressions of this program.
Definition Program.cpp:2378
bool hasLabel(std::string const &labelName) const
Checks whether the program has a label with the given name.
Definition Program.cpp:841
bool hasUndefinedConstants() const
Retrieves whether there are undefined constants of any type in the program.
Definition Program.cpp:285
std::vector< Label > const & getLabels() const
Retrieves all labels that are defined by the probabilitic program.
Definition Program.cpp:846
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
std::vector< storm::prism::StateReward > const & getStateRewards() const
Retrieves all state rewards associated with this reward model.
bool hasStateRewards() const
Retrieves whether there are any state rewards.
bool hasTransitionRewards() const
Retrieves whether there are any transition rewards.
std::vector< storm::prism::TransitionReward > const & getTransitionRewards() const
Retrieves all transition rewards associated with this reward model.
std::string const & getName() const
Retrieves the name of the reward model.
bool hasStateActionRewards() const
Retrieves whether there are any state-action rewards.
std::vector< storm::prism::StateActionReward > const & getStateActionRewards() const
Retrieves all state-action rewards associated with this reward model.
std::vector< storm::prism::Assignment > const & getAssignments() const
Retrieves a reference to the map of variable names to their respective assignments.
Definition Update.cpp:75
#define STORM_LOG_INFO(message)
Definition logging.h: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)
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)
SettingsType const & getModule()
Get module.
std::pair< storm::dd::Bdd< Type >, uint64_t > computeReachableStates(storm::dd::Bdd< Type > const &initialStates, storm::dd::Bdd< Type > const &transitions, std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables)
Definition dd.cpp:13
ValueType one()
Definition constants.cpp:19
carl::Cache< carl::PolynomialFactorizationPair< RawPolynomial > > RawPolynomialCache
RationalFunctionVariable createRFVariable(std::string const &name)
carl::Variable RationalFunctionVariable
carl::RationalFunction< Polynomial, true > RationalFunction
void preserveFormula(storm::logic::Formula const &formula)
Changes the options in a way that ensures that the given formula can be checked on the model once it ...
void setTerminalStatesFromFormula(storm::logic::Formula const &formula)
Analyzes the given formula and sets an expression for the states states of the model that can be trea...
Options()
Creates an object representing the default building options.
boost::optional< std::set< std::string > > labelsToBuild
DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram globalModule
SystemResult(storm::dd::Add< Type, ValueType > const &allTransitionsDd, DdPrismModelBuilder< Type, ValueType >::ModuleDecisionDiagram const &globalModule, boost::optional< storm::dd::Add< Type, ValueType > > const &stateActionDd)
boost::optional< storm::dd::Add< Type, ValueType > > stateActionDd