Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DdJaniModelBuilder.cpp
Go to the documentation of this file.
2
3#include <boost/algorithm/string/join.hpp>
4#include <sstream>
5
35#include "storm/utility/dd.h"
37#include "storm/utility/math.h"
38
39namespace storm {
40namespace builder {
41
42template<storm::dd::DdType Type, typename ValueType>
48 // We do not add Functions and arrays as these should ideally be substituted before creating this generator.
49 // This is because functions or arrays may also occur in properties and the user of this builder should take care of that.
50 return features;
51}
52
53template<storm::dd::DdType Type, typename ValueType>
54bool DdJaniModelBuilder<Type, ValueType>::canHandle(storm::jani::Model const& model, boost::optional<std::vector<storm::jani::Property>> const& properties) {
55 // Check jani features
56 auto features = model.getModelFeatures();
57 features.remove(storm::jani::ModelFeature::Arrays); // can be substituted
59 features.remove(storm::jani::ModelFeature::Functions); // can be substituted
62 if (!features.empty()) {
63 STORM_LOG_INFO("Symbolic engine can not build Jani model due to unsupported jani features.");
64 return false;
65 }
66 // Check assignment levels
67 if (model.usesAssignmentLevels()) {
68 STORM_LOG_INFO("Symbolic engine can not build Jani model due to assignment levels.");
69 return false;
70 }
71 // Check nonTrivial reward expressions
72 if (properties) {
73 std::set<std::string> rewardModels;
74 for (auto const& p : properties.get()) {
75 p.gatherReferencedRewardModels(rewardModels);
76 }
77 for (auto const& r : rewardModels) {
79 STORM_LOG_INFO("Symbolic engine can not build Jani model due to non-trivial reward expressions.");
80 return false;
81 }
82 }
83 } else {
85 STORM_LOG_INFO("Symbolic engine can not build Jani model due to non-trivial reward expressions.");
86 return false;
87 }
88 }
89
90 // There probably are more cases where the model is unsupported. However, checking these is often more involved.
91 // As this method is supposed to be a quick check, we just return true at this point.
92 return true;
93}
94
95template<storm::dd::DdType Type, typename ValueType>
96DdJaniModelBuilder<Type, ValueType>::Options::Options(bool buildAllLabels, bool buildAllRewardModels, bool applyMaximumProgressAssumption)
97 : buildAllLabels(buildAllLabels),
98 buildAllRewardModels(buildAllRewardModels),
99 applyMaximumProgressAssumption(applyMaximumProgressAssumption),
100 rewardModelsToBuild(),
101 constantDefinitions() {
102 // Intentionally left empty.
103}
104
105template<storm::dd::DdType Type, typename ValueType>
107 : buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions() {
108 this->preserveFormula(formula);
109 this->setTerminalStatesFromFormula(formula);
110}
111
112template<storm::dd::DdType Type, typename ValueType>
113DdJaniModelBuilder<Type, ValueType>::Options::Options(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas)
114 : buildAllLabels(false), buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions() {
115 if (!formulas.empty()) {
116 for (auto const& formula : formulas) {
117 this->preserveFormula(*formula);
118 }
119 if (formulas.size() == 1) {
120 this->setTerminalStatesFromFormula(*formulas.front());
121 }
122 }
123}
124
125template<storm::dd::DdType Type, typename ValueType>
127 // If we already had terminal states, we need to erase them.
128 terminalStates.clear();
129
130 // If we are not required to build all reward models, we determine the reward models we need to build.
131 if (!buildAllRewardModels) {
132 std::set<std::string> referencedRewardModels = formula.getReferencedRewardModels();
133 rewardModelsToBuild.insert(referencedRewardModels.begin(), referencedRewardModels.end());
134 }
135
136 // Extract all the labels used in the formula.
137 std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>> atomicLabelFormulas = formula.getAtomicLabelFormulas();
138 for (auto const& formula : atomicLabelFormulas) {
139 addLabel(formula->getLabel());
140 }
141}
142
143template<storm::dd::DdType Type, typename ValueType>
147
148template<storm::dd::DdType Type, typename ValueType>
150 return rewardModelsToBuild;
151}
152
153template<storm::dd::DdType Type, typename ValueType>
155 return buildAllRewardModels;
156}
157
158template<storm::dd::DdType Type, typename ValueType>
160 return buildAllLabels;
161}
162
163template<storm::dd::DdType Type, typename ValueType>
165 STORM_LOG_THROW(!buildAllLabels, storm::exceptions::InvalidStateException, "Cannot add label, because all labels are built anyway.");
166 labelNames.insert(labelName);
167}
168
169template<storm::dd::DdType Type, typename ValueType>
171 public:
172 void create(storm::jani::Model const& /*model*/, storm::adapters::AddExpressionAdapter<Type, ValueType>& /*rowExpressionAdapter*/) {
173 // Intentionally left empty: no support for parameters for this data type.
174 }
175
176 std::set<storm::RationalFunctionVariable> const& getParameters() const {
177 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Creating parameters for non-parametric model is not supported.");
178 }
179
180 private:
181};
182
183template<storm::dd::DdType Type>
185 public:
186 ParameterCreator() : cache(std::make_shared<storm::RawPolynomialCache>()) {
187 // Intentionally left empty.
188 }
189
191 for (auto const& constant : model.getConstants()) {
192 if (!constant.isDefined()) {
193 storm::RationalFunctionVariable carlVariable = carl::freshRealVariable(constant.getExpressionVariable().getName());
194 parameters.insert(carlVariable);
195 auto rf = convertVariableToPolynomial(carlVariable);
196 rowExpressionAdapter.setValue(constant.getExpressionVariable(), rf);
197 }
198 }
199 }
200
201 template<typename RationalFunctionType = storm::RationalFunction, typename TP = typename RationalFunctionType::PolyType,
202 carl::EnableIf<carl::needs_cache<TP>> = carl::dummy>
203 RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const& variable) {
204 return RationalFunctionType(typename RationalFunctionType::PolyType(typename RationalFunctionType::PolyType::PolyType(variable), cache));
205 }
206
207 template<typename RationalFunctionType = storm::RationalFunction, typename TP = typename RationalFunctionType::PolyType,
208 carl::DisableIf<carl::needs_cache<TP>> = carl::dummy>
209 RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const& variable) {
210 return RationalFunctionType(variable);
211 }
212
213 std::set<storm::RationalFunctionVariable> const& getParameters() const {
214 return parameters;
215 }
216
217 private:
218 // A mapping from our variables to carl's.
219 std::unordered_map<storm::expressions::Variable, storm::RationalFunctionVariable> variableToVariableMap;
220
221 // The cache that is used in case the underlying type needs a cache.
222 std::shared_ptr<storm::RawPolynomialCache> cache;
223
224 // All created parameters.
225 std::set<storm::RationalFunctionVariable> parameters;
226};
227
228template<storm::dd::DdType Type, typename ValueType>
230 CompositionVariables(std::shared_ptr<storm::dd::DdManager<Type>> const& manager)
231 : manager(manager),
232 variableToRowMetaVariableMap(std::make_shared<std::map<storm::expressions::Variable, storm::expressions::Variable>>()),
233 rowExpressionAdapter(std::make_shared<storm::adapters::AddExpressionAdapter<Type, ValueType>>(manager, variableToRowMetaVariableMap)),
234 variableToColumnMetaVariableMap(std::make_shared<std::map<storm::expressions::Variable, storm::expressions::Variable>>()) {
235 // Intentionally left empty.
236 }
237
238 std::shared_ptr<storm::dd::DdManager<Type>> manager;
239
240 // The meta variables for the row encoding.
241 std::set<storm::expressions::Variable> rowMetaVariables;
242 std::shared_ptr<std::map<storm::expressions::Variable, storm::expressions::Variable>> variableToRowMetaVariableMap;
243 std::shared_ptr<storm::adapters::AddExpressionAdapter<Type, ValueType>> rowExpressionAdapter;
244
245 // The meta variables for the column encoding.
246 std::set<storm::expressions::Variable> columnMetaVariables;
247 std::shared_ptr<std::map<storm::expressions::Variable, storm::expressions::Variable>> variableToColumnMetaVariableMap;
248
249 // All pairs of row/column meta variables.
250 std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> rowColumnMetaVariablePairs;
251
252 // A mapping from automata to the meta variables encoding their location.
253 std::map<std::string, std::pair<storm::expressions::Variable, storm::expressions::Variable>> automatonToLocationDdVariableMap;
254
255 // A mapping from action indices to the meta variables used to encode these actions.
256 std::map<uint64_t, storm::expressions::Variable> actionVariablesMap;
257
258 // The meta variables used to encode the remaining nondeterminism.
259 std::vector<storm::expressions::Variable> localNondeterminismVariables;
260
261 // The meta variable used to distinguish Markovian from probabilistic choices in Markov automata.
264
265 // The meta variables used to encode the actions and nondeterminism.
266 std::set<storm::expressions::Variable> allNondeterminismVariables;
267
268 // DDs representing the identity for each variable.
269 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> variableToIdentityMap;
270
271 // DDs representing the ranges of each variable.
272 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> variableToRangeMap;
273
274 // A set of all meta variables that correspond to global variables.
275 std::set<storm::expressions::Variable> allGlobalVariables;
276
277 // DDs representing the identity for each automaton.
278 std::map<std::string, storm::dd::Add<Type, ValueType>> automatonToIdentityMap;
279
280 // DDs representing the valid ranges of the variables of each automaton.
281 std::map<std::string, storm::dd::Add<Type, ValueType>> automatonToRangeMap;
282
283 // A DD representing the valid ranges of the global variables.
285
286 // The parameters that appear in the model.
287 std::set<storm::RationalFunctionVariable> parameters;
288};
289
290// A class responsible for creating the necessary variables for a subsequent composition of automata.
291template<storm::dd::DdType Type, typename ValueType>
293 public:
295 : model(model), automata(), actionInformation(actionInformation) {
296 // Intentionally left empty.
297 }
298
300 // First, check whether every automaton appears exactly once in the system composition. Simultaneously,
301 // we determine the set of non-silent actions used by the composition.
302 automata.clear();
303 this->model.getSystemComposition().accept(*this, boost::none);
304 STORM_LOG_THROW(automata.size() == this->model.getNumberOfAutomata(), storm::exceptions::InvalidArgumentException,
305 "Cannot build symbolic model from JANI model whose system composition refers to a subset of automata.");
306
307 STORM_LOG_THROW(!this->model.hasTransientEdgeDestinationAssignments(), storm::exceptions::InvalidArgumentException,
308 "The symbolic JANI model builder currently does not support transient edge destination assignments.");
309
310 // Then, check that the model does not contain non-transient unbounded integer or non-transient real variables.
311 STORM_LOG_THROW(!this->model.getGlobalVariables().containsNonTransientUnboundedIntegerVariables(), storm::exceptions::InvalidArgumentException,
312 "Cannot build symbolic model from JANI model that contains non-transient global unbounded integer variables.");
313 STORM_LOG_THROW(!this->model.getGlobalVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException,
314 "Cannot build symbolic model from JANI model that contains global non-transient real variables.");
315 for (auto const& automaton : this->model.getAutomata()) {
316 STORM_LOG_THROW(!automaton.getVariables().containsNonTransientUnboundedIntegerVariables(), storm::exceptions::InvalidArgumentException,
317 "Cannot build symbolic model from JANI model that contains non-transient unbounded integer variables in automaton '"
318 << automaton.getName() << "'.");
320 !automaton.getVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException,
321 "Cannot build symbolic model from JANI model that contains non-transient real variables in automaton '" << automaton.getName() << "'.");
322 }
323
324 // Based on this assumption, we create the variables.
325 return createVariables(manager);
326 }
327
328 boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const&) override {
329 auto it = automata.find(composition.getAutomatonName());
330 STORM_LOG_THROW(it == automata.end(), storm::exceptions::InvalidArgumentException,
331 "Cannot build symbolic model from JANI model whose system composition refers to the automaton '" << composition.getAutomatonName()
332 << "' multiple times.");
333 automata.insert(it, composition.getAutomatonName());
334 return boost::none;
335 }
336
337 boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override {
338 for (auto const& subcomposition : composition.getSubcompositions()) {
339 subcomposition->accept(*this, data);
340 }
341 return boost::none;
342 }
343
344 private:
345 CompositionVariables<Type, ValueType> createVariables(std::shared_ptr<storm::dd::DdManager<Type>> const& manager) {
347
348 for (auto const& nonSilentActionIndex : actionInformation.getNonSilentActionIndices()) {
349 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair =
350 result.manager->addMetaVariable(actionInformation.getActionName(nonSilentActionIndex));
351 result.actionVariablesMap[nonSilentActionIndex] = variablePair.first;
352 result.allNondeterminismVariables.insert(variablePair.first);
353 }
354
355 // FIXME: check how many nondeterminism variables we should actually allocate.
356 uint64_t numberOfNondeterminismVariables = this->model.getNumberOfAutomata();
357 for (auto const& automaton : this->model.getAutomata()) {
358 numberOfNondeterminismVariables += automaton.getNumberOfEdges();
359 }
360 for (uint_fast64_t i = 0; i < numberOfNondeterminismVariables; ++i) {
361 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair = result.manager->addMetaVariable("nondet" + std::to_string(i));
362 result.localNondeterminismVariables.push_back(variablePair.first);
363 result.allNondeterminismVariables.insert(variablePair.first);
364 }
365
366 if (this->model.getModelType() == storm::jani::ModelType::MA) {
367 result.probabilisticNondeterminismVariable = result.manager->addMetaVariable("prob").first;
368 result.probabilisticMarker = result.manager->getEncoding(result.probabilisticNondeterminismVariable, 1);
369 result.allNondeterminismVariables.insert(result.probabilisticNondeterminismVariable);
370 }
371
372 for (auto const& automatonName : this->automata) {
373 storm::jani::Automaton const& automaton = this->model.getAutomaton(automatonName);
374
375 // Start by creating a meta variable for the location of the automaton.
376 storm::expressions::Variable locationExpressionVariable = automaton.getLocationExpressionVariable();
377 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair =
378 result.manager->addMetaVariable("l_" + automaton.getName(), 0, automaton.getNumberOfLocations() - 1);
379 result.automatonToLocationDdVariableMap[automaton.getName()] = variablePair;
380 result.rowColumnMetaVariablePairs.push_back(variablePair);
381
382 result.variableToRowMetaVariableMap->emplace(locationExpressionVariable, variablePair.first);
383 result.variableToColumnMetaVariableMap->emplace(locationExpressionVariable, variablePair.second);
384
385 // Add the location variable to the row/column variables.
386 result.rowMetaVariables.insert(variablePair.first);
387 result.columnMetaVariables.insert(variablePair.second);
388
389 // Add the legal range for the location variables.
390 result.variableToRangeMap.emplace(variablePair.first, result.manager->getRange(variablePair.first));
391 result.variableToRangeMap.emplace(variablePair.second, result.manager->getRange(variablePair.second));
392 }
393
394 // Create global variables.
395 storm::dd::Bdd<Type> globalVariableRanges = result.manager->getBddOne();
396 for (auto const& variable : this->model.getGlobalVariables()) {
397 // Only create the variable if it's non-transient.
398 if (variable.isTransient()) {
399 continue;
400 }
401
402 createVariable(variable, result);
403 globalVariableRanges &= result.manager->getRange(result.variableToRowMetaVariableMap->at(variable.getExpressionVariable()));
404 }
405 result.globalVariableRanges = globalVariableRanges.template toAdd<ValueType>();
406
407 // Create the variables for the individual automata.
408 for (auto const& automaton : this->model.getAutomata()) {
409 storm::dd::Bdd<Type> identity = result.manager->getBddOne();
410 storm::dd::Bdd<Type> range = result.manager->getBddOne();
411
412 // Add the identity and ranges of the location variables to the ones of the automaton.
413 std::pair<storm::expressions::Variable, storm::expressions::Variable> const& locationVariables =
414 result.automatonToLocationDdVariableMap[automaton.getName()];
415 storm::dd::Bdd<Type> variableIdentity = result.manager->getIdentity(locationVariables.first, locationVariables.second);
416 identity &= variableIdentity;
417 range &= result.manager->getRange(locationVariables.first);
418
419 // Then create variables for the variables of the automaton.
420 for (auto const& variable : automaton.getVariables()) {
421 // Only create the variable if it's non-transient.
422 if (variable.isTransient()) {
423 continue;
424 }
425
426 createVariable(variable, result);
427 identity &= result.variableToIdentityMap.at(variable.getExpressionVariable()).toBdd();
428 range &= result.manager->getRange(result.variableToRowMetaVariableMap->at(variable.getExpressionVariable()));
429 }
430
431 result.automatonToIdentityMap[automaton.getName()] = identity.template toAdd<ValueType>();
432 result.automatonToRangeMap[automaton.getName()] = (range && globalVariableRanges).template toAdd<ValueType>();
433 }
434
435 ParameterCreator<Type, ValueType> parameterCreator;
436 parameterCreator.create(model, *result.rowExpressionAdapter);
437 if (std::is_same<ValueType, storm::RationalFunction>::value) {
438 result.parameters = parameterCreator.getParameters();
439 }
440
441 return result;
442 }
443
444 void createVariable(storm::jani::Variable const& variable, CompositionVariables<Type, ValueType>& result) {
445 auto const& type = variable.getType();
446 if (type.isBasicType() && type.asBasicType().isBooleanType()) {
447 createBooleanVariable(variable, result);
448 } else if (type.isBoundedType() && type.asBoundedType().isIntegerType()) {
449 createBoundedIntegerVariable(variable, result);
450 } else {
451 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Invalid type of variable in JANI model.");
452 }
453 }
454
455 void createBoundedIntegerVariable(storm::jani::Variable const& variable, CompositionVariables<Type, ValueType>& result) {
456 auto const& type = variable.getType().asBoundedType();
457 STORM_LOG_THROW(type.hasLowerBound(), storm::exceptions::NotSupportedException,
458 "DdJaniModelBuilder only supports bounded variables. Variable " << variable.getName() << " has no lower bound.");
459 STORM_LOG_THROW(type.hasUpperBound(), storm::exceptions::NotSupportedException,
460 "DdJaniModelBuilder only supports bounded variables. Variable " << variable.getName() << " has no upper bound.");
461 int_fast64_t low = type.getLowerBound().evaluateAsInt();
462 int_fast64_t high = type.getUpperBound().evaluateAsInt();
463
464 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair =
465 result.manager->addMetaVariable(variable.getExpressionVariable().getName(), low, high);
466
467 STORM_LOG_TRACE("Created meta variables for global integer variable: " << variablePair.first.getName() << " and " << variablePair.second.getName()
468 << ".");
469
470 result.rowMetaVariables.insert(variablePair.first);
471 result.variableToRowMetaVariableMap->emplace(variable.getExpressionVariable(), variablePair.first);
472
473 result.columnMetaVariables.insert(variablePair.second);
474 result.variableToColumnMetaVariableMap->emplace(variable.getExpressionVariable(), variablePair.second);
475
476 storm::dd::Bdd<Type> variableIdentity = result.manager->getIdentity(variablePair.first, variablePair.second);
477 result.variableToIdentityMap.emplace(variable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>());
478 result.rowColumnMetaVariablePairs.push_back(variablePair);
479 result.variableToRangeMap.emplace(variablePair.first, result.manager->getRange(variablePair.first));
480 result.variableToRangeMap.emplace(variablePair.second, result.manager->getRange(variablePair.second));
481
482 result.allGlobalVariables.insert(variable.getExpressionVariable());
483 }
484
485 void createBooleanVariable(storm::jani::Variable const& variable, CompositionVariables<Type, ValueType>& result) {
486 std::pair<storm::expressions::Variable, storm::expressions::Variable> variablePair =
487 result.manager->addMetaVariable(variable.getExpressionVariable().getName());
488
489 STORM_LOG_TRACE("Created meta variables for global boolean variable: " << variablePair.first.getName() << " and " << variablePair.second.getName()
490 << ".");
491
492 result.rowMetaVariables.insert(variablePair.first);
493 result.variableToRowMetaVariableMap->emplace(variable.getExpressionVariable(), variablePair.first);
494
495 result.columnMetaVariables.insert(variablePair.second);
496 result.variableToColumnMetaVariableMap->emplace(variable.getExpressionVariable(), variablePair.second);
497
498 storm::dd::Bdd<Type> variableIdentity = result.manager->getIdentity(variablePair.first, variablePair.second);
499 result.variableToIdentityMap.emplace(variable.getExpressionVariable(), variableIdentity.template toAdd<ValueType>());
500
501 result.variableToRangeMap.emplace(variablePair.first, result.manager->getRange(variablePair.first));
502 result.variableToRangeMap.emplace(variablePair.second, result.manager->getRange(variablePair.second));
503
504 result.rowColumnMetaVariablePairs.push_back(variablePair);
505 result.allGlobalVariables.insert(variable.getExpressionVariable());
506 }
507
508 storm::jani::Model const& model;
509 std::set<std::string> automata;
510 storm::jani::CompositionInformation actionInformation;
511};
512
513template<storm::dd::DdType Type, typename ValueType>
516 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientLocationAssignments,
517 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments,
518 storm::dd::Bdd<Type> const& illegalFragment, uint64_t numberOfNondeterminismVariables = 0)
519 : transitions(transitions),
520 transientLocationAssignments(transientLocationAssignments),
521 transientEdgeAssignments(transientEdgeAssignments),
522 illegalFragment(illegalFragment),
523 numberOfNondeterminismVariables(numberOfNondeterminismVariables) {
524 // Intentionally left empty.
525 }
526
528 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientLocationAssignments;
529 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
532};
533
534// A class that is responsible for performing the actual composition. This
535template<storm::dd::DdType Type, typename ValueType>
537 public:
539 std::vector<storm::expressions::Variable> const& transientVariables)
540 : model(model), variables(variables), transientVariables(transientVariables) {
541 // Intentionally left empty.
542 }
543
545
546 protected:
547 // The model that is referred to by the composition.
549
550 // The variable to use when building an automaton.
552
553 // The transient variables to consider during system composition.
554 std::vector<storm::expressions::Variable> transientVariables;
555};
556
557// This structure represents an edge destination.
558template<storm::dd::DdType Type, typename ValueType>
560 EdgeDestinationDd(storm::dd::Add<Type, ValueType> const& transitions, std::set<storm::expressions::Variable> const& writtenGlobalVariables = {})
561 : transitions(transitions), writtenGlobalVariables(writtenGlobalVariables) {
562 // Intentionally left empty.
563 }
564
566 std::set<storm::expressions::Variable> writtenGlobalVariables;
567};
568
569template<storm::dd::DdType Type, typename ValueType>
571 storm::dd::Bdd<Type> const& guard, CompositionVariables<Type, ValueType> const& variables) {
572 storm::dd::Add<Type, ValueType> transitions = variables.rowExpressionAdapter->translateExpression(destination.getProbability());
573
574 STORM_LOG_TRACE("Translating edge destination.");
575
576 // Iterate over all assignments (boolean and integer) and build the DD for it.
577 std::set<storm::expressions::Variable> assignedVariables;
578 for (auto const& assignment : destination.getOrderedAssignments().getNonTransientAssignments()) {
579 // Record the variable as being written.
580 STORM_LOG_TRACE("Assigning to variable " << variables.variableToRowMetaVariableMap->at(assignment.getExpressionVariable()).getName());
581 assignedVariables.insert(assignment.getExpressionVariable());
582
583 // Translate the written variable.
584 auto const& primedMetaVariable = variables.variableToColumnMetaVariableMap->at(assignment.getExpressionVariable());
585 storm::dd::Add<Type, ValueType> writtenVariable = variables.manager->template getIdentity<ValueType>(primedMetaVariable);
586
587 // Translate the expression that is being assigned.
588 storm::dd::Add<Type, ValueType> assignedExpression = variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression());
589
590 // Combine the assigned expression with the guard.
591 storm::dd::Add<Type, ValueType> result = assignedExpression * guard.template toAdd<ValueType>();
592
593 // Combine the variable and the assigned expression.
594 result = result.equals(writtenVariable).template toAdd<ValueType>();
595 result *= guard.template toAdd<ValueType>();
596
597 // Restrict the transitions to the range of the written variable.
598 result = result * variables.variableToRangeMap.at(primedMetaVariable).template toAdd<ValueType>();
599
600 // Combine the assignment DDs.
601 transitions *= result;
602 }
603
604 // Compute the set of assigned global variables.
605 std::set<storm::expressions::Variable> assignedGlobalVariables;
606 std::set_intersection(assignedVariables.begin(), assignedVariables.end(), variables.allGlobalVariables.begin(), variables.allGlobalVariables.end(),
607 std::inserter(assignedGlobalVariables, assignedGlobalVariables.begin()));
608
609 // All unassigned boolean variables need to keep their value.
610 for (storm::jani::Variable const& variable : automaton.getVariables().getBooleanVariables()) {
611 if (assignedVariables.find(variable.getExpressionVariable()) == assignedVariables.end()) {
612 STORM_LOG_TRACE("Multiplying identity of variable " << variable.getName());
613 transitions *= variables.variableToIdentityMap.at(variable.getExpressionVariable());
614 }
615 }
616
617 // All unassigned integer variables need to keep their value.
618 for (storm::jani::Variable const& variable : automaton.getVariables().getBoundedIntegerVariables()) {
619 if (assignedVariables.find(variable.getExpressionVariable()) == assignedVariables.end()) {
620 STORM_LOG_TRACE("Multiplying identity of variable " << variable.getName());
621 transitions *= variables.variableToIdentityMap.at(variable.getExpressionVariable());
622 }
623 }
624
625 transitions *= variables.manager->getEncoding(variables.automatonToLocationDdVariableMap.at(automaton.getName()).second, destination.getLocationIndex())
626 .template toAdd<ValueType>();
627
628 return EdgeDestinationDd<Type, ValueType>(transitions, assignedGlobalVariables);
629}
630
631template<storm::dd::DdType Type, typename ValueType>
632storm::dd::Add<Type, ValueType> encodeAction(boost::optional<uint64_t> const& actionIndex, boost::optional<bool> const& markovian,
633 CompositionVariables<Type, ValueType> const& variables) {
634 storm::dd::Add<Type, ValueType> encoding = variables.manager->template getAddOne<ValueType>();
635
636 for (auto it = variables.actionVariablesMap.rbegin(), ite = variables.actionVariablesMap.rend(); it != ite; ++it) {
637 if (actionIndex && it->first == actionIndex.get()) {
638 encoding *= variables.manager->getEncoding(it->second, 1).template toAdd<ValueType>();
639 } else {
640 encoding *= variables.manager->getEncoding(it->second, 0).template toAdd<ValueType>();
641 }
642 }
643
644 if (markovian) {
645 if (markovian.get()) {
646 encoding *= (!variables.probabilisticMarker).template toAdd<ValueType>();
647 } else {
648 encoding *= variables.probabilisticMarker.template toAdd<ValueType>();
649 }
650 }
651
652 return encoding;
653}
654
655template<storm::dd::DdType Type, typename ValueType>
656storm::dd::Add<Type, ValueType> encodeIndex(uint64_t index, uint64_t localNondeterminismVariableOffset, uint64_t numberOfLocalNondeterminismVariables,
657 CompositionVariables<Type, ValueType> const& variables) {
658 storm::dd::Add<Type, ValueType> result = variables.manager->template getAddZero<ValueType>();
659
660 std::map<storm::expressions::Variable, int_fast64_t> metaVariableNameToValueMap;
661 for (uint_fast64_t i = 0; i < numberOfLocalNondeterminismVariables; ++i) {
662 if (index & (1ull << (numberOfLocalNondeterminismVariables - i - 1))) {
663 metaVariableNameToValueMap.emplace(variables.localNondeterminismVariables[localNondeterminismVariableOffset + i], 1);
664 } else {
665 metaVariableNameToValueMap.emplace(variables.localNondeterminismVariables[localNondeterminismVariableOffset + i], 0);
666 }
667 }
668
669 result.setValue(metaVariableNameToValueMap, storm::utility::one<ValueType>());
670 return result;
671}
672
673template<storm::dd::DdType Type, typename ValueType>
674class CombinedEdgesSystemComposer : public SystemComposer<Type, ValueType> {
675 public:
676 // This structure represents an edge.
677 struct EdgeDd {
678 EdgeDd(bool isMarkovian, storm::dd::Bdd<Type> const& guard, storm::dd::Add<Type, ValueType> const& transitions,
679 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments,
680 std::set<storm::expressions::Variable> const& writtenGlobalVariables)
681 : isMarkovian(isMarkovian),
682 guard(guard),
683 transitions(transitions),
684 transientEdgeAssignments(transientEdgeAssignments),
685 variableToWritingFragment() {
686 // Convert the set of written variables to a mapping from variable to the writing fragments.
687 for (auto const& variable : writtenGlobalVariables) {
688 variableToWritingFragment[variable] = guard;
689 }
690 }
691
692 EdgeDd(bool isMarkovian, storm::dd::Bdd<Type> const& guard, storm::dd::Add<Type, ValueType> const& transitions,
693 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments,
694 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& variableToWritingFragment)
695 : isMarkovian(isMarkovian),
696 guard(guard),
697 transitions(transitions),
698 transientEdgeAssignments(transientEdgeAssignments),
699 variableToWritingFragment(variableToWritingFragment) {
700 // Intentionally left empty.
701 }
702
703 // A flag storing whether this edge is a Markovian one (i.e. one with a rate).
705
706 // A DD that represents all states that have this edge enabled.
708
709 // A DD that represents the transitions of this edge.
711
712 // A mapping from transient variables to the DDs representing their value assignments.
713 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
714
715 // A mapping of variables to the variables to the fragment of transitions that is writing the corresponding variable.
716 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> variableToWritingFragment;
717 };
718
719 // This structure represents an edge.
720 struct ActionDd {
723 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientEdgeAssignments = {},
724 std::pair<uint64_t, uint64_t> localNondeterminismVariables = std::pair<uint64_t, uint64_t>(0, 0),
725 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& variableToWritingFragment = {},
726 storm::dd::Bdd<Type> const& illegalFragment = storm::dd::Bdd<Type>())
727 : guard(guard),
728 transitions(transitions),
729 transientEdgeAssignments(transientEdgeAssignments),
730 localNondeterminismVariables(localNondeterminismVariables),
731 variableToWritingFragment(variableToWritingFragment),
732 illegalFragment(illegalFragment),
733 inputEnabled(false) {
734 // Intentionally left empty.
735 }
736
738 return localNondeterminismVariables.first;
739 }
740
742 return localNondeterminismVariables.second;
743 }
744
745 std::pair<uint64_t, uint64_t> const& getLocalNondeterminismVariables() const {
746 return localNondeterminismVariables;
747 }
748
750 return ActionDd(guard, transitions * factor, transientEdgeAssignments, localNondeterminismVariables, variableToWritingFragment, illegalFragment);
751 }
752
753 ActionDd add(ActionDd const& other) const {
754 storm::dd::Bdd<Type> newGuard = this->guard || other.guard;
755 storm::dd::Add<Type, ValueType> newTransitions = this->transitions + other.transitions;
756
757 // Join the transient edge assignments.
758 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> newTransientEdgeAssignments(this->transientEdgeAssignments);
759 for (auto const& entry : other.transientEdgeAssignments) {
760 auto it = newTransientEdgeAssignments.find(entry.first);
761 if (it == newTransientEdgeAssignments.end()) {
762 newTransientEdgeAssignments[entry.first] = entry.second;
763 } else {
764 it->second += entry.second;
765 }
766 }
767
768 std::pair<uint64_t, uint64_t> newLocalNondeterminismVariables =
769 std::make_pair(std::min(this->localNondeterminismVariables.first, other.localNondeterminismVariables.first),
770 std::max(this->localNondeterminismVariables.second, other.localNondeterminismVariables.second));
771
772 // Join variable-to-writing-fragment maps.
773 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> newVariableToWritingFragment(this->variableToWritingFragment);
774 for (auto const& entry : other.variableToWritingFragment) {
775 auto it = newVariableToWritingFragment.find(entry.first);
776 if (it == newVariableToWritingFragment.end()) {
777 newVariableToWritingFragment[entry.first] = entry.second;
778 } else {
779 it->second |= entry.second;
780 }
781 }
782
783 // Join illegal fragments.
784 storm::dd::Bdd<Type> newIllegalFragment = this->illegalFragment || other.illegalFragment;
785
786 return ActionDd(newGuard, newTransitions, newTransientEdgeAssignments, newLocalNondeterminismVariables, newVariableToWritingFragment,
787 newIllegalFragment);
788 }
789
794 guard &= condition;
795 storm::dd::Add<Type, ValueType> conditionAdd = condition.template toAdd<ValueType>();
796 transitions *= conditionAdd;
797 for (auto& t : transientEdgeAssignments) {
798 t.second *= conditionAdd;
799 }
800 illegalFragment &= condition;
801 }
802
803 bool isInputEnabled() const {
804 return inputEnabled;
805 }
806
808 inputEnabled = true;
809 }
810
811 // A DD that represents all states that have this action enabled.
813
814 // A DD that represents the transitions of this action.
816
817 // A mapping from transient variables to their assignments.
818 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
819
820 // The local nondeterminism variables used by this action DD, given as the lowest
821 std::pair<uint64_t, uint64_t> localNondeterminismVariables;
822
823 // A mapping from global variables to a DD that characterizes choices (nondeterminism variables) in
824 // states that write to this global variable.
825 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> variableToWritingFragment;
826
827 // A DD characterizing the fragment of the states satisfying the guard that are illegal because
828 // there are synchronizing edges enabled that write to the same global variable.
830
831 // A flag storing whether this action is input-enabled.
833 };
834
836 ActionIdentification(uint64_t actionIndex, bool markovian = false)
837 : actionIndex(actionIndex), synchronizationVectorIndex(boost::none), markovian(markovian) {
838 // Intentionally left empty.
839 }
840
841 ActionIdentification(uint64_t actionIndex, uint64_t synchronizationVectorIndex, bool markovian = false)
842 : actionIndex(actionIndex), synchronizationVectorIndex(synchronizationVectorIndex), markovian(markovian) {
843 // Intentionally left empty.
844 }
845
846 ActionIdentification(uint64_t actionIndex, boost::optional<uint64_t> synchronizationVectorIndex, bool markovian = false)
847 : actionIndex(actionIndex), synchronizationVectorIndex(synchronizationVectorIndex), markovian(markovian) {
848 // Intentionally left empty.
849 }
850
851 void setMarkovian(bool markovian) {
852 this->markovian = markovian;
853 }
854
855 bool isMarkovian() const {
856 return this->markovian;
857 }
858
859 bool operator==(ActionIdentification const& other) const {
860 bool result = actionIndex == other.actionIndex && markovian == other.markovian;
861 if (synchronizationVectorIndex) {
862 if (other.synchronizationVectorIndex) {
863 result &= synchronizationVectorIndex.get() == other.synchronizationVectorIndex.get();
864 } else {
865 result = false;
866 }
867 } else {
868 if (other.synchronizationVectorIndex) {
869 result = false;
870 }
871 }
872 return result;
873 }
874
875 uint64_t actionIndex;
876 boost::optional<uint64_t> synchronizationVectorIndex;
878 };
879
881 std::size_t operator()(ActionIdentification const& identification) const {
882 std::size_t seed = 0;
883 boost::hash_combine(seed, identification.actionIndex);
884 if (identification.synchronizationVectorIndex) {
885 boost::hash_combine(seed, identification.synchronizationVectorIndex.get());
886 }
887 return identification.markovian ? ~seed : seed;
888 }
889 };
890
891 // This structure represents a subcomponent of a composition.
892 struct AutomatonDd {
894 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientLocationAssignments = {})
895 : actions(),
896 transientLocationAssignments(transientLocationAssignments),
897 identity(identity),
898 localNondeterminismVariables(std::make_pair<uint64_t, uint64_t>(0, 0)) {
899 // Intentionally left empty.
900 }
901
903 return localNondeterminismVariables.first;
904 }
905
906 void setLowestLocalNondeterminismVariable(uint64_t newValue) {
907 localNondeterminismVariables.first = newValue;
908 }
909
911 return localNondeterminismVariables.second;
912 }
913
915 localNondeterminismVariables.second = newValue;
916 }
917
918 void extendLocalNondeterminismVariables(std::pair<uint64_t, uint64_t> const& localNondeterminismVariables) {
919 setLowestLocalNondeterminismVariable(std::min(localNondeterminismVariables.first, getLowestLocalNondeterminismVariable()));
920 setHighestLocalNondeterminismVariable(std::max(localNondeterminismVariables.second, getHighestLocalNondeterminismVariable()));
921 }
922
923 // A mapping from action identifications to the action DDs.
924 std::unordered_map<ActionIdentification, ActionDd, ActionIdentificationHash> actions;
925
926 // A mapping from transient variables to their location-based transient assignment values.
927 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientLocationAssignments;
928
929 // The identity of the automaton's variables.
931
932 // The local nondeterminism variables used by this action DD, given as the lowest and highest variable index.
933 std::pair<uint64_t, uint64_t> localNondeterminismVariables;
934 };
935
937 CompositionVariables<Type, ValueType> const& variables, std::vector<storm::expressions::Variable> const& transientVariables,
938 bool applyMaximumProgress)
939 : SystemComposer<Type, ValueType>(model, variables, transientVariables),
940 actionInformation(actionInformation),
941 applyMaximumProgress(applyMaximumProgress) {
942 // Intentionally left empty.
943 }
944
947
949 STORM_LOG_THROW(this->model.hasStandardCompliantComposition(), storm::exceptions::WrongFormatException,
950 "Model builder only supports non-nested parallel compositions.");
951 AutomatonDd globalAutomaton = boost::any_cast<AutomatonDd>(this->model.getSystemComposition().accept(*this, boost::any()));
952 return buildSystemFromAutomaton(globalAutomaton);
953 }
954
956 ActionInstantiation(uint64_t actionIndex, uint64_t synchronizationVectorIndex, uint64_t localNondeterminismVariableOffset, bool markovian = false)
957 : actionIndex(actionIndex),
958 synchronizationVectorIndex(synchronizationVectorIndex),
959 localNondeterminismVariableOffset(localNondeterminismVariableOffset),
960 markovian(markovian) {
961 // Intentionally left empty.
962 }
963
964 ActionInstantiation(uint64_t actionIndex, uint64_t localNondeterminismVariableOffset, bool markovian = false)
965 : actionIndex(actionIndex), localNondeterminismVariableOffset(localNondeterminismVariableOffset), markovian(markovian) {
966 // Intentionally left empty.
967 }
968
969 void setMarkovian(bool markovian) {
970 this->markovian = markovian;
971 }
972
973 bool isMarkovian() const {
974 return this->markovian;
975 }
976
977 bool operator==(ActionInstantiation const& other) const {
978 bool result = actionIndex == other.actionIndex && markovian == other.markovian;
979 result &= localNondeterminismVariableOffset == other.localNondeterminismVariableOffset;
980 if (synchronizationVectorIndex) {
981 if (!other.synchronizationVectorIndex) {
982 result = false;
983 } else {
984 result &= synchronizationVectorIndex.get() == other.synchronizationVectorIndex.get();
985 }
986 } else {
987 if (other.synchronizationVectorIndex) {
988 result = false;
989 }
990 }
991 return result;
992 }
993
994 uint64_t actionIndex;
995 boost::optional<uint64_t> synchronizationVectorIndex;
998 };
999
1001 std::size_t operator()(ActionInstantiation const& instantiation) const {
1002 std::size_t seed = 0;
1003 boost::hash_combine(seed, instantiation.actionIndex);
1004 boost::hash_combine(seed, instantiation.localNondeterminismVariableOffset);
1005 if (instantiation.synchronizationVectorIndex) {
1006 boost::hash_combine(seed, instantiation.synchronizationVectorIndex.get());
1007 }
1008 return instantiation.isMarkovian() ? ~seed : seed;
1009 }
1010 };
1011
1012 typedef std::map<uint64_t, std::vector<ActionInstantiation>> ActionInstantiations;
1013
1014 boost::any visit(storm::jani::AutomatonComposition const& composition, boost::any const& data) override {
1015 ActionInstantiations actionInstantiations;
1016 if (data.empty()) {
1017 // If no data was provided, this is the top level element in which case we build the full automaton.
1018 bool isCtmc = this->model.getModelType() == storm::jani::ModelType::CTMC;
1019
1020 for (auto const& actionIndex : actionInformation.getNonSilentActionIndices()) {
1021 actionInstantiations[actionIndex].emplace_back(actionIndex, 0, isCtmc);
1022 }
1023 actionInstantiations[storm::jani::Model::SILENT_ACTION_INDEX].emplace_back(storm::jani::Model::SILENT_ACTION_INDEX, 0, isCtmc);
1024 if (this->model.getModelType() == storm::jani::ModelType::MA) {
1025 actionInstantiations[storm::jani::Model::SILENT_ACTION_INDEX].emplace_back(storm::jani::Model::SILENT_ACTION_INDEX, 0, true);
1026 }
1027 }
1028
1029 std::set<uint64_t> inputEnabledActionIndices;
1030 for (auto const& actionName : composition.getInputEnabledActions()) {
1031 inputEnabledActionIndices.insert(actionInformation.getActionIndex(actionName));
1032 }
1033
1034 return buildAutomatonDd(composition.getAutomatonName(), data.empty() ? actionInstantiations : boost::any_cast<ActionInstantiations const&>(data),
1035 inputEnabledActionIndices, data.empty());
1036 }
1037
1038 boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override {
1039 STORM_LOG_ASSERT(data.empty(), "Expected parallel composition to be on topmost level to be JANI compliant.");
1040
1041 bool isCtmc = this->model.getModelType() == storm::jani::ModelType::CTMC;
1042
1043 // Prepare storage for the subautomata of the composition.
1044 std::vector<AutomatonDd> subautomata;
1045
1046 // The outer loop iterates over the indices of the subcomposition, because the first subcomposition needs
1047 // to be built before the second and so on.
1048 uint64_t silentActionIndex = actionInformation.getActionIndex(storm::jani::Model::SILENT_ACTION_NAME);
1049 for (uint64_t subcompositionIndex = 0; subcompositionIndex < composition.getNumberOfSubcompositions(); ++subcompositionIndex) {
1050 // Now build a new set of action instantiations for the current subcomposition index.
1051 ActionInstantiations actionInstantiations;
1052 actionInstantiations[silentActionIndex].emplace_back(silentActionIndex, 0, isCtmc);
1053 if (this->model.getModelType() == storm::jani::ModelType::MA) {
1054 actionInstantiations[storm::jani::Model::SILENT_ACTION_INDEX].emplace_back(silentActionIndex, 0, true);
1055 }
1056
1057 for (uint64_t synchronizationVectorIndex = 0; synchronizationVectorIndex < composition.getNumberOfSynchronizationVectors();
1058 ++synchronizationVectorIndex) {
1059 auto const& synchVector = composition.getSynchronizationVector(synchronizationVectorIndex);
1060
1061 // Determine the first participating subcomposition, because we need to build the corresponding action
1062 // from all local nondeterminism variable offsets that the output action of the synchronization vector
1063 // is required to have.
1064 if (subcompositionIndex == synchVector.getPositionOfFirstParticipatingAction()) {
1065 uint64_t actionIndex = actionInformation.getActionIndex(synchVector.getInput(subcompositionIndex));
1066 actionInstantiations[actionIndex].emplace_back(actionIndex, synchronizationVectorIndex, 0, isCtmc);
1067 } else if (synchVector.getInput(subcompositionIndex) != storm::jani::SynchronizationVector::NO_ACTION_INPUT) {
1068 uint64_t actionIndex = actionInformation.getActionIndex(synchVector.getInput(subcompositionIndex));
1069
1070 // If this subcomposition is participating in the synchronization vector, but it's not the first
1071 // such subcomposition, then we have to retrieve the offset we need for the participating action
1072 // by looking at the maximal offset used by the preceding participating action.
1073 boost::optional<uint64_t> previousActionPosition = synchVector.getPositionOfPrecedingParticipatingAction(subcompositionIndex);
1074 STORM_LOG_ASSERT(previousActionPosition, "Inconsistent information about synchronization vector.");
1075 AutomatonDd const& previousAutomatonDd = subautomata[previousActionPosition.get()];
1076 auto precedingActionIndex = actionInformation.getActionIndex(synchVector.getInput(previousActionPosition.get()));
1077 auto precedingActionIt = previousAutomatonDd.actions.find(ActionIdentification(precedingActionIndex, synchronizationVectorIndex, isCtmc));
1078
1079 uint64_t highestLocalNondeterminismVariable = 0;
1080 if (precedingActionIt != previousAutomatonDd.actions.end()) {
1081 highestLocalNondeterminismVariable = precedingActionIt->second.getHighestLocalNondeterminismVariable();
1082 } else {
1083 STORM_LOG_WARN("Subcomposition does not have action" << actionInformation.getActionName(precedingActionIndex)
1084 << " that is mentioned in parallel composition.");
1085 }
1086 actionInstantiations[actionIndex].emplace_back(actionIndex, synchronizationVectorIndex, highestLocalNondeterminismVariable, isCtmc);
1087 }
1088 }
1089
1090 subautomata.push_back(boost::any_cast<AutomatonDd>(composition.getSubcomposition(subcompositionIndex).accept(*this, actionInstantiations)));
1091 }
1092
1093 return composeInParallel(subautomata, composition.getSynchronizationVectors());
1094 }
1095
1096 private:
1097 AutomatonDd composeInParallel(std::vector<AutomatonDd> const& subautomata, std::vector<storm::jani::SynchronizationVector> const& synchronizationVectors) {
1098 AutomatonDd result(this->variables.manager->template getAddOne<ValueType>());
1099
1100 // Disjunction of all guards of non-markovian actions (only required for maximum progress assumption.
1101 storm::dd::Bdd<Type> nonMarkovianActionGuards = this->variables.manager->getBddZero();
1102
1103 // Build the results of the synchronization vectors.
1104 std::unordered_map<ActionIdentification, std::vector<ActionDd>, ActionIdentificationHash> actions;
1105 for (uint64_t synchronizationVectorIndex = 0; synchronizationVectorIndex < synchronizationVectors.size(); ++synchronizationVectorIndex) {
1106 auto const& synchVector = synchronizationVectors[synchronizationVectorIndex];
1107
1108 boost::optional<ActionDd> synchronizingAction = combineSynchronizingActions(subautomata, synchVector, synchronizationVectorIndex);
1109 if (synchronizingAction) {
1110 if (applyMaximumProgress) {
1111 STORM_LOG_ASSERT(this->model.getModelType() == storm::jani::ModelType::MA,
1112 "Maximum progress assumption enabled for unexpected model type.");
1113 // By the JANI standard, we can assume that synchronizing actions of MAs are always non-Markovian.
1114 nonMarkovianActionGuards |= synchronizingAction->guard;
1115 }
1116 actions[ActionIdentification(actionInformation.getActionIndex(synchVector.getOutput()),
1117 this->model.getModelType() == storm::jani::ModelType::CTMC)]
1118 .emplace_back(synchronizingAction.get());
1119 }
1120 }
1121
1122 // Construct the two silent action identifications.
1123 ActionIdentification silentActionIdentification(storm::jani::Model::SILENT_ACTION_INDEX);
1124 ActionIdentification silentMarkovianActionIdentification(storm::jani::Model::SILENT_ACTION_INDEX, true);
1125
1126 // Construct the silent action DDs.
1127 std::vector<ActionDd> silentActionDds;
1128 std::vector<ActionDd> silentMarkovianActionDds;
1129 for (auto const& automaton : subautomata) {
1130 for (auto& actionDd : silentActionDds) {
1131 STORM_LOG_TRACE("Extending previous (non-Markovian) silent action by identity of current automaton.");
1132 actionDd = actionDd.multiplyTransitions(automaton.identity);
1133 }
1134 for (auto& actionDd : silentMarkovianActionDds) {
1135 STORM_LOG_TRACE("Extending previous (Markovian) silent action by identity of current automaton.");
1136 actionDd = actionDd.multiplyTransitions(automaton.identity);
1137 }
1138
1139 auto silentActionIt = automaton.actions.find(silentActionIdentification);
1140 if (silentActionIt != automaton.actions.end()) {
1141 STORM_LOG_TRACE("Extending (non-Markovian) silent action by running identity.");
1142 silentActionDds.emplace_back(silentActionIt->second.multiplyTransitions(result.identity));
1143 }
1144
1145 silentActionIt = automaton.actions.find(silentMarkovianActionIdentification);
1146 if (silentActionIt != automaton.actions.end()) {
1147 STORM_LOG_TRACE("Extending (Markovian) silent action by running identity.");
1148 silentMarkovianActionDds.emplace_back(silentActionIt->second.multiplyTransitions(result.identity));
1149 }
1150
1151 result.identity *= automaton.identity;
1152
1153 // Add the transient location assignments of the automata.
1154 addToTransientAssignmentMap(result.transientLocationAssignments, automaton.transientLocationAssignments);
1155 }
1156
1157 if (!silentActionDds.empty()) {
1158 auto& allSilentActionDds = actions[silentActionIdentification];
1159 allSilentActionDds.insert(allSilentActionDds.end(), silentActionDds.begin(), silentActionDds.end());
1160 }
1161
1162 // Add guards of non-markovian actions
1163 if (applyMaximumProgress) {
1164 auto allSilentActionDdsIt = actions.find(silentActionIdentification);
1165 if (allSilentActionDdsIt != actions.end()) {
1166 for (ActionDd const& silentActionDd : allSilentActionDdsIt->second) {
1167 nonMarkovianActionGuards |= silentActionDd.guard;
1168 }
1169 }
1170 }
1171
1172 if (!silentMarkovianActionDds.empty()) {
1173 auto& allMarkovianSilentActionDds = actions[silentMarkovianActionIdentification];
1174 allMarkovianSilentActionDds.insert(allMarkovianSilentActionDds.end(), silentMarkovianActionDds.begin(), silentMarkovianActionDds.end());
1175 if (applyMaximumProgress && !nonMarkovianActionGuards.isZero()) {
1176 auto invertedNonMarkovianGuards = !nonMarkovianActionGuards;
1177 for (ActionDd& markovianActionDd : allMarkovianSilentActionDds) {
1178 markovianActionDd.conjunctGuardWith(invertedNonMarkovianGuards);
1179 }
1180 }
1181 }
1182
1183 // Finally, combine (potentially) multiple action DDs.
1184 for (auto const& actionDds : actions) {
1185 ActionDd combinedAction;
1186 if (actionDds.first == silentMarkovianActionIdentification) {
1187 // For the Markovian transitions, we can simply add the actions.
1188 combinedAction = actionDds.second.front();
1189 for (uint64_t i = 1; i < actionDds.second.size(); ++i) {
1190 combinedAction = combinedAction.add(actionDds.second[i]);
1191 }
1192 } else {
1193 combinedAction = actionDds.second.size() > 1 ? combineUnsynchronizedActions(actionDds.second) : actionDds.second.front();
1194 }
1195 result.actions[actionDds.first] = combinedAction;
1196 result.extendLocalNondeterminismVariables(combinedAction.getLocalNondeterminismVariables());
1197 }
1198
1199 // Construct combined identity.
1200 for (auto const& subautomaton : subautomata) {
1201 result.identity *= subautomaton.identity;
1202 }
1203
1204 return result;
1205 }
1206
1207 boost::optional<ActionDd> combineSynchronizingActions(std::vector<AutomatonDd> const& subautomata,
1208 storm::jani::SynchronizationVector const& synchronizationVector,
1209 uint64_t synchronizationVectorIndex) {
1210 std::vector<std::pair<uint64_t, std::reference_wrapper<ActionDd const>>> actions;
1211 storm::dd::Add<Type, ValueType> nonSynchronizingIdentity = this->variables.manager->template getAddOne<ValueType>();
1212 for (uint64_t subautomatonIndex = 0; subautomatonIndex < subautomata.size(); ++subautomatonIndex) {
1213 auto const& subautomaton = subautomata[subautomatonIndex];
1214 if (synchronizationVector.getInput(subautomatonIndex) != storm::jani::SynchronizationVector::NO_ACTION_INPUT) {
1215 auto it =
1216 subautomaton.actions.find(ActionIdentification(actionInformation.getActionIndex(synchronizationVector.getInput(subautomatonIndex)),
1217 synchronizationVectorIndex, this->model.getModelType() == storm::jani::ModelType::CTMC));
1218 if (it != subautomaton.actions.end()) {
1219 actions.emplace_back(subautomatonIndex, it->second);
1220 } else {
1221 return boost::none;
1222 }
1223 } else {
1224 nonSynchronizingIdentity *= subautomaton.identity;
1225 }
1226 }
1227
1228 // If there are only input-enabled actions, we also need to build the disjunction of the guards.
1229 bool allActionsInputEnabled = true;
1230 for (auto const& action : actions) {
1231 if (!action.second.get().isInputEnabled()) {
1232 allActionsInputEnabled = false;
1233 }
1234 }
1235
1236 boost::optional<storm::dd::Bdd<Type>> guardDisjunction;
1237 if (allActionsInputEnabled) {
1238 guardDisjunction = this->variables.manager->getBddZero();
1239 }
1240
1241 // Otherwise, construct the synchronization.
1242 storm::dd::Bdd<Type> illegalFragment = this->variables.manager->getBddZero();
1243
1244 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment;
1245 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragmentWithoutNondeterminism;
1246
1247 storm::dd::Bdd<Type> inputEnabledGuard = this->variables.manager->getBddOne();
1248 storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddOne<ValueType>();
1249
1250 uint64_t lowestNondeterminismVariable = actions.front().second.get().getLowestLocalNondeterminismVariable();
1251 uint64_t highestNondeterminismVariable = actions.front().second.get().getHighestLocalNondeterminismVariable();
1252
1253 bool hasTransientEdgeAssignments = false;
1254 for (auto const& actionIndexPair : actions) {
1255 auto const& action = actionIndexPair.second.get();
1256 if (!action.transientEdgeAssignments.empty()) {
1257 hasTransientEdgeAssignments = true;
1258 break;
1259 }
1260 }
1261
1262 boost::optional<storm::dd::Add<Type, ValueType>> exitRates;
1263 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
1264 if (this->model.getModelType() == storm::jani::ModelType::CTMC && hasTransientEdgeAssignments) {
1265 // For CTMCs, we need to weigh the transient assignments with the exit rates.
1266 exitRates = this->variables.manager->template getAddOne<ValueType>();
1267 for (auto const& actionIndexPair : actions) {
1268 auto const& action = actionIndexPair.second.get();
1269
1270 std::set<storm::expressions::Variable> columnVariablesToAbstract;
1271 std::set_intersection(action.transitions.getContainedMetaVariables().begin(), action.transitions.getContainedMetaVariables().end(),
1272 this->variables.columnMetaVariables.begin(), this->variables.columnMetaVariables.end(),
1273 std::inserter(columnVariablesToAbstract, columnVariablesToAbstract.begin()));
1274 auto actionExitRates = action.transitions.sumAbstract(columnVariablesToAbstract);
1275 exitRates = exitRates.get() * actionExitRates;
1276
1277 if (!action.transientEdgeAssignments.empty()) {
1278 for (auto const& entry : action.transientEdgeAssignments) {
1279 auto transientEdgeAssignmentIt = transientEdgeAssignments.find(entry.first);
1280 if (transientEdgeAssignmentIt != transientEdgeAssignments.end()) {
1281 transientEdgeAssignmentIt->second *= entry.second / actionExitRates;
1282 } else {
1283 transientEdgeAssignments.emplace(entry.first, entry.second / actionExitRates);
1284 }
1285 }
1286 }
1287 }
1288 } else if (hasTransientEdgeAssignments) {
1289 // Otherwise, just join the assignments.
1290 for (auto const& actionIndexPair : actions) {
1291 auto const& action = actionIndexPair.second.get();
1292 joinTransientAssignmentMapsInPlace(transientEdgeAssignments, action.transientEdgeAssignments);
1293 }
1294 }
1295
1296 storm::dd::Bdd<Type> newIllegalFragment = this->variables.manager->getBddZero();
1297 for (auto const& actionIndexPair : actions) {
1298 auto componentIndex = actionIndexPair.first;
1299 auto const& action = actionIndexPair.second.get();
1300
1301 if (guardDisjunction) {
1302 guardDisjunction.get() |= action.guard;
1303 }
1304
1305 lowestNondeterminismVariable = std::min(lowestNondeterminismVariable, action.getLowestLocalNondeterminismVariable());
1306 highestNondeterminismVariable = std::max(highestNondeterminismVariable, action.getHighestLocalNondeterminismVariable());
1307
1308 if (action.isInputEnabled()) {
1309 // If the action is input-enabled, we add self-loops to all states.
1310 transitions *= action.guard.ite(
1311 action.transitions,
1312 encodeIndex(0, action.getLowestLocalNondeterminismVariable(),
1313 action.getHighestLocalNondeterminismVariable() - action.getLowestLocalNondeterminismVariable(), this->variables) *
1314 subautomata[componentIndex].identity);
1315 } else {
1316 transitions *= action.transitions;
1317 }
1318
1319 // Create a set of variables that is used as nondeterminism variables in this action.
1320 auto nondetVariables =
1321 std::set<storm::expressions::Variable>(this->variables.localNondeterminismVariables.begin() + action.getLowestLocalNondeterminismVariable(),
1322 this->variables.localNondeterminismVariables.begin() + action.getHighestLocalNondeterminismVariable());
1323
1324 for (auto const& entry : action.variableToWritingFragment) {
1325 storm::dd::Bdd<Type> guardedWritingFragment = inputEnabledGuard && entry.second;
1326
1327 // Check whether there already is an entry for this variable in the mapping of global variables
1328 // to their writing fragments.
1329 auto globalFragmentIt = globalVariableToWritingFragment.find(entry.first);
1330 if (globalFragmentIt != globalVariableToWritingFragment.end()) {
1331 // If there is, take the conjunction of the entries and also of their versions without nondeterminism
1332 // variables.
1333 globalFragmentIt->second &= guardedWritingFragment;
1334 illegalFragment |=
1335 globalVariableToWritingFragmentWithoutNondeterminism[entry.first] && guardedWritingFragment.existsAbstract(nondetVariables);
1336 globalVariableToWritingFragmentWithoutNondeterminism[entry.first] |= guardedWritingFragment.existsAbstract(nondetVariables);
1337 } else {
1338 // If not, create the entry and also create a version of the entry that abstracts from the
1339 // used nondeterminism variables.
1340 globalVariableToWritingFragment[entry.first] = guardedWritingFragment;
1341 globalVariableToWritingFragmentWithoutNondeterminism[entry.first] = guardedWritingFragment.existsAbstract(nondetVariables);
1342 }
1343
1344 // Join all individual illegal fragments so we can see whether any of these elements lie in the
1345 // conjunction of all guards.
1346 illegalFragment |= action.illegalFragment;
1347 }
1348
1349 // Now go through all fragments that are not written by the current action and join them with the
1350 // guard of the current action if the current action is not input enabled.
1351 for (auto& entry : globalVariableToWritingFragment) {
1352 if (action.variableToWritingFragment.find(entry.first) == action.variableToWritingFragment.end() && !action.isInputEnabled()) {
1353 entry.second &= action.guard;
1354 }
1355 }
1356
1357 if (!action.isInputEnabled()) {
1358 inputEnabledGuard &= action.guard;
1359 }
1360 }
1361
1362 // If all actions were input-enabled, we need to constrain the transitions with the disjunction of all
1363 // guards to make sure there are not transitions resulting from input enabledness alone.
1364 if (allActionsInputEnabled) {
1365 inputEnabledGuard &= guardDisjunction.get();
1366 transitions *= guardDisjunction.get().template toAdd<ValueType>();
1367 }
1368
1369 // Cut the union of the illegal fragments to the conjunction of the guards since only these states have
1370 // such a combined transition.
1371 illegalFragment &= inputEnabledGuard;
1372
1373 storm::dd::Add<Type, ValueType> transientEdgeAssignmentWeights;
1374 if (hasTransientEdgeAssignments) {
1375 transientEdgeAssignmentWeights = inputEnabledGuard.template toAdd<ValueType>();
1376 if (exitRates) {
1377 transientEdgeAssignmentWeights *= exitRates.get();
1378 }
1379
1380 for (auto& entry : transientEdgeAssignments) {
1381 entry.second *= transientEdgeAssignmentWeights;
1382 }
1383 }
1384
1385 return ActionDd(inputEnabledGuard, transitions * nonSynchronizingIdentity, transientEdgeAssignments,
1386 std::make_pair(lowestNondeterminismVariable, highestNondeterminismVariable), globalVariableToWritingFragment, illegalFragment);
1387 }
1388
1389 ActionDd combineUnsynchronizedActions(ActionDd action1, ActionDd action2, storm::dd::Add<Type, ValueType> const& identity1,
1390 storm::dd::Add<Type, ValueType> const& identity2) {
1391 // First extend the action DDs by the other identities.
1392 STORM_LOG_TRACE("Multiplying identities to combine unsynchronized actions.");
1393 action1.transitions = action1.transitions * identity2;
1394 action2.transitions = action2.transitions * identity1;
1395
1396 // Then combine the extended action DDs.
1397 return combineUnsynchronizedActions(action1, action2);
1398 }
1399
1400 ActionDd combineUnsynchronizedActions(ActionDd action1, ActionDd action2) {
1401 return combineUnsynchronizedActions({action1, action2});
1402 }
1403
1404 ActionDd combineUnsynchronizedActions(std::vector<ActionDd> actions) {
1405 STORM_LOG_TRACE("Combining unsynchronized actions.");
1406
1407 if (this->model.getModelType() == storm::jani::ModelType::DTMC || this->model.getModelType() == storm::jani::ModelType::CTMC) {
1408 auto actionIt = actions.begin();
1409 ActionDd result(*actionIt);
1410
1411 for (++actionIt; actionIt != actions.end(); ++actionIt) {
1412 result = ActionDd(result.guard || actionIt->guard, result.transitions + actionIt->transitions,
1413 joinTransientAssignmentMaps(result.transientEdgeAssignments, actionIt->transientEdgeAssignments),
1414 std::make_pair<uint64_t, uint64_t>(0, 0),
1415 joinVariableWritingFragmentMaps(result.variableToWritingFragment, actionIt->variableToWritingFragment),
1416 result.illegalFragment || actionIt->illegalFragment);
1417 }
1418 return result;
1419 } else if (this->model.getModelType() == storm::jani::ModelType::MDP || this->model.getModelType() == storm::jani::ModelType::LTS ||
1420 this->model.getModelType() == storm::jani::ModelType::MA) {
1421 // Ensure that all actions start at the same local nondeterminism variable.
1422 uint_fast64_t lowestLocalNondeterminismVariable = actions.front().getLowestLocalNondeterminismVariable();
1423 uint_fast64_t highestLocalNondeterminismVariable = actions.front().getHighestLocalNondeterminismVariable();
1424 for (auto const& action : actions) {
1425 STORM_LOG_ASSERT(action.getLowestLocalNondeterminismVariable() == lowestLocalNondeterminismVariable,
1426 "Mismatching lowest nondeterminism variable indices.");
1427 highestLocalNondeterminismVariable = std::max(highestLocalNondeterminismVariable, action.getHighestLocalNondeterminismVariable());
1428 }
1429
1430 // Bring all actions to the same number of variables that encode the nondeterminism.
1431 for (auto& action : actions) {
1432 storm::dd::Bdd<Type> nondeterminismEncodingBdd = this->variables.manager->getBddOne();
1433 for (uint_fast64_t i = action.getHighestLocalNondeterminismVariable(); i < highestLocalNondeterminismVariable; ++i) {
1434 nondeterminismEncodingBdd &= this->variables.manager->getEncoding(this->variables.localNondeterminismVariables[i], 0);
1435 }
1436 storm::dd::Add<Type, ValueType> nondeterminismEncoding = nondeterminismEncodingBdd.template toAdd<ValueType>();
1437
1438 action.transitions *= nondeterminismEncoding;
1439
1440 for (auto& variableFragment : action.variableToWritingFragment) {
1441 variableFragment.second &= nondeterminismEncodingBdd;
1442 }
1443 for (auto& transientAssignment : action.transientEdgeAssignments) {
1444 transientAssignment.second *= nondeterminismEncoding;
1445 }
1446 }
1447
1448 uint64_t numberOfLocalNondeterminismVariables = static_cast<uint64_t>(std::ceil(std::log2(actions.size())));
1449 storm::dd::Bdd<Type> guard = this->variables.manager->getBddZero();
1450 storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddZero<ValueType>();
1451 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
1452 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> variableToWritingFragment;
1453 storm::dd::Bdd<Type> illegalFragment = this->variables.manager->getBddZero();
1454
1455 for (uint64_t actionIndex = 0; actionIndex < actions.size(); ++actionIndex) {
1456 ActionDd& action = actions[actionIndex];
1457
1458 guard |= action.guard;
1459
1460 storm::dd::Add<Type, ValueType> nondeterminismEncoding =
1461 encodeIndex(actionIndex, highestLocalNondeterminismVariable, numberOfLocalNondeterminismVariables, this->variables);
1462 transitions += nondeterminismEncoding * action.transitions;
1463
1464 joinTransientAssignmentMapsInPlace(transientEdgeAssignments, action.transientEdgeAssignments, nondeterminismEncoding);
1465
1466 storm::dd::Bdd<Type> nondeterminismEncodingBdd = nondeterminismEncoding.toBdd();
1467 for (auto& entry : action.variableToWritingFragment) {
1468 entry.second &= nondeterminismEncodingBdd;
1469 }
1470 addToVariableWritingFragmentMap(variableToWritingFragment, action.variableToWritingFragment);
1471 illegalFragment |= action.illegalFragment;
1472 }
1473
1474 return ActionDd(guard, transitions, transientEdgeAssignments,
1475 std::make_pair(lowestLocalNondeterminismVariable, highestLocalNondeterminismVariable + numberOfLocalNondeterminismVariables),
1476 variableToWritingFragment, illegalFragment);
1477 } else {
1478 STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Illegal model type.");
1479 }
1480 }
1481
1482 void performTransientAssignments(storm::jani::detail::ConstAssignments const& transientAssignments,
1483 std::function<void(storm::jani::Assignment const&)> const& callback) {
1484 auto transientVariableIt = this->transientVariables.begin();
1485 auto transientVariableIte = this->transientVariables.end();
1486 for (auto const& assignment : transientAssignments) {
1487 while (transientVariableIt != transientVariableIte && *transientVariableIt < assignment.getExpressionVariable()) {
1488 ++transientVariableIt;
1489 }
1490 if (transientVariableIt == transientVariableIte) {
1491 break;
1492 }
1493 if (*transientVariableIt == assignment.getExpressionVariable()) {
1494 callback(assignment);
1495 ++transientVariableIt;
1496 }
1497 }
1498 }
1499
1500 EdgeDd buildEdgeDd(storm::jani::Automaton const& automaton, storm::jani::Edge const& edge) {
1501 STORM_LOG_TRACE("Translating guard " << edge.getGuard());
1502
1503 // We keep the guard and a "ranged" version seperate, because building the destinations tends to be
1504 // slower when the full range is applied.
1505 storm::dd::Bdd<Type> guard = this->variables.rowExpressionAdapter->translateBooleanExpression(edge.getGuard());
1506 storm::dd::Bdd<Type> rangedGuard = guard && this->variables.automatonToRangeMap.at(automaton.getName()).toBdd();
1507 STORM_LOG_WARN_COND(!rangedGuard.isZero(), "The guard '" << edge.getGuard() << "' is unsatisfiable.");
1508
1509 if (!rangedGuard.isZero()) {
1510 // Create the DDs representing the individual updates.
1511 std::vector<EdgeDestinationDd<Type, ValueType>> destinationDds;
1512 for (storm::jani::EdgeDestination const& destination : edge.getDestinations()) {
1513 destinationDds.push_back(buildEdgeDestinationDd(automaton, destination, guard, this->variables));
1514
1515 STORM_LOG_WARN_COND(!destinationDds.back().transitions.isZero(), "Destination does not have any effect.");
1516 }
1517
1518 // Now that we have built the destinations, we always take the full guard.
1519 storm::dd::Bdd<Type> sourceLocationBdd = this->variables.manager->getEncoding(
1520 this->variables.automatonToLocationDdVariableMap.at(automaton.getName()).first, edge.getSourceLocationIndex());
1521 guard = sourceLocationBdd && rangedGuard;
1522
1523 // Start by gathering all variables that were written in at least one destination.
1524 std::set<storm::expressions::Variable> globalVariablesInSomeDestination;
1525
1526 // If the edge is not labeled with the silent action, we have to analyze which portion of the global
1527 // variables was written by any of the updates and make all update results equal w.r.t. this set. If
1528 // the edge is labeled with the silent action, we can already multiply the identities of all global variables.
1530 for (auto const& edgeDestinationDd : destinationDds) {
1531 globalVariablesInSomeDestination.insert(edgeDestinationDd.writtenGlobalVariables.begin(), edgeDestinationDd.writtenGlobalVariables.end());
1532 }
1533 } else {
1534 globalVariablesInSomeDestination = this->variables.allGlobalVariables;
1535 }
1536
1537 // Then, multiply the missing identities.
1538 for (auto& destinationDd : destinationDds) {
1539 std::set<storm::expressions::Variable> missingIdentities;
1540 std::set_difference(globalVariablesInSomeDestination.begin(), globalVariablesInSomeDestination.end(),
1541 destinationDd.writtenGlobalVariables.begin(), destinationDd.writtenGlobalVariables.end(),
1542 std::inserter(missingIdentities, missingIdentities.begin()));
1543
1544 for (auto const& variable : missingIdentities) {
1545 STORM_LOG_TRACE("Multiplying identity for variable " << variable.getName() << " to destination DD.");
1546 destinationDd.transitions *= this->variables.variableToIdentityMap.at(variable);
1547 }
1548 }
1549
1550 // Now combine the destination DDs to the edge DD.
1551 storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddZero<ValueType>();
1552 for (auto const& destinationDd : destinationDds) {
1553 transitions += destinationDd.transitions;
1554 }
1555
1556 // Add the source location and the guard.
1557 storm::dd::Add<Type, ValueType> guardAdd = guard.template toAdd<ValueType>();
1558 transitions *= guardAdd;
1559
1560 // If we multiply the ranges of global variables, make sure everything stays within its bounds.
1561 if (!globalVariablesInSomeDestination.empty()) {
1562 transitions *= this->variables.globalVariableRanges;
1563 }
1564
1565 // If the edge has a rate, we multiply it to the DD.
1566 bool isMarkovian = false;
1567 boost::optional<storm::dd::Add<Type, ValueType>> exitRates;
1568 if (edge.hasRate()) {
1569 exitRates = this->variables.rowExpressionAdapter->translateExpression(edge.getRate());
1570 transitions *= exitRates.get();
1571 isMarkovian = true;
1572 }
1573
1574 // Finally treat the transient assignments.
1575 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
1576 if (!this->transientVariables.empty()) {
1577 performTransientAssignments(edge.getAssignments().getTransientAssignments(), [this, &transientEdgeAssignments, &guardAdd,
1578 &exitRates](storm::jani::Assignment const& assignment) {
1579 auto newTransientEdgeAssignments = guardAdd * this->variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression());
1580 if (exitRates) {
1581 newTransientEdgeAssignments *= exitRates.get();
1582 }
1583 transientEdgeAssignments[assignment.getExpressionVariable()] = newTransientEdgeAssignments;
1584 });
1585 }
1586
1587 return EdgeDd(isMarkovian, guard, transitions, transientEdgeAssignments, globalVariablesInSomeDestination);
1588 } else {
1589 return EdgeDd(edge.hasRate(), rangedGuard, rangedGuard.template toAdd<ValueType>(),
1590 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>(), std::set<storm::expressions::Variable>());
1591 }
1592 }
1593
1594 EdgeDd combineMarkovianEdgesToSingleEdge(std::vector<EdgeDd> const& edgeDds) {
1595 storm::dd::Bdd<Type> guard = this->variables.manager->getBddZero();
1596 storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddZero<ValueType>();
1597 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
1598 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> variableToWritingFragment;
1599
1600 bool overlappingGuards = false;
1601 for (auto const& edge : edgeDds) {
1602 STORM_LOG_THROW(edge.isMarkovian, storm::exceptions::WrongFormatException, "Can only combine Markovian edges.");
1603
1604 if (!overlappingGuards) {
1605 overlappingGuards |= !(guard && edge.guard).isZero();
1606 }
1607
1608 guard |= edge.guard;
1609 transitions += edge.transitions;
1610 variableToWritingFragment = joinVariableWritingFragmentMaps(variableToWritingFragment, edge.variableToWritingFragment);
1611 joinTransientAssignmentMapsInPlace(transientEdgeAssignments, edge.transientEdgeAssignments);
1612 }
1613
1614 // Currently, we can only combine the transient edge assignments if there is no overlap of the guards of the edges.
1615 STORM_LOG_THROW(!overlappingGuards || transientEdgeAssignments.empty(), storm::exceptions::NotSupportedException,
1616 "Cannot have transient edge assignments when combining Markovian edges with overlapping guards.");
1617
1618 return EdgeDd(true, guard, transitions, transientEdgeAssignments, variableToWritingFragment);
1619 }
1620
1621 ActionDd buildActionDdForActionInstantiation(storm::jani::Automaton const& automaton, ActionInstantiation const& instantiation) {
1622 // Translate the individual edges.
1623 std::vector<EdgeDd> edgeDds;
1624 for (auto const& edge : automaton.getEdges()) {
1625 if (edge.getActionIndex() == instantiation.actionIndex && edge.hasRate() == instantiation.isMarkovian()) {
1626 EdgeDd result = buildEdgeDd(automaton, edge);
1627 edgeDds.emplace_back(result);
1628 }
1629 }
1630
1631 // Now combine the edges to a single action.
1632 uint64_t localNondeterminismVariableOffset = instantiation.localNondeterminismVariableOffset;
1633 if (!edgeDds.empty()) {
1634 storm::jani::ModelType modelType = this->model.getModelType();
1635 if (modelType == storm::jani::ModelType::DTMC) {
1636 return combineEdgesToActionDeterministic(edgeDds);
1637 } else if (modelType == storm::jani::ModelType::CTMC) {
1638 return combineEdgesToActionDeterministic(edgeDds);
1639 } else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS) {
1640 return combineEdgesToActionNondeterministic(edgeDds, localNondeterminismVariableOffset);
1641 } else if (modelType == storm::jani::ModelType::MA) {
1642 if (instantiation.isMarkovian()) {
1643 return combineEdgesToActionDeterministic(edgeDds);
1644 } else {
1645 return combineEdgesToActionNondeterministic(edgeDds, localNondeterminismVariableOffset);
1646 }
1647 } else {
1648 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Cannot translate model of type " << modelType << ".");
1649 }
1650 } else {
1651 return ActionDd(this->variables.manager->getBddZero(), this->variables.manager->template getAddZero<ValueType>(), {},
1652 std::make_pair<uint64_t, uint64_t>(0, 0), {}, this->variables.manager->getBddZero());
1653 }
1654 }
1655
1656 void addToTransientAssignmentMap(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>& transientAssignments,
1657 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& assignmentsToAdd) {
1658 for (auto const& entry : assignmentsToAdd) {
1659 auto it = transientAssignments.find(entry.first);
1660 if (it != transientAssignments.end()) {
1661 it->second += entry.second;
1662 } else {
1663 transientAssignments[entry.first] = entry.second;
1664 }
1665 }
1666 }
1667
1668 void addToTransientAssignmentMap(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>& transientAssignments,
1669 storm::expressions::Variable const& variable, storm::dd::Add<Type, ValueType> const& assignmentToAdd) {
1670 auto it = transientAssignments.find(variable);
1671 if (it != transientAssignments.end()) {
1672 it->second += assignmentToAdd;
1673 } else {
1674 transientAssignments[variable] = assignmentToAdd;
1675 }
1676 }
1677
1678 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> joinTransientAssignmentMaps(
1679 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientAssignments1,
1680 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& transientAssignments2) {
1681 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> result = transientAssignments1;
1682
1683 for (auto const& entry : transientAssignments2) {
1684 auto resultIt = result.find(entry.first);
1685 if (resultIt != result.end()) {
1686 resultIt->second += entry.second;
1687 } else {
1688 result.emplace(entry);
1689 }
1690 }
1691
1692 return result;
1693 }
1694
1695 void joinTransientAssignmentMapsInPlace(std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>>& target,
1696 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> const& newTransientAssignments,
1697 boost::optional<storm::dd::Add<Type, ValueType>> const& factor = boost::none) {
1698 for (auto const& entry : newTransientAssignments) {
1699 auto targetIt = target.find(entry.first);
1700 if (targetIt != target.end()) {
1701 targetIt->second += factor ? factor.get() * entry.second : entry.second;
1702 } else {
1703 target[entry.first] = factor ? factor.get() * entry.second : entry.second;
1704 }
1705 }
1706 }
1707
1708 ActionDd combineEdgesToActionDeterministic(std::vector<EdgeDd> const& edgeDds) {
1709 storm::dd::Bdd<Type> allGuards = this->variables.manager->getBddZero();
1710 storm::dd::Add<Type, ValueType> allTransitions = this->variables.manager->template getAddZero<ValueType>();
1711 storm::dd::Bdd<Type> temporary;
1712
1713 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment;
1714 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
1715 bool overlappingGuards = false;
1716 for (auto const& edgeDd : edgeDds) {
1718 (this->model.getModelType() == storm::jani::ModelType::CTMC || this->model.getModelType() == storm::jani::ModelType::MA) == edgeDd.isMarkovian,
1719 storm::exceptions::WrongFormatException, "Unexpected edge type.");
1720
1721 // Check for overlapping guards.
1722 overlappingGuards = !(edgeDd.guard && allGuards).isZero();
1723
1724 // Issue a warning if there are overlapping guards in a DTMC.
1726 !overlappingGuards || this->model.getModelType() == storm::jani::ModelType::CTMC || this->model.getModelType() == storm::jani::ModelType::MA,
1727 "Guard of an edge in a DTMC overlaps with previous guards.");
1728
1729 // Add the elements of the current edge to the global ones.
1730 allGuards |= edgeDd.guard;
1731 allTransitions += edgeDd.transitions;
1732
1733 // Add the transient variable assignments to the resulting one. This transformation is illegal for
1734 // CTMCs for which there is some overlap in edges that have some transient assignment (this needs to
1735 // be checked later).
1736 addToTransientAssignmentMap(transientEdgeAssignments, edgeDd.transientEdgeAssignments);
1737
1738 // Keep track of the fragment that is writing global variables.
1739 globalVariableToWritingFragment = joinVariableWritingFragmentMaps(globalVariableToWritingFragment, edgeDd.variableToWritingFragment);
1740 }
1741
1742 STORM_LOG_THROW(this->model.getModelType() == storm::jani::ModelType::DTMC || !overlappingGuards || transientEdgeAssignments.empty(),
1743 storm::exceptions::NotSupportedException,
1744 "Cannot have transient edge assignments when combining Markovian edges with overlapping guards.");
1745
1746 return ActionDd(allGuards, allTransitions, transientEdgeAssignments, std::make_pair<uint64_t, uint64_t>(0, 0), globalVariableToWritingFragment,
1747 this->variables.manager->getBddZero());
1748 }
1749
1750 void addToVariableWritingFragmentMap(std::map<storm::expressions::Variable, storm::dd::Bdd<Type>>& globalVariableToWritingFragment,
1751 storm::expressions::Variable const& variable, storm::dd::Bdd<Type> const& partToAdd) const {
1752 auto it = globalVariableToWritingFragment.find(variable);
1753 if (it != globalVariableToWritingFragment.end()) {
1754 it->second |= partToAdd;
1755 } else {
1756 globalVariableToWritingFragment.emplace(variable, partToAdd);
1757 }
1758 }
1759
1760 void addToVariableWritingFragmentMap(std::map<storm::expressions::Variable, storm::dd::Bdd<Type>>& globalVariableToWritingFragment,
1761 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& partToAdd) const {
1762 for (auto const& entry : partToAdd) {
1763 addToVariableWritingFragmentMap(globalVariableToWritingFragment, entry.first, entry.second);
1764 }
1765 }
1766
1767 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> joinVariableWritingFragmentMaps(
1768 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& globalVariableToWritingFragment1,
1769 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> const& globalVariableToWritingFragment2) {
1770 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> result = globalVariableToWritingFragment1;
1771
1772 for (auto const& entry : globalVariableToWritingFragment2) {
1773 auto resultIt = result.find(entry.first);
1774 if (resultIt != result.end()) {
1775 resultIt->second |= entry.second;
1776 } else {
1777 result[entry.first] = entry.second;
1778 }
1779 }
1780
1781 return result;
1782 }
1783
1784 ActionDd combineEdgesBySummation(storm::dd::Bdd<Type> const& guard, std::vector<EdgeDd> const& edges) {
1785 storm::dd::Add<Type, ValueType> transitions = this->variables.manager->template getAddZero<ValueType>();
1786 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment;
1787 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
1788
1789 for (auto const& edge : edges) {
1790 transitions += edge.transitions;
1791 for (auto const& assignment : edge.transientEdgeAssignments) {
1792 addToTransientAssignmentMap(transientEdgeAssignments, assignment.first, assignment.second);
1793 }
1794 for (auto const& variableFragment : edge.variableToWritingFragment) {
1795 addToVariableWritingFragmentMap(globalVariableToWritingFragment, variableFragment.first, variableFragment.second);
1796 }
1797 }
1798
1799 return ActionDd(guard, transitions, transientEdgeAssignments, std::make_pair<uint64_t, uint64_t>(0, 0), globalVariableToWritingFragment,
1800 this->variables.manager->getBddZero());
1801 }
1802
1803 ActionDd combineEdgesToActionNondeterministic(std::vector<EdgeDd> const& edges, uint64_t localNondeterminismVariableOffset) {
1804 // Sum all guards, so we can read off the maximal number of nondeterministic choices in any given state.
1805 storm::dd::Bdd<Type> allGuards = this->variables.manager->getBddZero();
1806 storm::dd::Add<Type, uint_fast64_t> sumOfGuards = this->variables.manager->template getAddZero<uint_fast64_t>();
1807 for (auto const& edge : edges) {
1808 STORM_LOG_ASSERT(!edge.isMarkovian, "Unexpected Markovian edge.");
1809 sumOfGuards += edge.guard.template toAdd<uint_fast64_t>();
1810 allGuards |= edge.guard;
1811 }
1812 uint_fast64_t maxChoices = sumOfGuards.getMax();
1813 STORM_LOG_TRACE("Found " << maxChoices << " non-Markovian local choices.");
1814
1815 // Depending on the maximal number of nondeterminstic choices, we need to use some variables to encode the nondeterminism.
1816 if (maxChoices <= 1) {
1817 return combineEdgesBySummation(allGuards, edges);
1818 } else {
1819 // Calculate number of required variables to encode the nondeterminism.
1820 uint_fast64_t numberOfBinaryVariables = static_cast<uint_fast64_t>(std::ceil(std::log2(maxChoices)));
1821
1822 storm::dd::Add<Type, ValueType> allEdges = this->variables.manager->template getAddZero<ValueType>();
1823 std::map<storm::expressions::Variable, storm::dd::Bdd<Type>> globalVariableToWritingFragment;
1824 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientAssignments;
1825
1826 storm::dd::Bdd<Type> equalsNumberOfChoicesDd;
1827 std::vector<storm::dd::Add<Type, ValueType>> choiceDds(maxChoices, this->variables.manager->template getAddZero<ValueType>());
1828 std::vector<storm::dd::Bdd<Type>> remainingDds(maxChoices, this->variables.manager->getBddZero());
1829 std::vector<std::pair<storm::dd::Bdd<Type>, storm::dd::Add<Type, ValueType>>> indicesEncodedWithLocalNondeterminismVariables;
1830 for (uint64_t j = 0; j < maxChoices; ++j) {
1831 storm::dd::Add<Type, ValueType> indexEncoding = encodeIndex(j, localNondeterminismVariableOffset, numberOfBinaryVariables, this->variables);
1832 indicesEncodedWithLocalNondeterminismVariables.push_back(std::make_pair(indexEncoding.toBdd(), indexEncoding));
1833 }
1834
1835 for (uint_fast64_t currentChoices = 1; currentChoices <= maxChoices; ++currentChoices) {
1836 // Determine the set of states with exactly currentChoices choices.
1837 equalsNumberOfChoicesDd = sumOfGuards.equals(this->variables.manager->getConstant(currentChoices));
1838
1839 // If there is no such state, continue with the next possible number of choices.
1840 if (equalsNumberOfChoicesDd.isZero()) {
1841 continue;
1842 }
1843
1844 // Reset the previously used intermediate storage.
1845 for (uint_fast64_t j = 0; j < currentChoices; ++j) {
1846 choiceDds[j] = this->variables.manager->template getAddZero<ValueType>();
1847 remainingDds[j] = equalsNumberOfChoicesDd;
1848 }
1849
1850 for (std::size_t j = 0; j < edges.size(); ++j) {
1851 EdgeDd const& currentEdge = edges[j];
1852
1853 // Check if edge guard overlaps with equalsNumberOfChoicesDd. That is, there are states with exactly currentChoices
1854 // choices such that one outgoing choice is given by the j-th edge.
1855 storm::dd::Bdd<Type> guardChoicesIntersection = currentEdge.guard && equalsNumberOfChoicesDd;
1856
1857 // If there is no such state, continue with the next command.
1858 if (guardChoicesIntersection.isZero()) {
1859 continue;
1860 }
1861
1862 // Split the currentChoices nondeterministic choices.
1863 for (uint_fast64_t k = 0; k < currentChoices; ++k) {
1864 // Calculate the overlapping part of command guard and the remaining DD.
1865 storm::dd::Bdd<Type> remainingGuardChoicesIntersection = guardChoicesIntersection && remainingDds[k];
1866
1867 // Check if we can add some overlapping parts to the current index.
1868 if (!remainingGuardChoicesIntersection.isZero()) {
1869 // Remove overlapping parts from the remaining DD.
1870 remainingDds[k] = remainingDds[k] && !remainingGuardChoicesIntersection;
1871
1872 // Combine the overlapping part of the guard with command updates and add it to the resulting DD.
1873 choiceDds[k] += remainingGuardChoicesIntersection.template toAdd<ValueType>() * currentEdge.transitions;
1874
1875 // Keep track of the fragment of transient assignments.
1876 for (auto const& transientAssignment : currentEdge.transientEdgeAssignments) {
1877 addToTransientAssignmentMap(transientAssignments, transientAssignment.first,
1878 remainingGuardChoicesIntersection.template toAdd<ValueType>() * transientAssignment.second *
1879 indicesEncodedWithLocalNondeterminismVariables[k].second);
1880 }
1881
1882 // Keep track of the written global variables of the fragment.
1883 for (auto const& variableFragment : currentEdge.variableToWritingFragment) {
1884 addToVariableWritingFragmentMap(
1885 globalVariableToWritingFragment, variableFragment.first,
1886 remainingGuardChoicesIntersection && variableFragment.second && indicesEncodedWithLocalNondeterminismVariables[k].first);
1887 }
1888 }
1889
1890 // Remove overlapping parts from the command guard DD
1891 guardChoicesIntersection = guardChoicesIntersection && !remainingGuardChoicesIntersection;
1892
1893 // If the guard DD has become equivalent to false, we can stop here.
1894 if (guardChoicesIntersection.isZero()) {
1895 break;
1896 }
1897 }
1898 }
1899
1900 // Add the meta variables that encode the nondeterminisim to the different choices.
1901 for (uint_fast64_t j = 0; j < currentChoices; ++j) {
1902 allEdges += indicesEncodedWithLocalNondeterminismVariables[j].second * choiceDds[j];
1903 }
1904
1905 // Delete currentChoices out of overlapping DD
1906 sumOfGuards = sumOfGuards * (!equalsNumberOfChoicesDd).template toAdd<uint_fast64_t>();
1907 }
1908
1909 return ActionDd(allGuards, allEdges, transientAssignments,
1910 std::make_pair(localNondeterminismVariableOffset, localNondeterminismVariableOffset + numberOfBinaryVariables),
1911 globalVariableToWritingFragment, this->variables.manager->getBddZero());
1912 }
1913 }
1914
1915 AutomatonDd buildAutomatonDd(std::string const& automatonName, ActionInstantiations const& actionInstantiations,
1916 std::set<uint64_t> const& inputEnabledActionIndices, bool isTopLevelAutomaton) {
1917 STORM_LOG_TRACE("Building DD for automaton '" << automatonName << "'.");
1918 AutomatonDd result(this->variables.automatonToIdentityMap.at(automatonName));
1919
1920 // Disjunction of all guards of non-markovian actions (only required for maximum progress assumption).
1921 storm::dd::Bdd<Type> nonMarkovianActionGuards = this->variables.manager->getBddZero();
1922
1923 storm::jani::Automaton const& automaton = this->model.getAutomaton(automatonName);
1924 for (auto const& actionInstantiation : actionInstantiations) {
1925 uint64_t actionIndex = actionInstantiation.first;
1926 if (!automaton.hasEdgeLabeledWithActionIndex(actionIndex)) {
1927 continue;
1928 }
1929 bool inputEnabled = false;
1930 if (inputEnabledActionIndices.find(actionIndex) != inputEnabledActionIndices.end()) {
1931 inputEnabled = true;
1932 }
1933 for (auto const& instantiation : actionInstantiation.second) {
1934 STORM_LOG_TRACE("Building " << (instantiation.isMarkovian() ? "(Markovian) " : "")
1935 << (actionInformation.getActionName(actionIndex).empty() ? "silent " : "") << "action "
1936 << (actionInformation.getActionName(actionIndex).empty() ? "" : actionInformation.getActionName(actionIndex) + " ")
1937 << "from offset " << instantiation.localNondeterminismVariableOffset << ".");
1938 ActionDd actionDd = buildActionDdForActionInstantiation(automaton, instantiation);
1939 if (inputEnabled) {
1940 actionDd.setIsInputEnabled();
1941 }
1942 if (applyMaximumProgress && isTopLevelAutomaton && !instantiation.isMarkovian()) {
1943 nonMarkovianActionGuards |= actionDd.guard;
1944 }
1945 STORM_LOG_TRACE("Used local nondeterminism variables are " << actionDd.getLowestLocalNondeterminismVariable() << " to "
1946 << actionDd.getHighestLocalNondeterminismVariable() << ".");
1947 result.actions[ActionIdentification(actionIndex, instantiation.synchronizationVectorIndex, instantiation.isMarkovian())] = actionDd;
1948 result.extendLocalNondeterminismVariables(actionDd.getLocalNondeterminismVariables());
1949 }
1950 }
1951
1952 if (applyMaximumProgress && isTopLevelAutomaton) {
1953 ActionIdentification silentMarkovianActionIdentification(storm::jani::Model::SILENT_ACTION_INDEX, true);
1954 result.actions[silentMarkovianActionIdentification].conjunctGuardWith(!nonMarkovianActionGuards);
1955 }
1956
1957 for (uint64_t locationIndex = 0; locationIndex < automaton.getNumberOfLocations(); ++locationIndex) {
1958 auto const& location = automaton.getLocation(locationIndex);
1959 performTransientAssignments(
1960 location.getAssignments().getTransientAssignments(), [this, &automatonName, locationIndex, &result](storm::jani::Assignment const& assignment) {
1961 storm::dd::Add<Type, ValueType> assignedValues =
1962 this->variables.manager->getEncoding(this->variables.automatonToLocationDdVariableMap.at(automatonName).first, locationIndex)
1963 .template toAdd<ValueType>() *
1964 this->variables.rowExpressionAdapter->translateExpression(assignment.getAssignedExpression());
1965
1966 auto it = result.transientLocationAssignments.find(assignment.getExpressionVariable());
1967 if (it != result.transientLocationAssignments.end()) {
1968 it->second += assignedValues;
1969 } else {
1970 result.transientLocationAssignments[assignment.getExpressionVariable()] = assignedValues;
1971 }
1972 });
1973 }
1974
1975 return result;
1976 }
1977
1978 void addMissingGlobalVariableIdentities(ActionDd& action) {
1979 // Build a DD that we can multiply to the transitions and adds all missing global variable identities that way.
1980 storm::dd::Add<Type, ValueType> missingIdentities = this->variables.manager->template getAddOne<ValueType>();
1981
1982 for (auto const& variable : this->variables.allGlobalVariables) {
1983 auto it = action.variableToWritingFragment.find(variable);
1984 if (it != action.variableToWritingFragment.end()) {
1985 missingIdentities *=
1986 (it->second).ite(this->variables.manager->template getAddOne<ValueType>(), this->variables.variableToIdentityMap.at(variable));
1987 } else {
1988 missingIdentities *= this->variables.variableToIdentityMap.at(variable);
1989 }
1990 }
1991
1992 action.transitions *= missingIdentities;
1993 }
1994
1995 ComposerResult<Type, ValueType> buildSystemFromAutomaton(AutomatonDd& automaton) {
1996 STORM_LOG_TRACE("Building system from final automaton.");
1997
1998 auto modelType = this->model.getModelType();
1999
2000 // If the model is an MDP, we need to encode the nondeterminism using additional variables.
2001 if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::MA || modelType == storm::jani::ModelType::LTS) {
2002 storm::dd::Add<Type, ValueType> result = this->variables.manager->template getAddZero<ValueType>();
2003 storm::dd::Bdd<Type> illegalFragment = this->variables.manager->getBddZero();
2004
2005 // First, determine the highest number of nondeterminism variables that is used in any action and make
2006 // all actions use the same amout of nondeterminism variables.
2007 uint64_t numberOfUsedNondeterminismVariables = automaton.getHighestLocalNondeterminismVariable();
2008 STORM_LOG_TRACE("Building system from composed automaton; number of used nondeterminism variables is " << numberOfUsedNondeterminismVariables
2009 << ".");
2010
2011 // Add missing global variable identities, action and nondeterminism encodings.
2012 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
2013 std::unordered_set<ActionIdentification, ActionIdentificationHash> containedActions;
2014 for (auto& action : automaton.actions) {
2015 STORM_LOG_TRACE("Treating action with index " << action.first.actionIndex << (action.first.isMarkovian() ? " (Markovian)" : "") << ".");
2016
2017 uint64_t actionIndex = action.first.actionIndex;
2018 bool markovian = action.first.isMarkovian();
2019 ActionIdentification identificationWithoutSynchVector(actionIndex, markovian);
2020
2021 STORM_LOG_THROW(containedActions.find(identificationWithoutSynchVector) == containedActions.end(), storm::exceptions::WrongFormatException,
2022 "Duplicate action " << actionInformation.getActionName(actionIndex));
2023 containedActions.insert(identificationWithoutSynchVector);
2024 illegalFragment |= action.second.illegalFragment;
2025 addMissingGlobalVariableIdentities(action.second);
2026 storm::dd::Add<Type, ValueType> actionEncoding =
2027 encodeAction(actionIndex != storm::jani::Model::SILENT_ACTION_INDEX ? boost::make_optional(actionIndex) : boost::none,
2028 this->model.getModelType() == storm::jani::ModelType::MA ? boost::make_optional(markovian) : boost::none, this->variables);
2029
2030 storm::dd::Add<Type, ValueType> missingNondeterminismEncoding =
2031 encodeIndex(0, action.second.getHighestLocalNondeterminismVariable(),
2032 numberOfUsedNondeterminismVariables - action.second.getHighestLocalNondeterminismVariable(), this->variables);
2033 storm::dd::Add<Type, ValueType> extendedTransitions = actionEncoding * missingNondeterminismEncoding * action.second.transitions;
2034 for (auto const& transientAssignment : action.second.transientEdgeAssignments) {
2035 addToTransientAssignmentMap(transientEdgeAssignments, transientAssignment.first,
2036 actionEncoding * missingNondeterminismEncoding * transientAssignment.second);
2037 }
2038
2039 result += extendedTransitions;
2040 }
2041
2042 return ComposerResult<Type, ValueType>(result, automaton.transientLocationAssignments, transientEdgeAssignments, illegalFragment,
2043 numberOfUsedNondeterminismVariables);
2044 } else if (modelType == storm::jani::ModelType::DTMC || modelType == storm::jani::ModelType::CTMC) {
2045 // Simply add all actions, but make sure to include the missing global variable identities.
2046
2047 storm::dd::Add<Type, ValueType> result = this->variables.manager->template getAddZero<ValueType>();
2048 storm::dd::Bdd<Type> illegalFragment = this->variables.manager->getBddZero();
2049 std::map<storm::expressions::Variable, storm::dd::Add<Type, ValueType>> transientEdgeAssignments;
2050 std::unordered_set<uint64_t> actionIndices;
2051 for (auto& action : automaton.actions) {
2052 STORM_LOG_THROW(actionIndices.find(action.first.actionIndex) == actionIndices.end(), storm::exceptions::WrongFormatException,
2053 "Duplication action " << actionInformation.getActionName(action.first.actionIndex));
2054 actionIndices.insert(action.first.actionIndex);
2055 illegalFragment |= action.second.illegalFragment;
2056 addMissingGlobalVariableIdentities(action.second);
2057 addToTransientAssignmentMap(transientEdgeAssignments, action.second.transientEdgeAssignments);
2058 result += action.second.transitions;
2059 }
2060
2061 return ComposerResult<Type, ValueType>(result, automaton.transientLocationAssignments, transientEdgeAssignments, illegalFragment, 0);
2062 } else {
2063 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Model type '" << this->model.getModelType() << "' not supported.");
2064 }
2065 }
2066};
2067
2068template<storm::dd::DdType Type, typename ValueType>
2074 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> rewardModels;
2075 std::map<std::string, storm::expressions::Expression> labelToExpressionMap;
2076};
2077
2078template<storm::dd::DdType Type, typename ValueType>
2079std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> createModel(storm::jani::ModelType const& modelType,
2081 ModelComponents<Type, ValueType> const& modelComponents) {
2082 std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> result;
2083 if (modelType == storm::jani::ModelType::DTMC) {
2084 result = std::make_shared<storm::models::symbolic::Dtmc<Type, ValueType>>(
2085 variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates, modelComponents.transitionMatrix,
2087 modelComponents.labelToExpressionMap, modelComponents.rewardModels);
2088 } else if (modelType == storm::jani::ModelType::CTMC) {
2089 result = std::make_shared<storm::models::symbolic::Ctmc<Type, ValueType>>(
2090 variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates, modelComponents.transitionMatrix,
2092 modelComponents.labelToExpressionMap, modelComponents.rewardModels);
2093 } else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS) {
2094 result = std::make_shared<storm::models::symbolic::Mdp<Type, ValueType>>(
2095 variables.manager, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates, modelComponents.transitionMatrix,
2097 variables.allNondeterminismVariables, modelComponents.labelToExpressionMap, modelComponents.rewardModels);
2098 } else if (modelType == storm::jani::ModelType::MA) {
2099 result = std::make_shared<storm::models::symbolic::MarkovAutomaton<Type, ValueType>>(
2100 variables.manager, !variables.probabilisticMarker, modelComponents.reachableStates, modelComponents.initialStates, modelComponents.deadlockStates,
2101 modelComponents.transitionMatrix, variables.rowMetaVariables, variables.rowExpressionAdapter, variables.columnMetaVariables,
2102 variables.rowColumnMetaVariablePairs, variables.allNondeterminismVariables, modelComponents.labelToExpressionMap, modelComponents.rewardModels);
2103 } else {
2104 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Model type '" << modelType << "' not supported.");
2105 }
2106
2107 if (std::is_same<ValueType, storm::RationalFunction>::value) {
2108 result->addParameters(variables.parameters);
2109 }
2110
2111 return result;
2112}
2113
2114template<storm::dd::DdType Type, typename ValueType>
2116 // Add all action/row/column variables to the DD. If we omitted multiplying edges in the construction, this will
2117 // introduce the variables so they can later be abstracted without raising an error.
2120
2121 // If the model is an MDP, we also add all action variables.
2122 if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS) {
2123 for (auto const& actionVariablePair : variables.actionVariablesMap) {
2124 system.transitions.addMetaVariable(actionVariablePair.second);
2125 }
2126 }
2127
2128 // Get rid of the local nondeterminism variables that were not used.
2129 for (uint64_t index = system.numberOfNondeterminismVariables; index < variables.localNondeterminismVariables.size(); ++index) {
2130 variables.allNondeterminismVariables.erase(variables.localNondeterminismVariables[index]);
2131 }
2133}
2134
2135template<storm::dd::DdType Type, typename ValueType>
2138 typename DdJaniModelBuilder<Type, ValueType>::Options const& options,
2139 std::map<std::string, storm::expressions::Expression> const& labelsToExpressionMap) {
2140 // For DTMCs, we normalize each row to 1 (to account for non-determinism).
2142 storm::dd::Add<Type, ValueType> stateToNumberOfChoices = system.transitions.sumAbstract(variables.columnMetaVariables);
2143 system.transitions = system.transitions / stateToNumberOfChoices;
2144
2145 // Scale all state-action rewards.
2146 for (auto& entry : system.transientEdgeAssignments) {
2147 entry.second = entry.second / stateToNumberOfChoices;
2148 }
2149 }
2150
2151 // If we were asked to treat some states as terminal states, we cut away their transitions now.
2152 storm::dd::Bdd<Type> terminalStatesBdd = variables.manager->getBddZero();
2153 if (!options.terminalStates.empty()) {
2154 storm::expressions::Expression terminalExpression = options.terminalStates.asExpression([&model, &labelsToExpressionMap](std::string const& labelName) {
2155 auto exprIt = labelsToExpressionMap.find(labelName);
2156 if (exprIt != labelsToExpressionMap.end()) {
2157 return exprIt->second;
2158 } else {
2159 STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException,
2160 "Terminal states refer to illegal label '" << labelName << "'.");
2161 // 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.
2162 return model.getExpressionManager().boolean(labelName == "init");
2163 }
2164 });
2165 terminalExpression = terminalExpression.substitute(model.getConstantsSubstitution());
2166 terminalStatesBdd = variables.rowExpressionAdapter->translateExpression(terminalExpression).toBdd();
2167 system.transitions *= (!terminalStatesBdd).template toAdd<ValueType>();
2168 }
2169 return terminalStatesBdd;
2170}
2171
2172template<storm::dd::DdType Type, typename ValueType>
2174 std::vector<std::reference_wrapper<storm::jani::Automaton const>> allAutomata;
2175 for (auto const& automaton : model.getAutomata()) {
2176 allAutomata.push_back(automaton);
2177 }
2178 storm::dd::Bdd<Type> initialStates = variables.rowExpressionAdapter->translateExpression(model.getInitialStatesExpression(allAutomata)).toBdd();
2179 for (auto const& automaton : model.getAutomata()) {
2180 storm::dd::Bdd<Type> initialLocationIndices = variables.manager->getBddZero();
2181 for (auto const& locationIndex : automaton.getInitialLocationIndices()) {
2182 initialLocationIndices |= variables.manager->getEncoding(variables.automatonToLocationDdVariableMap.at(automaton.getName()).first, locationIndex);
2183 }
2184 initialStates &= initialLocationIndices;
2185 }
2186 for (auto const& metaVariable : variables.rowMetaVariables) {
2187 initialStates &= variables.variableToRangeMap.at(metaVariable);
2188 }
2189 return initialStates;
2190}
2191
2192template<storm::dd::DdType Type, typename ValueType>
2194 storm::dd::Bdd<Type> const& transitionMatrixBdd, storm::dd::Bdd<Type> const& reachableStates,
2195 CompositionVariables<Type, ValueType> const& variables) {
2196 // Detect deadlocks and 1) fix them if requested 2) throw an error otherwise.
2197 storm::dd::Bdd<Type> statesWithTransition = transitionMatrixBdd.existsAbstract(variables.columnMetaVariables);
2198 storm::dd::Bdd<Type> deadlockStates = reachableStates && !statesWithTransition;
2199
2200 if (!deadlockStates.isZero()) {
2201 // If we need to fix deadlocks, we do so now.
2202 if (!storm::settings::getModule<storm::settings::modules::BuildSettings>().isDontFixDeadlocksSet()) {
2203 STORM_LOG_INFO("Fixing deadlocks in " << deadlockStates.getNonZeroCount() << " states. The first three of these states are: ");
2204
2205 storm::dd::Add<Type, ValueType> deadlockStatesAdd = deadlockStates.template toAdd<ValueType>();
2206 uint_fast64_t count = 0;
2207 for (auto it = deadlockStatesAdd.begin(), ite = deadlockStatesAdd.end(); it != ite && count < 3; ++it, ++count) {
2208 STORM_LOG_INFO((*it).first.toPrettyString(variables.rowMetaVariables) << '\n');
2209 }
2210
2211 // Create a global identity DD.
2212 storm::dd::Add<Type, ValueType> globalIdentity = variables.manager->template getAddOne<ValueType>();
2213 for (auto const& identity : variables.automatonToIdentityMap) {
2214 globalIdentity *= identity.second;
2215 }
2216 for (auto const& variable : variables.allGlobalVariables) {
2217 globalIdentity *= variables.variableToIdentityMap.at(variable);
2218 }
2219
2220 if (modelType == storm::jani::ModelType::DTMC || modelType == storm::jani::ModelType::CTMC) {
2221 // For DTMCs, we can simply add the identity of the global module for all deadlock states.
2222 transitionMatrix += deadlockStatesAdd * globalIdentity;
2223 } else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS || modelType == storm::jani::ModelType::MA) {
2224 // For nondeterministic models, however, we need to select an action associated with the self-loop, if we do not
2225 // want to attach a lot of self-loops to the deadlock states.
2227 encodeAction(boost::none, modelType == storm::jani::ModelType::MA ? boost::make_optional(true) : boost::none, variables);
2228
2229 for (auto const& variable : variables.localNondeterminismVariables) {
2230 action *= variables.manager->getEncoding(variable, 0).template toAdd<ValueType>();
2231 }
2232
2233 transitionMatrix += deadlockStatesAdd * globalIdentity * action;
2234 }
2235 } else {
2236 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
2237 "The model contains " << deadlockStates.getNonZeroCount()
2238 << " deadlock states. Please unset the option to not fix deadlocks, if you want to fix them automatically.");
2239 }
2240 }
2241 return deadlockStates;
2242}
2243
2244template<storm::dd::DdType Type, typename ValueType>
2245std::vector<storm::expressions::Variable> selectRewardVariables(storm::jani::Model const& model,
2246 typename DdJaniModelBuilder<Type, ValueType>::Options const& options) {
2247 std::vector<storm::expressions::Variable> rewardVariables;
2248 if (options.isBuildAllRewardModelsSet()) {
2249 for (auto const& rewExpr : model.getAllRewardModelExpressions()) {
2250 STORM_LOG_THROW(!model.isNonTrivialRewardModelExpression(rewExpr.first), storm::exceptions::NotSupportedException,
2251 "The DD-builder can not build the non-trivial reward expression '" << rewExpr.second << "'.");
2252 rewardVariables.push_back(rewExpr.second.getBaseExpression().asVariableExpression().getVariable());
2253 }
2254 } else {
2255 for (auto const& rewardModelName : options.getRewardModelNames()) {
2256 STORM_LOG_THROW(!model.isNonTrivialRewardModelExpression(rewardModelName), storm::exceptions::NotSupportedException,
2257 "The DD-builder can not build the non-trivial reward expression '" << rewardModelName << "'.");
2258 auto const& rewExpr = model.getRewardModelExpression(rewardModelName);
2259 rewardVariables.push_back(rewExpr.getBaseExpression().asVariableExpression().getVariable());
2260 }
2261 }
2262 // Sort the reward variables to match the order in the ordered assignments
2263 std::sort(rewardVariables.begin(), rewardVariables.end());
2264
2265 return rewardVariables;
2266}
2267
2268template<storm::dd::DdType Type, typename ValueType>
2269std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> buildRewardModels(
2270 storm::dd::Add<Type, ValueType> const& reachableStates, storm::dd::Add<Type, ValueType> const& transitionMatrix, storm::jani::ModelType const& modelType,
2272 std::vector<storm::expressions::Variable> const& rewardVariables) {
2273 std::unordered_map<std::string, storm::models::symbolic::StandardRewardModel<Type, ValueType>> result;
2274
2275 // For CTMCs, we need to scale the state-action rewards with the total exit rates.
2276 boost::optional<storm::dd::Add<Type, ValueType>> exitRates;
2277 if (modelType == storm::jani::ModelType::CTMC || modelType == storm::jani::ModelType::DTMC) {
2278 exitRates = transitionMatrix.sumAbstract(variables.columnMetaVariables);
2279 }
2280
2281 for (auto const& variable : rewardVariables) {
2282 boost::optional<storm::dd::Add<Type, ValueType>> stateRewards = boost::none;
2283 boost::optional<storm::dd::Add<Type, ValueType>> stateActionRewards = boost::none;
2284 boost::optional<storm::dd::Add<Type, ValueType>> transitionRewards = boost::none;
2285
2286 auto it = system.transientLocationAssignments.find(variable);
2287 if (it != system.transientLocationAssignments.end()) {
2288 stateRewards = reachableStates * it->second;
2289 }
2290
2291 it = system.transientEdgeAssignments.find(variable);
2292 if (it != system.transientEdgeAssignments.end()) {
2293 stateActionRewards = reachableStates * it->second;
2294 if (exitRates) {
2295 stateActionRewards.get() = stateActionRewards.get() / exitRates.get();
2296 }
2297 }
2298
2299 result.emplace(variable.getName(), storm::models::symbolic::StandardRewardModel<Type, ValueType>(stateRewards, stateActionRewards, transitionRewards));
2300 }
2301
2302 return result;
2303}
2304
2305template<storm::dd::DdType Type, typename ValueType>
2306std::map<std::string, storm::expressions::Expression> buildLabelExpressions(storm::jani::Model const& model,
2308 typename DdJaniModelBuilder<Type, ValueType>::Options const& options) {
2309 std::map<std::string, storm::expressions::Expression> result;
2310
2311 // Create a list of composed automata to restrict the labels to locations of these automata.
2312 std::vector<std::reference_wrapper<storm::jani::Automaton const>> composedAutomata;
2313 for (auto const& entry : variables.automatonToIdentityMap) {
2314 composedAutomata.emplace_back(model.getAutomaton(entry.first));
2315 }
2316
2317 for (auto const& variable : model.getGlobalVariables().getTransientVariables()) {
2318 if (variable.getType().isBasicType() && variable.getType().asBasicType().isBooleanType()) {
2319 if (options.buildAllLabels || options.labelNames.find(variable.getName()) != options.labelNames.end()) {
2320 result[variable.getName()] = model.getLabelExpression(variable, composedAutomata);
2321 }
2322 }
2323 }
2324
2325 return result;
2326}
2327
2328template<storm::dd::DdType Type, typename ValueType>
2329std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> buildInternal(storm::jani::Model const& model,
2330 typename DdJaniModelBuilder<Type, ValueType>::Options const& options,
2331 std::shared_ptr<storm::dd::DdManager<Type>> const& manager) {
2332 // Determine the actions that will appear in the parallel composition.
2334 storm::jani::CompositionInformation actionInformation = visitor.getInformation();
2335
2336 // Create all necessary variables.
2337 CompositionVariableCreator<Type, ValueType> variableCreator(model, actionInformation);
2338 CompositionVariables<Type, ValueType> variables = variableCreator.create(manager);
2339
2340 // Determine which transient assignments need to be considered in the building process.
2341 std::vector<storm::expressions::Variable> rewardVariables = selectRewardVariables<Type, ValueType>(model, options);
2342
2343 // Create a builder to compose and build the model.
2344 bool applyMaximumProgress = options.applyMaximumProgressAssumption && model.getModelType() == storm::jani::ModelType::MA;
2345 CombinedEdgesSystemComposer<Type, ValueType> composer(model, actionInformation, variables, rewardVariables, applyMaximumProgress);
2346 ComposerResult<Type, ValueType> system = composer.compose();
2347
2348 // Postprocess the variables in place.
2349 postprocessVariables(model.getModelType(), system, variables);
2350
2351 // Build the label to expressions mapping.
2352 auto labelsToExpressionMap = buildLabelExpressions(model, variables, options);
2353
2354 // Postprocess the system in place and get the states that were terminal (i.e. whose transitions were cut off).
2355 storm::dd::Bdd<Type> terminalStates = postprocessSystem(model, system, variables, options, labelsToExpressionMap);
2356
2357 // Start creating the model components.
2358 ModelComponents<Type, ValueType> modelComponents;
2359
2360 // Set the label expressions
2361 modelComponents.labelToExpressionMap = std::move(labelsToExpressionMap);
2362
2363 // Build initial states.
2364 modelComponents.initialStates = computeInitialStates(model, variables);
2365
2366 // Perform reachability analysis to obtain reachable states.
2367 storm::dd::Bdd<Type> transitionMatrixBdd = system.transitions.notZero();
2370 transitionMatrixBdd = transitionMatrixBdd.existsAbstract(variables.allNondeterminismVariables);
2371 }
2372 modelComponents.reachableStates = storm::utility::dd::computeReachableStates(modelComponents.initialStates, transitionMatrixBdd, variables.rowMetaVariables,
2373 variables.columnMetaVariables)
2374 .first;
2375
2376 // Check that the reachable fragment does not overlap with the illegal fragment.
2377 storm::dd::Bdd<Type> reachableIllegalFragment = modelComponents.reachableStates && system.illegalFragment;
2378 STORM_LOG_THROW(reachableIllegalFragment.isZero(), storm::exceptions::WrongFormatException,
2379 "There are reachable states in the model that have synchronizing edges enabled that write the same global variable.");
2380
2381 // Cut transitions to reachable states.
2382 storm::dd::Add<Type, ValueType> reachableStatesAdd = modelComponents.reachableStates.template toAdd<ValueType>();
2383 modelComponents.transitionMatrix = system.transitions * reachableStatesAdd;
2384
2385 // Fix deadlocks if existing.
2386 modelComponents.deadlockStates =
2387 fixDeadlocks(model.getModelType(), modelComponents.transitionMatrix, transitionMatrixBdd, modelComponents.reachableStates, variables);
2388
2389 // Cut the deadlock states by removing all states that we 'converted' to deadlock states by making them terminal.
2390 modelComponents.deadlockStates = modelComponents.deadlockStates && !terminalStates;
2391
2392 // Build the reward models.
2393 modelComponents.rewardModels =
2394 buildRewardModels(reachableStatesAdd, modelComponents.transitionMatrix, model.getModelType(), variables, system, rewardVariables);
2395
2396 // Finally, create the model.
2397 return createModel(model.getModelType(), variables, modelComponents);
2398}
2399
2400template<storm::dd::DdType Type, typename ValueType>
2401std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> DdJaniModelBuilder<Type, ValueType>::build(storm::jani::Model const& model,
2402 Options const& options) {
2403 // Prepare the model and do some sanity checks
2404 if (!std::is_same<ValueType, storm::RationalFunction>::value && model.hasUndefinedConstants()) {
2405 std::vector<std::reference_wrapper<storm::jani::Constant const>> undefinedConstants = model.getUndefinedConstants();
2406 std::vector<std::string> strings;
2407 for (auto const& constant : undefinedConstants) {
2408 std::stringstream stream;
2409 stream << constant.get().getName() << " (" << constant.get().getType() << ")";
2410 strings.push_back(stream.str());
2411 }
2412 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException,
2413 "Model still contains these undefined constants: " << boost::join(strings, ", ") << ".");
2414 }
2415
2416 STORM_LOG_THROW(!model.usesAssignmentLevels(), storm::exceptions::WrongFormatException,
2417 "The symbolic JANI model builder currently does not support assignment levels.");
2418 auto features = model.getModelFeatures();
2422
2423 storm::jani::Model preparedModel = model;
2424 preparedModel.simplifyComposition();
2425 if (features.hasArrays()) {
2427 "The jani model still considers arrays. These should have been eliminated before calling the dd builder. The arrays are eliminated now, but "
2428 "occurrences in properties will not be handled properly.");
2429 preparedModel.eliminateArrays();
2430 features.remove(storm::jani::ModelFeature::Arrays);
2431 }
2432 if (features.hasFunctions()) {
2434 "The jani model still considers functions. These should have been substituted before calling the dd builder. The functions are substituted now, "
2435 "but occurrences in properties will not be handled properly.");
2436 preparedModel.substituteFunctions();
2437 features.remove(storm::jani::ModelFeature::Functions);
2438 }
2439 STORM_LOG_THROW(features.empty(), storm::exceptions::InvalidStateException,
2440 "The dd jani model builder does not support the following model feature(s): " << features.toString() << ".");
2441
2442 // Lift the transient edge destinations. We can do so, as we know that there are no assignment levels (because that's not supported anyway).
2443 if (preparedModel.hasTransientEdgeDestinationAssignments()) {
2444 // This operation is correct as we are asserting that there are no assignment levels and no non-trivial reward expressions.
2446 }
2447
2448 STORM_LOG_THROW(!preparedModel.hasTransientEdgeDestinationAssignments(), storm::exceptions::WrongFormatException,
2449 "The symbolic JANI model builder currently does not support transient edge destination assignments.");
2450
2451 // Create the manager
2452 auto manager = std::make_shared<storm::dd::DdManager<Type>>();
2453
2454 // Prepare a result
2455 std::shared_ptr<storm::models::symbolic::Model<Type, ValueType>> result;
2456
2457 // invoke the builder
2458 manager->execute([&preparedModel, &options, &manager, &result]() { result = buildInternal<Type, ValueType>(preparedModel, options, manager); });
2459
2460 return result;
2461}
2462
2465
2468} // namespace builder
2469} // namespace storm
void setValue(storm::expressions::Variable const &variable, ValueType const &value)
CombinedEdgesSystemComposer(storm::jani::Model const &model, storm::jani::CompositionInformation const &actionInformation, CompositionVariables< Type, ValueType > const &variables, std::vector< storm::expressions::Variable > const &transientVariables, bool applyMaximumProgress)
std::map< uint64_t, std::vector< ActionInstantiation > > ActionInstantiations
boost::any visit(storm::jani::AutomatonComposition const &composition, boost::any const &data) override
boost::any visit(storm::jani::ParallelComposition const &composition, boost::any const &data) override
storm::jani::CompositionInformation const & actionInformation
ComposerResult< Type, ValueType > compose() override
CompositionVariables< Type, ValueType > create(std::shared_ptr< storm::dd::DdManager< Type > > const &manager)
boost::any visit(storm::jani::AutomatonComposition const &composition, boost::any const &) override
boost::any visit(storm::jani::ParallelComposition const &composition, boost::any const &data) override
CompositionVariableCreator(storm::jani::Model const &model, storm::jani::CompositionInformation const &actionInformation)
static storm::jani::ModelFeatures getSupportedJaniFeatures()
Returns the jani features with which this builder can deal natively.
static bool canHandle(storm::jani::Model const &model, boost::optional< std::vector< storm::jani::Property > > const &properties=boost::none)
A quick check to detect whether the given model is not supported.
std::set< storm::RationalFunctionVariable > const & getParameters() const
RationalFunctionType convertVariableToPolynomial(storm::RationalFunctionVariable const &variable)
void create(storm::jani::Model const &model, storm::adapters::AddExpressionAdapter< Type, storm::RationalFunction > &rowExpressionAdapter)
std::set< storm::RationalFunctionVariable > const & getParameters() const
void create(storm::jani::Model const &, storm::adapters::AddExpressionAdapter< Type, ValueType > &)
virtual ComposerResult< Type, ValueType > compose()=0
CompositionVariables< Type, ValueType > const & variables
storm::jani::Model const & model
SystemComposer(storm::jani::Model const &model, CompositionVariables< Type, ValueType > const &variables, std::vector< storm::expressions::Variable > const &transientVariables)
std::vector< storm::expressions::Variable > transientVariables
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
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
Bdd< LibraryType > toBdd() const
Converts the ADD to a BDD by mapping all values unequal to zero to 1.
Definition Add.cpp:1180
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
static Bdd< LibraryType > getEncoding(DdManager< LibraryType > const &ddManager, uint64_t targetOffset, storm::dd::Odd const &odd, std::set< storm::expressions::Variable > const &metaVariables)
Constructs the BDD representation of the encoding with the given offset.
Definition Bdd.cpp:88
void addMetaVariable(storm::expressions::Variable const &metaVariable)
Adds the given meta variable to the set of meta variables that are contained in this DD.
Definition Dd.cpp:51
void addMetaVariables(std::set< storm::expressions::Variable > const &metaVariables)
Adds the given set of meta variables to the DD.
Definition Dd.cpp:43
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.
bool isBooleanType() const
Checks whether this type is a boolean type.
Definition Type.cpp:178
Type const & getType() const
Retrieves the type of the variable.
Definition Variable.cpp:50
std::string const & getName() const
Retrieves the name of the variable.
Definition Variable.cpp:46
storm::expressions::Variable const & getExpressionVariable() const
Retrieves the expression variable that is written in this assignment.
std::string const & getAutomatonName() const
Retrieves the name of the automaton this composition element refers to.
std::set< std::string > const & getInputEnabledActions() const
VariableSet & getVariables()
Retrieves the variables of this automaton.
Definition Automaton.cpp:59
storm::expressions::Variable const & getLocationExpressionVariable() const
Retrieves the expression variable that represents the location of this automaton.
Location const & getLocation(uint64_t index) const
Retrieves the location with the given index.
uint64_t getNumberOfLocations() const
Retrieves the number of locations.
bool hasEdgeLabeledWithActionIndex(uint64_t actionIndex) const
Retrieves whether there is an edge labeled with the action with the given index in this automaton.
std::string const & getName() const
Retrieves the name of the automaton.
Definition Automaton.cpp:47
virtual boost::any accept(CompositionVisitor &visitor, boost::any const &data) const =0
std::string const & getActionName(uint64_t index) const
std::set< uint64_t > const & getNonSilentActionIndices() const
uint64_t getActionIndex(std::string const &name) const
storm::expressions::Expression const & getProbability() const
Retrieves the probability of choosing this destination.
OrderedAssignments const & getOrderedAssignments() const
Retrieves the assignments to make when choosing this destination.
uint64_t getLocationIndex() const
Retrieves the id of the destination location.
uint64_t getActionIndex() const
Retrieves the id of the action with which this edge is labeled.
Definition Edge.cpp:45
bool hasRate() const
Retrieves whether this edge has an associated rate.
Definition Edge.cpp:49
uint64_t getSourceLocationIndex() const
Retrieves the index of the source location.
Definition Edge.cpp:41
OrderedAssignments const & getAssignments() const
Retrieves the assignments of this edge.
Definition Edge.cpp:89
storm::expressions::Expression const & getRate() const
Retrieves the rate of this edge.
Definition Edge.cpp:53
storm::expressions::Expression const & getGuard() const
Retrieves the guard of this edge.
Definition Edge.cpp:65
BoundedType const & asBoundedType() const
Definition JaniType.cpp:39
ModelFeatures & add(ModelFeature const &modelFeature)
void remove(ModelFeature const &modelFeature)
bool hasUndefinedConstants() const
Retrieves whether the model still has undefined constants.
Definition Model.cpp:1083
VariableSet & getGlobalVariables()
Retrieves the variables of this automaton.
Definition Model.cpp:721
storm::expressions::ExpressionManager & getExpressionManager() const
Retrieves the manager responsible for the expressions in the JANI model.
Definition Model.cpp:789
storm::expressions::Expression getRewardModelExpression(std::string const &identifier) const
Retrieves the defining reward expression of the reward model with the given identifier.
Definition Model.cpp:813
Composition const & getSystemComposition() const
Retrieves the system composition expression.
Definition Model.cpp:949
bool hasTransientEdgeDestinationAssignments() const
Retrieves whether there is any transient edge destination assignment in the model.
Definition Model.cpp:1556
void liftTransientEdgeDestinationAssignments(int64_t maxLevel=0)
Lifts the common edge destination assignments of transient variables to edge assignments.
Definition Model.cpp:1550
storm::expressions::Expression getInitialStatesExpression() const
Retrieves the expression defining the legal initial values of the variables.
Definition Model.cpp:1317
static const uint64_t SILENT_ACTION_INDEX
The index of the silent action.
Definition Model.h:642
std::vector< Automaton > & getAutomata()
Retrieves the automata of the model.
Definition Model.cpp:872
ModelType const & getModelType() const
Retrieves the type of the model.
Definition Model.cpp:121
bool hasNonTrivialRewardExpression() const
Returns true iff there is a non-trivial reward model, i.e., a reward model that does not consist of a...
Definition Model.cpp:793
std::vector< Constant > const & getConstants() const
Retrieves the constants of the model.
Definition Model.cpp:689
storm::expressions::Expression getLabelExpression(Variable const &transientVariable, std::vector< std::reference_wrapper< Automaton const > > const &automata) const
Creates the expression that characterizes all states in which the provided transient boolean variable...
Definition Model.cpp:1425
void substituteFunctions()
Substitutes all function calls with the corresponding function definition.
Definition Model.cpp:1210
std::vector< std::pair< std::string, storm::expressions::Expression > > getAllRewardModelExpressions() const
Retrieves all available reward model names and expressions of the model.
Definition Model.cpp:841
bool isNonTrivialRewardModelExpression(std::string const &identifier) const
Returns true iff the given identifier corresponds to a non-trivial reward expression i....
Definition Model.cpp:797
void simplifyComposition()
Attempts to simplify the composition.
Definition Model.cpp:985
static const std::string SILENT_ACTION_NAME
The name of the silent action.
Definition Model.h:639
ModelFeatures const & getModelFeatures() const
Retrieves the enabled model features.
Definition Model.cpp:129
std::map< storm::expressions::Variable, storm::expressions::Expression > getConstantsSubstitution() const
Retrieves a mapping from expression variables associated with defined constants of the model to their...
Definition Model.cpp:1159
Automaton & getAutomaton(std::string const &name)
Retrieves the automaton with the given name.
Definition Model.cpp:888
bool usesAssignmentLevels(bool onlyTransient=false) const
Retrieves whether the model uses an assignment level other than zero.
Definition Model.cpp:1565
ArrayEliminatorData eliminateArrays(bool keepNonTrivialArrayAccess=false)
Eliminates occurring array variables and expressions by replacing array variables by multiple basic v...
Definition Model.cpp:1231
std::vector< std::reference_wrapper< Constant const > > getUndefinedConstants() const
Retrieves all undefined constants of the model.
Definition Model.cpp:1092
detail::ConstAssignments getNonTransientAssignments() const
Returns all non-transient assignments in this set of assignments.
detail::ConstAssignments getTransientAssignments() const
Returns all transient assignments in this set of assignments.
uint64_t getNumberOfSubcompositions() const
Retrieves the number of subcompositions of this parallel composition.
std::vector< SynchronizationVector > const & getSynchronizationVectors() const
Retrieves the synchronization vectors of the parallel composition.
std::size_t getNumberOfSynchronizationVectors() const
Retrieves the number of synchronization vectors.
SynchronizationVector const & getSynchronizationVector(uint64_t index) const
Retrieves the synchronization vector with the given index.
std::vector< std::shared_ptr< Composition > > const & getSubcompositions() const
Retrieves the subcompositions of the parallel composition.
Composition const & getSubcomposition(uint64_t index) const
Retrieves the subcomposition with the given index.
static const std::string NO_ACTION_INPUT
std::vector< std::string > const & getInput() const
storm::expressions::Variable const & getExpressionVariable() const
Retrieves the associated expression variable.
Definition Variable.cpp:26
JaniType & getType()
Definition Variable.cpp:67
std::string const & getName() const
Retrieves the name of the variable.
Definition Variable.cpp:34
detail::Variables< Variable > getBoundedIntegerVariables()
Retrieves the bounded integer variables in this set.
detail::Variables< Variable > getBooleanVariables()
Retrieves the boolean variables in this set.
detail::ConstVariables< Variable > getTransientVariables() const
Retrieves the transient variables in this variable set.
std::vector< std::shared_ptr< AtomicLabelFormula const > > getAtomicLabelFormulas() const
Definition Formula.cpp:506
std::set< std::string > getReferencedRewardModels() const
Definition Formula.cpp:518
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ERROR(message)
Definition logging.h:26
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#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.
std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > createModel(storm::jani::ModelType const &modelType, CompositionVariables< Type, ValueType > const &variables, ModelComponents< Type, ValueType > const &modelComponents)
storm::dd::Add< Type, ValueType > encodeIndex(uint64_t index, uint64_t localNondeterminismVariableOffset, uint64_t numberOfLocalNondeterminismVariables, CompositionVariables< Type, ValueType > const &variables)
std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > buildInternal(storm::jani::Model const &model, typename DdJaniModelBuilder< Type, ValueType >::Options const &options, std::shared_ptr< storm::dd::DdManager< Type > > const &manager)
storm::dd::Bdd< Type > fixDeadlocks(storm::jani::ModelType const &modelType, storm::dd::Add< Type, ValueType > &transitionMatrix, storm::dd::Bdd< Type > const &transitionMatrixBdd, storm::dd::Bdd< Type > const &reachableStates, CompositionVariables< Type, ValueType > const &variables)
storm::dd::Add< Type, ValueType > encodeAction(boost::optional< uint64_t > const &actionIndex, boost::optional< bool > const &markovian, CompositionVariables< Type, ValueType > const &variables)
std::unordered_map< std::string, storm::models::symbolic::StandardRewardModel< Type, ValueType > > buildRewardModels(storm::dd::Add< Type, ValueType > const &reachableStates, storm::dd::Add< Type, ValueType > const &transitionMatrix, storm::jani::ModelType const &modelType, CompositionVariables< Type, ValueType > const &variables, ComposerResult< Type, ValueType > const &system, std::vector< storm::expressions::Variable > const &rewardVariables)
storm::dd::Bdd< Type > postprocessSystem(storm::jani::Model const &model, ComposerResult< Type, ValueType > &system, CompositionVariables< Type, ValueType > const &variables, typename DdJaniModelBuilder< Type, ValueType >::Options const &options, std::map< std::string, storm::expressions::Expression > const &labelsToExpressionMap)
EdgeDestinationDd< Type, ValueType > buildEdgeDestinationDd(storm::jani::Automaton const &automaton, storm::jani::EdgeDestination const &destination, storm::dd::Bdd< Type > const &guard, CompositionVariables< Type, ValueType > const &variables)
std::map< std::string, storm::expressions::Expression > buildLabelExpressions(storm::jani::Model const &model, CompositionVariables< Type, ValueType > const &variables, typename DdJaniModelBuilder< Type, ValueType >::Options const &options)
std::vector< storm::expressions::Variable > selectRewardVariables(storm::jani::Model const &model, typename DdJaniModelBuilder< Type, ValueType >::Options const &options)
void postprocessVariables(storm::jani::ModelType const &modelType, ComposerResult< Type, ValueType > &system, CompositionVariables< Type, ValueType > &variables)
storm::dd::Bdd< Type > computeInitialStates(storm::jani::Model const &model, CompositionVariables< Type, ValueType > const &variables)
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
ModelType getModelType(std::string const &type)
Definition ModelType.cpp:9
std::set< storm::RationalFunctionVariable > getVariables(SparseMatrix< storm::RationalFunction > const &matrix)
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
bool isZero(ValueType const &a)
Definition constants.cpp:39
carl::Variable RationalFunctionVariable
carl::RationalFunction< Polynomial, true > RationalFunction
std::pair< uint64_t, uint64_t > const & getLocalNondeterminismVariables() const
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > transientEdgeAssignments
std::map< storm::expressions::Variable, storm::dd::Bdd< Type > > variableToWritingFragment
void conjunctGuardWith(storm::dd::Bdd< Type > const &condition)
Conjuncts the guard of the action with the provided condition, i.e., this action is only enabled if t...
ActionDd multiplyTransitions(storm::dd::Add< Type, ValueType > const &factor) const
ActionDd(storm::dd::Bdd< Type > const &guard=storm::dd::Bdd< Type >(), storm::dd::Add< Type, ValueType > const &transitions=storm::dd::Add< Type, ValueType >(), std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > const &transientEdgeAssignments={}, std::pair< uint64_t, uint64_t > localNondeterminismVariables=std::pair< uint64_t, uint64_t >(0, 0), std::map< storm::expressions::Variable, storm::dd::Bdd< Type > > const &variableToWritingFragment={}, storm::dd::Bdd< Type > const &illegalFragment=storm::dd::Bdd< Type >())
std::size_t operator()(ActionIdentification const &identification) const
ActionIdentification(uint64_t actionIndex, uint64_t synchronizationVectorIndex, bool markovian=false)
ActionIdentification(uint64_t actionIndex, boost::optional< uint64_t > synchronizationVectorIndex, bool markovian=false)
std::size_t operator()(ActionInstantiation const &instantiation) const
ActionInstantiation(uint64_t actionIndex, uint64_t synchronizationVectorIndex, uint64_t localNondeterminismVariableOffset, bool markovian=false)
ActionInstantiation(uint64_t actionIndex, uint64_t localNondeterminismVariableOffset, bool markovian=false)
void extendLocalNondeterminismVariables(std::pair< uint64_t, uint64_t > const &localNondeterminismVariables)
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > transientLocationAssignments
AutomatonDd(storm::dd::Add< Type, ValueType > const &identity, std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > const &transientLocationAssignments={})
std::unordered_map< ActionIdentification, ActionDd, ActionIdentificationHash > actions
std::map< storm::expressions::Variable, storm::dd::Bdd< Type > > variableToWritingFragment
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > transientEdgeAssignments
EdgeDd(bool isMarkovian, storm::dd::Bdd< Type > const &guard, storm::dd::Add< Type, ValueType > const &transitions, std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > const &transientEdgeAssignments, std::set< storm::expressions::Variable > const &writtenGlobalVariables)
EdgeDd(bool isMarkovian, storm::dd::Bdd< Type > const &guard, storm::dd::Add< Type, ValueType > const &transitions, std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > const &transientEdgeAssignments, std::map< storm::expressions::Variable, storm::dd::Bdd< Type > > const &variableToWritingFragment)
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > transientLocationAssignments
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > transientEdgeAssignments
storm::dd::Bdd< Type > illegalFragment
storm::dd::Add< Type, ValueType > transitions
ComposerResult(storm::dd::Add< Type, ValueType > const &transitions, std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > const &transientLocationAssignments, std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > const &transientEdgeAssignments, storm::dd::Bdd< Type > const &illegalFragment, uint64_t numberOfNondeterminismVariables=0)
std::map< storm::expressions::Variable, storm::dd::Add< Type, ValueType > > variableToIdentityMap
std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > rowColumnMetaVariablePairs
storm::dd::Add< Type, ValueType > globalVariableRanges
std::set< storm::expressions::Variable > allNondeterminismVariables
std::shared_ptr< std::map< storm::expressions::Variable, storm::expressions::Variable > > variableToRowMetaVariableMap
std::map< uint64_t, storm::expressions::Variable > actionVariablesMap
std::set< storm::expressions::Variable > allGlobalVariables
std::map< std::string, std::pair< storm::expressions::Variable, storm::expressions::Variable > > automatonToLocationDdVariableMap
std::map< std::string, storm::dd::Add< Type, ValueType > > automatonToRangeMap
std::shared_ptr< storm::adapters::AddExpressionAdapter< Type, ValueType > > rowExpressionAdapter
std::map< std::string, storm::dd::Add< Type, ValueType > > automatonToIdentityMap
std::vector< storm::expressions::Variable > localNondeterminismVariables
CompositionVariables(std::shared_ptr< storm::dd::DdManager< Type > > const &manager)
std::map< storm::expressions::Variable, storm::dd::Bdd< Type > > variableToRangeMap
std::set< storm::expressions::Variable > columnMetaVariables
std::shared_ptr< storm::dd::DdManager< Type > > manager
storm::expressions::Variable probabilisticNondeterminismVariable
std::shared_ptr< std::map< storm::expressions::Variable, storm::expressions::Variable > > variableToColumnMetaVariableMap
std::set< storm::RationalFunctionVariable > parameters
std::set< storm::expressions::Variable > rowMetaVariables
bool buildAllLabels
A flag that indicates whether all labels are to be built. In this case, the label names are to be ign...
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(bool buildAllLabels=false, bool buildAllRewardModels=false, bool applyMaximumProgressAssumption=true)
Creates an object representing the default building options.
storm::builder::TerminalStates terminalStates
bool isBuildAllRewardModelsSet() const
Retrieves whether the flag to build all reward models is set.
bool applyMaximumProgressAssumption
A flag that indicates whether the maximum progress assumption should be applied.
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 addLabel(std::string const &labelName)
Adds the given label to the ones that are supposed to be built.
std::set< std::string > const & getRewardModelNames() const
Retrieves the names of the reward models to build.
std::set< std::string > labelNames
A set of labels to build.
bool isBuildAllLabelsSet() const
Retrieves whether the flag to build all labels is set.
storm::dd::Add< Type, ValueType > transitions
EdgeDestinationDd(storm::dd::Add< Type, ValueType > const &transitions, std::set< storm::expressions::Variable > const &writtenGlobalVariables={})
std::set< storm::expressions::Variable > writtenGlobalVariables
std::map< std::string, storm::expressions::Expression > labelToExpressionMap
std::unordered_map< std::string, storm::models::symbolic::StandardRewardModel< Type, ValueType > > rewardModels
storm::dd::Add< Type, ValueType > transitionMatrix
bool empty() const
True if no terminal states are specified.
storm::expressions::Expression asExpression(std::function< storm::expressions::Expression(std::string const &)> const &labelToExpressionMap) const
Returns an expression that evaluates to true only if the exploration can stop at the corresponding st...