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