Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
InternalSylvanBdd.cpp
Go to the documentation of this file.
2
3#include <boost/functional/hash.hpp>
4
8
11
15
16#include "storm-config.h"
18
19namespace storm {
20namespace dd {
21InternalBdd<DdType::Sylvan>::InternalBdd() : ddManager(nullptr), sylvanBdd() {
22 // Intentionally left empty.
23}
24
26 : ddManager(ddManager), sylvanBdd(sylvanBdd) {
27 // Intentionally left empty.
28}
29
31 std::vector<uint_fast64_t> const& sortedDdVariableIndices,
32 std::function<bool(uint64_t)> const& filter) {
33 uint_fast64_t offset = 0;
34 return InternalBdd<DdType::Sylvan>(ddManager, sylvan::Bdd(fromVectorRec(offset, 0, sortedDdVariableIndices.size(), odd, sortedDdVariableIndices, filter)));
35}
36
37BDD InternalBdd<DdType::Sylvan>::fromVectorRec(uint_fast64_t& currentOffset, uint_fast64_t currentLevel, uint_fast64_t maxLevel, Odd const& odd,
38 std::vector<uint_fast64_t> const& ddVariableIndices, std::function<bool(uint64_t)> const& filter) {
39 if (currentLevel == maxLevel) {
40 // If we are in a terminal node of the ODD, we need to check whether the then-offset of the ODD is one
41 // (meaning the encoding is a valid one) or zero (meaning the encoding is not valid). Consequently, we
42 // need to copy the next value of the vector iff the then-offset is greater than zero.
43 if (odd.getThenOffset() > 0) {
44 if (filter(currentOffset++)) {
45 return sylvan_true;
46 } else {
47 return sylvan_false;
48 }
49 } else {
50 return sylvan_false;
51 }
52 } else {
53 // If the total offset is zero, we can just return the constant zero DD.
54 if (odd.getThenOffset() + odd.getElseOffset() == 0) {
55 return sylvan_false;
56 }
57
58 // Determine the new else-successor.
59 BDD elseSuccessor;
60 if (odd.getElseOffset() > 0) {
61 elseSuccessor = fromVectorRec(currentOffset, currentLevel + 1, maxLevel, odd.getElseSuccessor(), ddVariableIndices, filter);
62 } else {
63 elseSuccessor = sylvan_false;
64 }
65 bdd_refs_push(elseSuccessor);
66
67 // Determine the new then-successor.
68 BDD thenSuccessor;
69 if (odd.getThenOffset() > 0) {
70 thenSuccessor = fromVectorRec(currentOffset, currentLevel + 1, maxLevel, odd.getThenSuccessor(), ddVariableIndices, filter);
71 } else {
72 thenSuccessor = sylvan_false;
73 }
74 bdd_refs_push(thenSuccessor);
75
76 // Create a node representing ITE(currentVar, thenSuccessor, elseSuccessor);
77 BDD currentVar = sylvan_ithvar(static_cast<BDDVAR>(ddVariableIndices[currentLevel]));
78 bdd_refs_push(currentVar);
79
80 BDD result = sylvan_ite(currentVar, thenSuccessor, elseSuccessor);
81
82 // Dispose of the intermediate results.
83 bdd_refs_pop(3);
84
85 return result;
86 }
87}
88
90 return sylvanBdd == other.sylvanBdd;
91}
92
94 return sylvanBdd != other.sylvanBdd;
95}
96
98 std::vector<InternalBdd<DdType::Sylvan>> const&,
99 std::vector<InternalBdd<DdType::Sylvan>> const&) const {
100 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.RelNext(relation.sylvanBdd, sylvan::Bdd(sylvan_false)));
101}
102
104 std::vector<InternalBdd<DdType::Sylvan>> const&,
105 std::vector<InternalBdd<DdType::Sylvan>> const&) const {
106 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.RelPrev(relation.sylvanBdd, sylvan::Bdd(sylvan_false)));
107}
108
110 InternalBdd<DdType::Sylvan> const& relation, std::vector<InternalBdd<DdType::Sylvan>> const& rowVariables,
111 std::vector<InternalBdd<DdType::Sylvan>> const& columnVariables) const {
112 // Currently, there is no specialized version to perform this operation, so we fall back to the regular operations.
113
114 InternalBdd<DdType::Sylvan> columnCube = ddManager->getBddOne();
115 for (auto const& variable : columnVariables) {
116 columnCube &= variable;
117 }
118
119 return this->swapVariables(rowVariables, columnVariables).andExists(relation, columnCube);
120}
121
123 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.Ite(thenDd.sylvanBdd, elseDd.sylvanBdd));
124}
125
126template<typename ValueType>
128 InternalAdd<DdType::Sylvan, ValueType> const& elseAdd) const {
129 return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanBdd.Ite(thenAdd.getSylvanMtbdd(), elseAdd.getSylvanMtbdd()));
130}
131
133 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd | other.sylvanBdd);
134}
135
137 this->sylvanBdd |= other.sylvanBdd;
138 return *this;
139}
140
142 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd & other.sylvanBdd);
143}
144
146 this->sylvanBdd &= other.sylvanBdd;
147 return *this;
148}
149
151 return InternalBdd<DdType::Sylvan>(ddManager, !(this->sylvanBdd ^ other.sylvanBdd));
152}
153
155 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd ^ other.sylvanBdd);
156}
157
159 return InternalBdd<DdType::Sylvan>(ddManager, (!this->sylvanBdd) | other.sylvanBdd);
160}
161
165
167 this->sylvanBdd = !this->sylvanBdd;
168 return *this;
169}
170
172 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.ExistAbstract(cube.sylvanBdd));
173}
174
176 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.ExistAbstractRepresentative(cube.sylvanBdd));
177}
178
180 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.UnivAbstract(cube.sylvanBdd));
181}
182
184 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.AndAbstract(other.sylvanBdd, cube.sylvanBdd));
185}
186
188 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.Constrain(constraint.sylvanBdd));
189}
190
192 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.Restrict(constraint.sylvanBdd));
193}
194
196 std::vector<InternalBdd<DdType::Sylvan>> const& to) const {
197 std::vector<uint32_t> fromIndices;
198 std::vector<uint32_t> toIndices;
199 for (auto it1 = from.begin(), ite1 = from.end(), it2 = to.begin(); it1 != ite1; ++it1, ++it2) {
200 fromIndices.push_back(it1->getIndex());
201 fromIndices.push_back(it2->getIndex());
202 toIndices.push_back(it2->getIndex());
203 toIndices.push_back(it1->getIndex());
204 }
205 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.Permute(fromIndices, toIndices));
206}
207
209 return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanBdd.Support());
210}
211
212uint_fast64_t InternalBdd<DdType::Sylvan>::getNonZeroCount(uint_fast64_t numberOfDdVariables) const {
213 if (numberOfDdVariables == 0) {
214 return 0;
215 }
216 return static_cast<uint_fast64_t>(this->sylvanBdd.SatCount(numberOfDdVariables));
217}
218
220 // For BDDs, the leaf count is always one, because the only leaf is the false leaf (and true is represented
221 // by a negation edge to false).
222 return 1;
223}
224
226 // We have to add one to also count the false-leaf, which is the only leaf appearing in BDDs.
227 return static_cast<uint_fast64_t>(this->sylvanBdd.NodeCount());
228}
229
231 return this->sylvanBdd.isOne();
232}
233
235 return this->sylvanBdd.isZero();
236}
237
239 return static_cast<uint_fast64_t>(this->sylvanBdd.TopVar());
240}
241
243 return this->getIndex();
244}
245
246void InternalBdd<DdType::Sylvan>::exportToDot(std::string const& filename, std::vector<std::string> const&, bool) const {
247 FILE* filePointer = fopen(filename.c_str(), "a+");
248 // fopen returns a nullptr on failure
249 if (filePointer == nullptr) {
250 STORM_LOG_ERROR("Failure to open file: " << filename);
251 } else {
252 this->sylvanBdd.PrintDot(filePointer);
253 fclose(filePointer);
254 }
255}
256
257void InternalBdd<DdType::Sylvan>::exportToText(std::string const& filename) const {
258 FILE* filePointer = fopen(filename.c_str(), "a+");
259 // fopen returns a nullptr on failure
260 if (filePointer == nullptr) {
261 STORM_LOG_ERROR("Failure to open file: " << filename);
262 } else {
263 this->sylvanBdd.PrintText(filePointer);
264 fclose(filePointer);
265 }
266}
267
269 return sylvanBdd;
270}
271
273 return sylvanBdd;
274}
275
276template<typename ValueType>
278 if (std::is_same<ValueType, double>::value) {
279 return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanBdd.toDoubleMtbdd());
280 } else if (std::is_same<ValueType, uint_fast64_t>::value) {
281 return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanBdd.toInt64Mtbdd());
282 }
283#ifdef STORM_HAVE_CARL
284 else if (std::is_same<ValueType, storm::RationalNumber>::value) {
285 return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanBdd.toStormRationalNumberMtbdd());
286 } else if (std::is_same<ValueType, storm::RationalFunction>::value) {
287 return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanBdd.toStormRationalFunctionMtbdd());
288 }
289#endif
290 else {
291 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Illegal ADD type.");
292 }
293}
294
295storm::storage::BitVector InternalBdd<DdType::Sylvan>::toVector(storm::dd::Odd const& rowOdd, std::vector<uint_fast64_t> const& ddVariableIndices) const {
297 this->toVectorRec(bdd_regular(this->getSylvanBdd().GetBDD()), result, rowOdd, bdd_isnegated(this->getSylvanBdd().GetBDD()), 0, ddVariableIndices.size(), 0,
298 ddVariableIndices);
299 return result;
300}
301
302void InternalBdd<DdType::Sylvan>::toVectorRec(BDD dd, storm::storage::BitVector& result, Odd const& rowOdd, bool complement, uint_fast64_t currentRowLevel,
303 uint_fast64_t maxLevel, uint_fast64_t currentRowOffset,
304 std::vector<uint_fast64_t> const& ddRowVariableIndices) const {
305 // If there are no more values to select, we can directly return.
306 if (dd == sylvan_false && !complement) {
307 return;
308 } else if (dd == sylvan_true && complement) {
309 return;
310 }
311
312 // If we are at the maximal level, the value to be set is stored as a constant in the DD.
313 if (currentRowLevel == maxLevel) {
314 result.set(currentRowOffset, true);
315 } else if (bdd_isterminal(dd) || ddRowVariableIndices[currentRowLevel] < sylvan_var(dd)) {
316 toVectorRec(dd, result, rowOdd.getElseSuccessor(), complement, currentRowLevel + 1, maxLevel, currentRowOffset, ddRowVariableIndices);
317 toVectorRec(dd, result, rowOdd.getThenSuccessor(), complement, currentRowLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(),
318 ddRowVariableIndices);
319 } else {
320 // Otherwise, we compute the ODDs for both the then- and else successors.
321 BDD elseDdNode = sylvan_low(dd);
322 BDD thenDdNode = sylvan_high(dd);
323
324 // Determine whether we have to evaluate the successors as if they were complemented.
325 bool elseComplemented = bdd_isnegated(elseDdNode) ^ complement;
326 bool thenComplemented = bdd_isnegated(thenDdNode) ^ complement;
327
328 toVectorRec(bdd_regular(elseDdNode), result, rowOdd.getElseSuccessor(), elseComplemented, currentRowLevel + 1, maxLevel, currentRowOffset,
329 ddRowVariableIndices);
330 toVectorRec(bdd_regular(thenDdNode), result, rowOdd.getThenSuccessor(), thenComplemented, currentRowLevel + 1, maxLevel,
331 currentRowOffset + rowOdd.getElseOffset(), ddRowVariableIndices);
332 }
333}
334
335Odd InternalBdd<DdType::Sylvan>::createOdd(std::vector<uint_fast64_t> const& ddVariableIndices) const {
336 // Prepare a unique table for each level that keeps the constructed ODD nodes unique.
337 std::vector<std::unordered_map<std::pair<BDD, bool>, std::shared_ptr<Odd>, HashFunctor>> uniqueTableForLevels(ddVariableIndices.size() + 1);
338
339 // Now construct the ODD structure from the BDD.
340 std::shared_ptr<Odd> rootOdd = createOddRec(bdd_regular(this->getSylvanBdd().GetBDD()), bdd_isnegated(this->getSylvanBdd().GetBDD()), 0,
341 ddVariableIndices.size(), ddVariableIndices, uniqueTableForLevels);
342
343 // Return a copy of the root node to remove the shared_ptr encapsulation.
344 return Odd(*rootOdd);
345}
346
347std::size_t InternalBdd<DdType::Sylvan>::HashFunctor::operator()(std::pair<BDD, bool> const& key) const {
348 std::size_t result = 0;
349 boost::hash_combine(result, key.first);
350 boost::hash_combine(result, key.second);
351 return result;
352}
353
354std::shared_ptr<Odd> InternalBdd<DdType::Sylvan>::createOddRec(
355 BDD dd, bool complement, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices,
356 std::vector<std::unordered_map<std::pair<BDD, bool>, std::shared_ptr<Odd>, HashFunctor>>& uniqueTableForLevels) {
357 // Check whether the ODD for this node has already been computed (for this level) and if so, return this instead.
358 auto const& iterator = uniqueTableForLevels[currentLevel].find(std::make_pair(dd, complement));
359 if (iterator != uniqueTableForLevels[currentLevel].end()) {
360 return iterator->second;
361 } else {
362 // Otherwise, we need to recursively compute the ODD.
363
364 // If we are already at the maximal level that is to be considered, we can simply create an Odd without
365 // successors.
366 if (currentLevel == maxLevel) {
367 uint_fast64_t elseOffset = 0;
368 uint_fast64_t thenOffset = 0;
369
370 // If the DD is not the zero leaf, then the then-offset is 1.
371 if (dd != mtbdd_false) {
372 thenOffset = 1;
373 }
374
375 // If we need to complement the 'terminal' node, we need to negate its offset.
376 if (complement) {
377 thenOffset = 1 - thenOffset;
378 }
379
380 auto oddNode = std::make_shared<Odd>(nullptr, elseOffset, nullptr, thenOffset);
381 uniqueTableForLevels[currentLevel].emplace(std::make_pair(dd, complement), oddNode);
382 return oddNode;
383 } else if (bdd_isterminal(dd) || ddVariableIndices[currentLevel] < sylvan_var(dd)) {
384 // If we skipped the level in the DD, we compute the ODD just for the else-successor and use the same
385 // node for the then-successor as well.
386 std::shared_ptr<Odd> elseNode = createOddRec(dd, complement, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels);
387 std::shared_ptr<Odd> thenNode = elseNode;
388 uint_fast64_t totalOffset = elseNode->getElseOffset() + elseNode->getThenOffset();
389 auto oddNode = std::make_shared<Odd>(elseNode, totalOffset, thenNode, totalOffset);
390 uniqueTableForLevels[currentLevel].emplace(std::make_pair(dd, complement), oddNode);
391 return oddNode;
392 } else {
393 // Otherwise, we compute the ODDs for both the then- and else successors.
394 BDD thenDdNode = sylvan_high(dd);
395 BDD elseDdNode = sylvan_low(dd);
396
397 // Determine whether we have to evaluate the successors as if they were complemented.
398 bool elseComplemented = bdd_isnegated(elseDdNode) ^ complement;
399 bool thenComplemented = bdd_isnegated(thenDdNode) ^ complement;
400
401 std::shared_ptr<Odd> elseNode =
402 createOddRec(bdd_regular(elseDdNode), elseComplemented, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels);
403 std::shared_ptr<Odd> thenNode =
404 createOddRec(bdd_regular(thenDdNode), thenComplemented, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels);
405
406 auto oddNode = std::make_shared<Odd>(elseNode, elseNode->getElseOffset() + elseNode->getThenOffset(), thenNode,
407 thenNode->getElseOffset() + thenNode->getThenOffset());
408 uniqueTableForLevels[currentLevel].emplace(std::make_pair(dd, complement), oddNode);
409 return oddNode;
410 }
411 }
412}
413
414template<typename ValueType>
415void InternalBdd<DdType::Sylvan>::filterExplicitVector(Odd const& odd, std::vector<uint_fast64_t> const& ddVariableIndices,
416 std::vector<ValueType> const& sourceValues, std::vector<ValueType>& targetValues) const {
417 uint_fast64_t currentIndex = 0;
418 filterExplicitVectorRec(bdd_regular(this->getSylvanBdd().GetBDD()), 0, bdd_isnegated(this->getSylvanBdd().GetBDD()), ddVariableIndices.size(),
419 ddVariableIndices, 0, odd, targetValues, currentIndex, sourceValues);
420}
421
422template<typename ValueType>
423void InternalBdd<DdType::Sylvan>::filterExplicitVectorRec(BDD dd, uint_fast64_t currentLevel, bool complement, uint_fast64_t maxLevel,
424 std::vector<uint_fast64_t> const& ddVariableIndices, uint_fast64_t currentOffset,
425 storm::dd::Odd const& odd, std::vector<ValueType>& result, uint_fast64_t& currentIndex,
426 std::vector<ValueType> const& values) {
427 // If there are no more values to select, we can directly return.
428 if (dd == sylvan_false && !complement) {
429 return;
430 } else if (dd == sylvan_true && complement) {
431 return;
432 }
433
434 if (currentLevel == maxLevel) {
435 result[currentIndex++] = values[currentOffset];
436 } else if (bdd_isterminal(dd) || ddVariableIndices[currentLevel] < sylvan_var(dd)) {
437 // If we skipped a level, we need to enumerate the explicit entries for the case in which the bit is set
438 // and for the one in which it is not set.
439 filterExplicitVectorRec(dd, currentLevel + 1, complement, maxLevel, ddVariableIndices, currentOffset, odd.getElseSuccessor(), result, currentIndex,
440 values);
441 filterExplicitVectorRec(dd, currentLevel + 1, complement, maxLevel, ddVariableIndices, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(),
442 result, currentIndex, values);
443 } else {
444 // Otherwise, we compute the ODDs for both the then- and else successors.
445 BDD thenDdNode = sylvan_high(dd);
446 BDD elseDdNode = sylvan_low(dd);
447
448 // Determine whether we have to evaluate the successors as if they were complemented.
449 bool elseComplemented = bdd_isnegated(elseDdNode) ^ complement;
450 bool thenComplemented = bdd_isnegated(thenDdNode) ^ complement;
451
452 filterExplicitVectorRec(bdd_regular(elseDdNode), currentLevel + 1, elseComplemented, maxLevel, ddVariableIndices, currentOffset, odd.getElseSuccessor(),
453 result, currentIndex, values);
454 filterExplicitVectorRec(bdd_regular(thenDdNode), currentLevel + 1, thenComplemented, maxLevel, ddVariableIndices, currentOffset + odd.getElseOffset(),
455 odd.getThenSuccessor(), result, currentIndex, values);
456 }
457}
458
459void InternalBdd<DdType::Sylvan>::filterExplicitVector(Odd const& odd, std::vector<uint_fast64_t> const& ddVariableIndices,
460 storm::storage::BitVector const& sourceValues, storm::storage::BitVector& targetValues) const {
461 uint_fast64_t currentIndex = 0;
462 filterExplicitVectorRec(bdd_regular(this->getSylvanBdd().GetBDD()), 0, bdd_isnegated(this->getSylvanBdd().GetBDD()), ddVariableIndices.size(),
463 ddVariableIndices, 0, odd, targetValues, currentIndex, sourceValues);
464}
465
466void InternalBdd<DdType::Sylvan>::filterExplicitVectorRec(BDD dd, uint_fast64_t currentLevel, bool complement, uint_fast64_t maxLevel,
467 std::vector<uint_fast64_t> const& ddVariableIndices, uint_fast64_t currentOffset,
468 storm::dd::Odd const& odd, storm::storage::BitVector& result, uint_fast64_t& currentIndex,
469 storm::storage::BitVector const& values) {
470 // If there are no more values to select, we can directly return.
471 if (dd == sylvan_false && !complement) {
472 return;
473 } else if (dd == sylvan_true && complement) {
474 return;
475 }
476
477 if (currentLevel == maxLevel) {
478 result.set(currentIndex++, values.get(currentOffset));
479 } else if (bdd_isterminal(dd) || ddVariableIndices[currentLevel] < sylvan_var(dd)) {
480 // If we skipped a level, we need to enumerate the explicit entries for the case in which the bit is set
481 // and for the one in which it is not set.
482 filterExplicitVectorRec(dd, currentLevel + 1, complement, maxLevel, ddVariableIndices, currentOffset, odd.getElseSuccessor(), result, currentIndex,
483 values);
484 filterExplicitVectorRec(dd, currentLevel + 1, complement, maxLevel, ddVariableIndices, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(),
485 result, currentIndex, values);
486 } else {
487 // Otherwise, we compute the ODDs for both the then- and else successors.
488 BDD thenDdNode = sylvan_high(dd);
489 BDD elseDdNode = sylvan_low(dd);
490
491 // Determine whether we have to evaluate the successors as if they were complemented.
492 bool elseComplemented = bdd_isnegated(elseDdNode) ^ complement;
493 bool thenComplemented = bdd_isnegated(thenDdNode) ^ complement;
494
495 filterExplicitVectorRec(bdd_regular(elseDdNode), currentLevel + 1, elseComplemented, maxLevel, ddVariableIndices, currentOffset, odd.getElseSuccessor(),
496 result, currentIndex, values);
497 filterExplicitVectorRec(bdd_regular(thenDdNode), currentLevel + 1, thenComplemented, maxLevel, ddVariableIndices, currentOffset + odd.getElseOffset(),
498 odd.getThenSuccessor(), result, currentIndex, values);
499 }
500}
501
502std::vector<InternalBdd<DdType::Sylvan>> InternalBdd<DdType::Sylvan>::splitIntoGroups(std::vector<uint_fast64_t> const& ddGroupVariableIndices) const {
503 std::vector<InternalBdd<DdType::Sylvan>> result;
504 splitIntoGroupsRec(this->getSylvanBdd().GetBDD(), result, ddGroupVariableIndices, 0, ddGroupVariableIndices.size());
505 return result;
506}
507
509 std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel,
510 uint_fast64_t maxLevel) const {
511 // For the empty DD, we do not need to create a group.
512 if (dd == sylvan_false) {
513 return;
514 }
515
516 if (currentLevel == maxLevel) {
517 groups.push_back(InternalBdd<DdType::Sylvan>(ddManager, sylvan::Bdd(dd)));
518 } else if (bdd_isterminal(dd) || ddGroupVariableIndices[currentLevel] < sylvan_var(dd)) {
519 splitIntoGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel);
520 splitIntoGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel);
521 } else {
522 // Otherwise, we compute the ODDs for both the then- and else successors.
523 BDD thenDdNode = sylvan_high(dd);
524 BDD elseDdNode = sylvan_low(dd);
525
526 splitIntoGroupsRec(elseDdNode, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel);
527 splitIntoGroupsRec(thenDdNode, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel);
528 }
529}
530
531std::pair<std::vector<storm::expressions::Expression>, std::unordered_map<uint_fast64_t, storm::expressions::Variable>>
533 std::pair<std::vector<storm::expressions::Expression>, std::unordered_map<uint_fast64_t, storm::expressions::Variable>> result;
534
535 // Create (and maintain) a mapping from the DD nodes to a counter that says the how-many-th node (within the
536 // nodes of equal index) the node was.
537 std::unordered_map<BDD, uint_fast64_t> nodeToCounterMap;
538 std::vector<uint_fast64_t> nextCounterForIndex(ddManager->getNumberOfDdVariables(), 0);
539 std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, storm::expressions::Variable> countIndexToVariablePair;
540
541 bool negated = bdd_isnegated(this->getSylvanBdd().GetBDD());
542
543 // Translate from the top node downwards.
544 storm::expressions::Variable topVariable = this->toExpressionRec(bdd_regular(this->getSylvanBdd().GetBDD()), manager, result.first, result.second,
545 countIndexToVariablePair, nodeToCounterMap, nextCounterForIndex);
546
547 // Create the final expression.
548 if (negated) {
549 result.first.push_back(!topVariable);
550 } else {
551 result.first.push_back(topVariable);
552 }
553
554 return result;
555}
556
558 BDD dd, storm::expressions::ExpressionManager& manager, std::vector<storm::expressions::Expression>& expressions,
559 std::unordered_map<uint_fast64_t, storm::expressions::Variable>& indexToVariableMap,
560 std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, storm::expressions::Variable>& countIndexToVariablePair,
561 std::unordered_map<BDD, uint_fast64_t>& nodeToCounterMap, std::vector<uint_fast64_t>& nextCounterForIndex) {
562 STORM_LOG_ASSERT(!bdd_isnegated(dd), "Expected non-negated BDD node.");
563
564 // First, try to look up the current node if it's not a terminal node.
565 auto nodeCounterIt = nodeToCounterMap.find(dd);
566 if (nodeCounterIt != nodeToCounterMap.end()) {
567 // If we have found the node, this means we can look up the counter-index pair and get the corresponding variable.
568 auto variableIt = countIndexToVariablePair.find(std::make_pair(nodeCounterIt->second, sylvan_var(dd)));
569 STORM_LOG_ASSERT(variableIt != countIndexToVariablePair.end(), "Unable to find node.");
570 return variableIt->second;
571 }
572
573 // If the node was not yet encountered, we create a variable and associate it with the appropriate expression.
574 storm::expressions::Variable newNodeVariable = manager.declareFreshBooleanVariable();
575
576 // Since we want to reuse the variable whenever possible, we insert the appropriate entries in the hash table.
577 if (!bdd_isterminal(dd)) {
578 // If we are dealing with a non-terminal node, we count it as a new node with this index.
579 nodeToCounterMap[dd] = nextCounterForIndex[sylvan_var(dd)];
580 countIndexToVariablePair[std::make_pair(nextCounterForIndex[sylvan_var(dd)], sylvan_var(dd))] = newNodeVariable;
581 ++nextCounterForIndex[sylvan_var(dd)];
582 } else {
583 // If it's a terminal node, it is the one leaf and there's no need to keep track of a counter for this level.
584 nodeToCounterMap[dd] = 0;
585 countIndexToVariablePair[std::make_pair(0, sylvan_var(dd))] = newNodeVariable;
586 }
587
588 // In the terminal case, we can only have a one since we are considering non-negated nodes only.
589 if (bdd_isterminal(dd)) {
590 if (dd == sylvan_true) {
591 expressions.push_back(storm::expressions::iff(manager.boolean(true), newNodeVariable));
592 } else {
593 expressions.push_back(storm::expressions::iff(manager.boolean(false), newNodeVariable));
594 }
595 } else {
596 // In the non-terminal case, we recursively translate the children nodes and then construct and appropriate ite-expression.
597 BDD t = sylvan_high(dd);
598 BDD e = sylvan_low(dd);
599 BDD T = bdd_regular(t);
600 BDD E = bdd_regular(e);
601 storm::expressions::Variable thenVariable =
602 toExpressionRec(T, manager, expressions, indexToVariableMap, countIndexToVariablePair, nodeToCounterMap, nextCounterForIndex);
603 storm::expressions::Variable elseVariable =
604 toExpressionRec(E, manager, expressions, indexToVariableMap, countIndexToVariablePair, nodeToCounterMap, nextCounterForIndex);
605
606 // Create the appropriate expression.
607 // Create the appropriate expression.
608 auto indexVariable = indexToVariableMap.find(sylvan_var(dd));
609 storm::expressions::Variable levelVariable;
610 if (indexVariable == indexToVariableMap.end()) {
611 levelVariable = manager.declareFreshBooleanVariable();
612 indexToVariableMap[sylvan_var(dd)] = levelVariable;
613 } else {
614 levelVariable = indexVariable->second;
615 }
616 expressions.push_back(storm::expressions::iff(
617 newNodeVariable, storm::expressions::ite(levelVariable, t == T ? thenVariable : !thenVariable, e == E ? elseVariable : !elseVariable)));
618 }
619
620 // Return the variable for this node.
621 return newNodeVariable;
622}
623
624template InternalAdd<DdType::Sylvan, double> InternalBdd<DdType::Sylvan>::toAdd() const;
625template InternalAdd<DdType::Sylvan, uint_fast64_t> InternalBdd<DdType::Sylvan>::toAdd() const;
626template InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalBdd<DdType::Sylvan>::toAdd() const;
627template InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalBdd<DdType::Sylvan>::toAdd() const;
628
629template void InternalBdd<DdType::Sylvan>::filterExplicitVector(Odd const& odd, std::vector<uint_fast64_t> const& ddVariableIndices,
630 std::vector<double> const& sourceValues, std::vector<double>& targetValues) const;
631template void InternalBdd<DdType::Sylvan>::filterExplicitVector(Odd const& odd, std::vector<uint_fast64_t> const& ddVariableIndices,
632 std::vector<uint_fast64_t> const& sourceValues, std::vector<uint_fast64_t>& targetValues) const;
633template void InternalBdd<DdType::Sylvan>::filterExplicitVector(Odd const& odd, std::vector<uint_fast64_t> const& ddVariableIndices,
634 std::vector<storm::RationalNumber> const& sourceValues,
635 std::vector<storm::RationalNumber>& targetValues) const;
636template void InternalBdd<DdType::Sylvan>::filterExplicitVector(Odd const& odd, std::vector<uint_fast64_t> const& ddVariableIndices,
637 std::vector<storm::RationalFunction> const& sourceValues,
638 std::vector<storm::RationalFunction>& targetValues) const;
639
640template InternalAdd<DdType::Sylvan, double> InternalBdd<DdType::Sylvan>::ite(InternalAdd<DdType::Sylvan, double> const& thenAdd,
641 InternalAdd<DdType::Sylvan, double> const& elseAdd) const;
642template InternalAdd<DdType::Sylvan, uint_fast64_t> InternalBdd<DdType::Sylvan>::ite(InternalAdd<DdType::Sylvan, uint_fast64_t> const& thenAdd,
643 InternalAdd<DdType::Sylvan, uint_fast64_t> const& elseAdd) const;
644template InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalBdd<DdType::Sylvan>::ite(
645 InternalAdd<DdType::Sylvan, storm::RationalNumber> const& thenAdd, InternalAdd<DdType::Sylvan, storm::RationalNumber> const& elseAdd) const;
646template InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalBdd<DdType::Sylvan>::ite(
647 InternalAdd<DdType::Sylvan, storm::RationalFunction> const& thenAdd, InternalAdd<DdType::Sylvan, storm::RationalFunction> const& elseAdd) const;
648} // namespace dd
649} // namespace storm
Odd const & getThenSuccessor() const
Retrieves the then-successor of this ODD node.
Definition Odd.cpp:23
uint_fast64_t getTotalOffset() const
Retrieves the total offset, i.e., the sum of the then- and else-offset.
Definition Odd.cpp:47
uint_fast64_t getElseOffset() const
Retrieves the else-offset of this ODD node.
Definition Odd.cpp:31
Odd const & getElseSuccessor() const
Retrieves the else-successor of this ODD node.
Definition Odd.cpp:27
uint_fast64_t getThenOffset() const
Retrieves the then-offset of this ODD node.
Definition Odd.cpp:39
This class is responsible for managing a set of typed variables and all expressions using these varia...
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
#define STORM_LOG_ERROR(message)
Definition logging.h:31
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
Expression ite(Expression const &condition, Expression const &thenExpression, Expression const &elseExpression)
Expression iff(Expression const &first, Expression const &second)
SettingsManager const & manager()
Retrieves the settings manager.
LabParser.cpp.
Definition cli.cpp:18