#include "src/storage/dd/cudd/InternalCuddAdd.h" #include "src/storage/dd/cudd/InternalCuddDdManager.h" #include "src/storage/dd/cudd/InternalCuddBdd.h" #include "src/storage/dd/cudd/CuddAddIterator.h" #include "src/storage/dd/Odd.h" #include "src/storage/SparseMatrix.h" #include "src/utility/constants.h" #include "src/utility/macros.h" namespace storm { namespace dd { template InternalAdd::InternalAdd(InternalDdManager const* ddManager, cudd::ADD cuddAdd) : ddManager(ddManager), cuddAdd(cuddAdd) { // Intentionally left empty. } template bool InternalAdd::operator==(InternalAdd const& other) const { return this->getCuddAdd() == other.getCuddAdd(); } template bool InternalAdd::operator!=(InternalAdd const& other) const { return !(*this == other); } template InternalAdd InternalAdd::ite(InternalAdd const& thenDd, InternalAdd const& elseDd) const { return InternalAdd(ddManager, this->getCuddAdd().Ite(thenDd.getCuddAdd(), elseDd.getCuddAdd())); } template InternalAdd InternalAdd::operator+(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd() + other.getCuddAdd()); } template InternalAdd& InternalAdd::operator+=(InternalAdd const& other) { this->cuddAdd = this->getCuddAdd() + other.getCuddAdd(); return *this; } template InternalAdd InternalAdd::operator*(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd() * other.getCuddAdd()); } template InternalAdd& InternalAdd::operator*=(InternalAdd const& other) { this->cuddAdd = this->getCuddAdd() * other.getCuddAdd(); return *this; } template InternalAdd InternalAdd::operator-(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd() - other.getCuddAdd()); } template InternalAdd& InternalAdd::operator-=(InternalAdd const& other) { this->cuddAdd = this->getCuddAdd() - other.getCuddAdd(); return *this; } template InternalAdd InternalAdd::operator/(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd().Divide(other.getCuddAdd())); } template InternalAdd& InternalAdd::operator/=(InternalAdd const& other) { this->cuddAdd = this->getCuddAdd().Divide(other.getCuddAdd()); return *this; } template InternalBdd InternalAdd::equals(InternalAdd const& other) const { return InternalBdd(ddManager, this->getCuddAdd().Equals(other.getCuddAdd()).BddPattern()); } template InternalBdd InternalAdd::notEquals(InternalAdd const& other) const { return InternalBdd(ddManager, this->getCuddAdd().NotEquals(other.getCuddAdd()).BddPattern()); } template InternalBdd InternalAdd::less(InternalAdd const& other) const { return InternalBdd(ddManager, this->getCuddAdd().LessThan(other.getCuddAdd()).BddPattern()); } template InternalBdd InternalAdd::lessOrEqual(InternalAdd const& other) const { return InternalBdd(ddManager, this->getCuddAdd().LessThanOrEqual(other.getCuddAdd()).BddPattern()); } template InternalBdd InternalAdd::greater(InternalAdd const& other) const { return InternalBdd(ddManager, this->getCuddAdd().GreaterThan(other.getCuddAdd()).BddPattern()); } template InternalBdd InternalAdd::greaterOrEqual(InternalAdd const& other) const { return InternalBdd(ddManager, this->getCuddAdd().GreaterThanOrEqual(other.getCuddAdd()).BddPattern()); } template InternalAdd InternalAdd::pow(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd().Pow(other.getCuddAdd())); } template InternalAdd InternalAdd::mod(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd().Mod(other.getCuddAdd())); } template InternalAdd InternalAdd::logxy(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd().LogXY(other.getCuddAdd())); } template InternalAdd InternalAdd::floor() const { return InternalAdd(ddManager, this->getCuddAdd().Floor()); } template InternalAdd InternalAdd::ceil() const { return InternalAdd(ddManager, this->getCuddAdd().Ceil()); } template InternalAdd InternalAdd::minimum(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd().Minimum(other.getCuddAdd())); } template InternalAdd InternalAdd::maximum(InternalAdd const& other) const { return InternalAdd(ddManager, this->getCuddAdd().Maximum(other.getCuddAdd())); } template InternalAdd InternalAdd::sumAbstract(InternalBdd const& cube) const { return InternalAdd(ddManager, this->getCuddAdd().ExistAbstract(cube.toAdd().getCuddAdd())); } template InternalAdd InternalAdd::minAbstract(InternalBdd const& cube) const { return InternalAdd(ddManager, this->getCuddAdd().MinAbstract(cube.toAdd().getCuddAdd())); } template InternalAdd InternalAdd::maxAbstract(InternalBdd const& cube) const { return InternalAdd(ddManager, this->getCuddAdd().MaxAbstract(cube.toAdd().getCuddAdd())); } template bool InternalAdd::equalModuloPrecision(InternalAdd const& other, double precision, bool relative) const { if (relative) { return this->getCuddAdd().EqualSupNormRel(other.getCuddAdd(), precision); } else { return this->getCuddAdd().EqualSupNorm(other.getCuddAdd(), precision); } } template InternalAdd InternalAdd::swapVariables(std::vector> const& from, std::vector> const& to) const { std::vector fromAdd; std::vector toAdd; STORM_LOG_ASSERT(fromAdd.size() == toAdd.size(), "Sizes of vectors do not match."); for (auto it1 = from.begin(), ite1 = from.end(), it2 = to.begin(); it1 != ite1; ++it1, ++it2) { fromAdd.push_back(it1->getCuddBdd().Add()); toAdd.push_back(it2->getCuddBdd().Add()); } return InternalAdd(ddManager, this->getCuddAdd().SwapVariables(fromAdd, toAdd)); } template InternalAdd InternalAdd::multiplyMatrix(InternalAdd const& otherMatrix, std::vector> const& summationDdVariables) const { // Create the CUDD summation variables. std::vector summationAdds; for (auto const& ddVariable : summationDdVariables) { summationAdds.push_back(ddVariable.toAdd().getCuddAdd()); } return InternalAdd(ddManager, this->getCuddAdd().MatrixMultiply(otherMatrix.getCuddAdd(), summationAdds)); } template InternalBdd InternalAdd::greater(ValueType const& value) const { return InternalBdd(ddManager, this->getCuddAdd().BddStrictThreshold(value)); } template InternalBdd InternalAdd::greaterOrEqual(ValueType const& value) const { return InternalBdd(ddManager, this->getCuddAdd().BddThreshold(value)); } template InternalBdd InternalAdd::less(ValueType const& value) const { return InternalBdd(ddManager, ~this->getCuddAdd().BddThreshold(value)); } template InternalBdd InternalAdd::lessOrEqual(ValueType const& value) const { return InternalBdd(ddManager, ~this->getCuddAdd().BddStrictThreshold(value)); } template InternalBdd InternalAdd::notZero() const { return InternalBdd(ddManager, this->getCuddAdd().BddPattern()); } template InternalAdd InternalAdd::constrain(InternalAdd const& constraint) const { return InternalAdd(ddManager, this->getCuddAdd().Constrain(constraint.getCuddAdd())); } template InternalAdd InternalAdd::restrict(InternalAdd const& constraint) const { return InternalAdd(ddManager, this->getCuddAdd().Restrict(constraint.getCuddAdd())); } template InternalBdd InternalAdd::getSupport() const { return InternalBdd(ddManager, this->getCuddAdd().Support()); } template uint_fast64_t InternalAdd::getNonZeroCount(uint_fast64_t numberOfDdVariables) const { // If the number of DD variables is zero, CUDD returns a number greater 0 for constant nodes different from // zero, which is not the behaviour we expect. if (numberOfDdVariables == 0) { return 0; } return static_cast(this->getCuddAdd().CountMinterm(static_cast(numberOfDdVariables))); } template uint_fast64_t InternalAdd::getLeafCount() const { return static_cast(this->getCuddAdd().CountLeaves()); } template uint_fast64_t InternalAdd::getNodeCount() const { return static_cast(this->getCuddAdd().nodeCount()); } template ValueType InternalAdd::getMin() const { cudd::ADD constantMinAdd = this->getCuddAdd().FindMin(); return static_cast(Cudd_V(constantMinAdd.getNode())); } template ValueType InternalAdd::getMax() const { cudd::ADD constantMaxAdd = this->getCuddAdd().FindMax(); return static_cast(Cudd_V(constantMaxAdd.getNode())); } template ValueType InternalAdd::getValue() const { return static_cast(Cudd_V(this->getCuddAdd().getNode())); } template bool InternalAdd::isOne() const { return this->getCuddAdd().IsOne(); } template bool InternalAdd::isZero() const { return this->getCuddAdd().IsZero(); } template bool InternalAdd::isConstant() const { return Cudd_IsConstant(this->getCuddAdd().getNode()); } template uint_fast64_t InternalAdd::getIndex() const { return static_cast(this->getCuddAdd().NodeReadIndex()); } template void InternalAdd::exportToDot(std::string const& filename, std::vector const& ddVariableNamesAsStrings) const { // Build the name input of the DD. std::vector ddNames; std::string ddName("f"); ddNames.push_back(new char[ddName.size() + 1]); std::copy(ddName.c_str(), ddName.c_str() + 2, ddNames.back()); // Now build the variables names. std::vector ddVariableNames; for (auto const& element : ddVariableNamesAsStrings) { ddVariableNames.push_back(new char[element.size() + 1]); std::copy(element.c_str(), element.c_str() + element.size() + 1, ddVariableNames.back()); } // Open the file, dump the DD and close it again. FILE* filePointer = fopen(filename.c_str() , "w"); std::vector cuddAddVector = { this->getCuddAdd() }; ddManager->getCuddManager().DumpDot(cuddAddVector, &ddVariableNames[0], &ddNames[0], filePointer); fclose(filePointer); // Finally, delete the names. for (char* element : ddNames) { delete[] element; } for (char* element : ddVariableNames) { delete[] element; } } template AddIterator InternalAdd::begin(DdManager const& fullDdManager, std::set const& metaVariables, bool enumerateDontCareMetaVariables) const { int* cube; double value; DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value); return AddIterator(fullDdManager, generator, cube, value, (Cudd_IsGenEmpty(generator) != 0), &metaVariables, enumerateDontCareMetaVariables); } template AddIterator InternalAdd::end(DdManager const& fullDdManager, bool enumerateDontCareMetaVariables) const { return AddIterator(fullDdManager, nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); } template cudd::ADD InternalAdd::getCuddAdd() const { return this->cuddAdd; } template DdNode* InternalAdd::getCuddDdNode() const { return this->getCuddAdd().getNode(); } template Odd InternalAdd::createOdd(std::vector const& ddVariableIndices) const { // Prepare a unique table for each level that keeps the constructed ODD nodes unique. std::vector>> uniqueTableForLevels(ddVariableIndices.size() + 1); // Now construct the ODD structure from the ADD. std::shared_ptr rootOdd = createOddRec(this->getCuddDdNode(), ddManager->getCuddManager(), 0, ddVariableIndices.size(), ddVariableIndices, uniqueTableForLevels); // Return a copy of the root node to remove the shared_ptr encapsulation. return Odd(*rootOdd); } template std::shared_ptr InternalAdd::createOddRec(DdNode* dd, cudd::Cudd const& manager, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector const& ddVariableIndices, std::vector>>& uniqueTableForLevels) { // Check whether the ODD for this node has already been computed (for this level) and if so, return this instead. auto const& iterator = uniqueTableForLevels[currentLevel].find(dd); if (iterator != uniqueTableForLevels[currentLevel].end()) { return iterator->second; } else { // Otherwise, we need to recursively compute the ODD. // If we are already past the maximal level that is to be considered, we can simply create an Odd without // successors if (currentLevel == maxLevel) { uint_fast64_t elseOffset = 0; uint_fast64_t thenOffset = 0; // If the DD is not the zero leaf, then the then-offset is 1. if (dd != Cudd_ReadZero(manager.getManager())) { thenOffset = 1; } return std::make_shared(nullptr, elseOffset, nullptr, thenOffset); } else if (ddVariableIndices[currentLevel] < static_cast(dd->index)) { // If we skipped the level in the DD, we compute the ODD just for the else-successor and use the same // node for the then-successor as well. std::shared_ptr elseNode = createOddRec(dd, manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); std::shared_ptr thenNode = elseNode; return std::make_shared(elseNode, elseNode->getElseOffset() + elseNode->getThenOffset(), thenNode, thenNode->getElseOffset() + thenNode->getThenOffset()); } else { // Otherwise, we compute the ODDs for both the then- and else successors. std::shared_ptr elseNode = createOddRec(Cudd_E(dd), manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); std::shared_ptr thenNode = createOddRec(Cudd_T(dd), manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); uint_fast64_t totalElseOffset = elseNode->getElseOffset() + elseNode->getThenOffset(); uint_fast64_t totalThenOffset = thenNode->getElseOffset() + thenNode->getThenOffset(); return std::make_shared(elseNode, totalElseOffset, thenNode, totalThenOffset); } } } template void InternalAdd::composeWithExplicitVector(storm::dd::Odd const& odd, std::vector const& ddVariableIndices, std::vector& targetVector, std::function const& function) const { composeWithExplicitVectorRec(this->getCuddDdNode(), nullptr, 0, ddVariableIndices.size(), 0, odd, ddVariableIndices, targetVector, function); } template void InternalAdd::composeWithExplicitVector(storm::dd::Odd const& odd, std::vector const& ddVariableIndices, std::vector const& offsets, std::vector& targetVector, std::function const& function) const { composeWithExplicitVectorRec(this->getCuddDdNode(), &offsets, 0, ddVariableIndices.size(), 0, odd, ddVariableIndices, targetVector, function); } template void InternalAdd::composeWithExplicitVectorRec(DdNode const* dd, std::vector const* offsets, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd const& odd, std::vector const& ddVariableIndices, std::vector& targetVector, std::function const& function) const { // For the empty DD, we do not need to add any entries. if (dd == Cudd_ReadZero(ddManager->getCuddManager().getManager())) { return; } // If we are at the maximal level, the value to be set is stored as a constant in the DD. if (currentLevel == maxLevel) { ValueType& targetValue = targetVector[offsets != nullptr ? (*offsets)[currentOffset] : currentOffset]; targetValue = function(targetValue, Cudd_V(dd)); } else if (ddVariableIndices[currentLevel] < dd->index) { // If we skipped a level, we need to enumerate the explicit entries for the case in which the bit is set // and for the one in which it is not set. composeWithExplicitVectorRec(dd, offsets, currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector, function); composeWithExplicitVectorRec(dd, offsets, currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector, function); } else { // Otherwise, we simply recursively call the function for both (different) cases. composeWithExplicitVectorRec(Cudd_E(dd), offsets, currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector, function); composeWithExplicitVectorRec(Cudd_T(dd), offsets, currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector, function); } } template std::vector> InternalAdd::splitIntoGroups(std::vector const& ddGroupVariableIndices) const { std::vector> result; splitIntoGroupsRec(this->getCuddDdNode(), result, ddGroupVariableIndices, 0, ddGroupVariableIndices.size()); return result; } template void InternalAdd::splitIntoGroupsRec(DdNode* dd, std::vector>& groups, std::vector const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel) const { // For the empty DD, we do not need to create a group. if (dd == Cudd_ReadZero(ddManager->getCuddManager().getManager())) { return; } if (currentLevel == maxLevel) { groups.push_back(InternalAdd(ddManager, cudd::ADD(ddManager->getCuddManager(), dd))); } else if (ddGroupVariableIndices[currentLevel] < dd->index) { splitIntoGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); splitIntoGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); } else { splitIntoGroupsRec(Cudd_E(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); splitIntoGroupsRec(Cudd_T(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); } } template std::vector, InternalAdd>> InternalAdd::splitIntoGroups(InternalAdd vector, std::vector const& ddGroupVariableIndices) const { std::vector, InternalAdd>> result; splitIntoGroupsRec(this->getCuddDdNode(), vector.getCuddDdNode(), result, ddGroupVariableIndices, 0, ddGroupVariableIndices.size()); return result; } template void InternalAdd::splitIntoGroupsRec(DdNode* dd1, DdNode* dd2, std::vector, InternalAdd>>& groups, std::vector const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel) const { // For the empty DD, we do not need to create a group. if (dd1 == Cudd_ReadZero(ddManager->getCuddManager().getManager()) && dd2 == Cudd_ReadZero(ddManager->getCuddManager().getManager())) { return; } if (currentLevel == maxLevel) { groups.push_back(std::make_pair(InternalAdd(ddManager, cudd::ADD(ddManager->getCuddManager(), dd1)), InternalAdd(ddManager, cudd::ADD(ddManager->getCuddManager(), dd2)))); } else if (ddGroupVariableIndices[currentLevel] < dd1->index) { if (ddGroupVariableIndices[currentLevel] < dd2->index) { splitIntoGroupsRec(dd1, dd2, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); splitIntoGroupsRec(dd1, dd2, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); } else { splitIntoGroupsRec(dd1, Cudd_T(dd2), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); splitIntoGroupsRec(dd1, Cudd_E(dd2), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); } } else if (ddGroupVariableIndices[currentLevel] < dd2->index) { splitIntoGroupsRec(Cudd_T(dd1), dd2, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); splitIntoGroupsRec(Cudd_E(dd1), dd2, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); } else { splitIntoGroupsRec(Cudd_T(dd1), Cudd_T(dd2), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); splitIntoGroupsRec(Cudd_E(dd1), Cudd_E(dd2), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel); } } template void InternalAdd::toMatrixComponents(std::vector const& rowGroupIndices, std::vector& rowIndications, std::vector>& columnsAndValues, Odd const& rowOdd, Odd const& columnOdd, std::vector const& ddRowVariableIndices, std::vector const& ddColumnVariableIndices, bool writeValues) const { return toMatrixComponentsRec(this->getCuddDdNode(), rowGroupIndices, rowIndications, columnsAndValues, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, writeValues); } template void InternalAdd::toMatrixComponentsRec(DdNode const* dd, std::vector const& rowGroupOffsets, std::vector& rowIndications, std::vector>& columnsAndValues, Odd const& rowOdd, Odd const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector const& ddRowVariableIndices, std::vector const& ddColumnVariableIndices, bool generateValues) const { // For the empty DD, we do not need to add any entries. if (dd == Cudd_ReadZero(ddManager->getCuddManager().getManager())) { return; } // If we are at the maximal level, the value to be set is stored as a constant in the DD. if (currentRowLevel + currentColumnLevel == maxLevel) { if (generateValues) { columnsAndValues[rowIndications[rowGroupOffsets[currentRowOffset]]] = storm::storage::MatrixEntry(currentColumnOffset, Cudd_V(dd)); } ++rowIndications[rowGroupOffsets[currentRowOffset]]; } else { DdNode const* elseElse; DdNode const* elseThen; DdNode const* thenElse; DdNode const* thenThen; if (ddColumnVariableIndices[currentColumnLevel] < dd->index) { elseElse = elseThen = thenElse = thenThen = dd; } else if (ddRowVariableIndices[currentColumnLevel] < dd->index) { elseElse = thenElse = Cudd_E(dd); elseThen = thenThen = Cudd_T(dd); } else { DdNode const* elseNode = Cudd_E(dd); if (ddColumnVariableIndices[currentColumnLevel] < elseNode->index) { elseElse = elseThen = elseNode; } else { elseElse = Cudd_E(elseNode); elseThen = Cudd_T(elseNode); } DdNode const* thenNode = Cudd_T(dd); if (ddColumnVariableIndices[currentColumnLevel] < thenNode->index) { thenElse = thenThen = thenNode; } else { thenElse = Cudd_E(thenNode); thenThen = Cudd_T(thenNode); } } // Visit else-else. toMatrixComponentsRec(elseElse, rowGroupOffsets, rowIndications, columnsAndValues, rowOdd.getElseSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); // Visit else-then. toMatrixComponentsRec(elseThen, rowGroupOffsets, rowIndications, columnsAndValues, rowOdd.getElseSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); // Visit then-else. toMatrixComponentsRec(thenElse, rowGroupOffsets, rowIndications, columnsAndValues, rowOdd.getThenSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); // Visit then-then. toMatrixComponentsRec(thenThen, rowGroupOffsets, rowIndications, columnsAndValues, rowOdd.getThenSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); } } template InternalAdd InternalAdd::fromVector(InternalDdManager const* ddManager, std::vector const& values, storm::dd::Odd const& odd, std::vector const& ddVariableIndices) { uint_fast64_t offset = 0; return InternalAdd(ddManager, cudd::ADD(ddManager->getCuddManager(), fromVectorRec(ddManager->getCuddManager().getManager(), offset, 0, ddVariableIndices.size(), values, odd, ddVariableIndices))); } template DdNode* InternalAdd::fromVectorRec(::DdManager* manager, uint_fast64_t& currentOffset, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector const& values, Odd const& odd, std::vector const& ddVariableIndices) { if (currentLevel == maxLevel) { // If we are in a terminal node of the ODD, we need to check whether the then-offset of the ODD is one // (meaning the encoding is a valid one) or zero (meaning the encoding is not valid). Consequently, we // need to copy the next value of the vector iff the then-offset is greater than zero. if (odd.getThenOffset() > 0) { return Cudd_addConst(manager, values[currentOffset++]); } else { return Cudd_ReadZero(manager); } } else { // If the total offset is zero, we can just return the constant zero DD. if (odd.getThenOffset() + odd.getElseOffset() == 0) { return Cudd_ReadZero(manager); } // Determine the new else-successor. DdNode* elseSuccessor = nullptr; if (odd.getElseOffset() > 0) { elseSuccessor = fromVectorRec(manager, currentOffset, currentLevel + 1, maxLevel, values, odd.getElseSuccessor(), ddVariableIndices); } else { elseSuccessor = Cudd_ReadZero(manager); } Cudd_Ref(elseSuccessor); // Determine the new then-successor. DdNode* thenSuccessor = nullptr; if (odd.getThenOffset() > 0) { thenSuccessor = fromVectorRec(manager, currentOffset, currentLevel + 1, maxLevel, values, odd.getThenSuccessor(), ddVariableIndices); } else { thenSuccessor = Cudd_ReadZero(manager); } Cudd_Ref(thenSuccessor); // Create a node representing ITE(currentVar, thenSuccessor, elseSuccessor); DdNode* result = Cudd_addIthVar(manager, static_cast(ddVariableIndices[currentLevel])); Cudd_Ref(result); DdNode* newResult = Cudd_addIte(manager, result, thenSuccessor, elseSuccessor); Cudd_Ref(newResult); // Dispose of the intermediate results Cudd_RecursiveDeref(manager, result); Cudd_RecursiveDeref(manager, thenSuccessor); Cudd_RecursiveDeref(manager, elseSuccessor); // Before returning, we remove the protection imposed by the previous call to Cudd_Ref. Cudd_Deref(newResult); return newResult; } } template class InternalAdd; template class InternalAdd; } }