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