#include #include #include #include "src/storage/dd/CuddAdd.h" #include "src/storage/dd/CuddBdd.h" #include "src/storage/dd/CuddOdd.h" #include "src/storage/dd/CuddDdManager.h" #include "src/storage/BitVector.h" #include "src/logic/ComparisonType.h" #include "src/utility/macros.h" #include "src/exceptions/InvalidArgumentException.h" namespace storm { namespace dd { Bdd::Bdd(std::weak_ptr const> ddManager, BDD cuddBdd, std::set const& containedMetaVariables) : Dd(ddManager, containedMetaVariables), cuddBdd(cuddBdd) { // Intentionally left empty. } Bdd::Bdd(std::weak_ptr const> ddManager, std::vector const& explicitValues, storm::dd::Odd const& odd, std::set const& metaVariables, storm::logic::ComparisonType comparisonType, double value) : Dd(ddManager, metaVariables) { switch (comparisonType) { case storm::logic::ComparisonType::Less: this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::greater(), value, std::placeholders::_1)); break; case storm::logic::ComparisonType::LessEqual: this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::greater_equal(), value, std::placeholders::_1)); break; case storm::logic::ComparisonType::Greater: this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::less(), value, std::placeholders::_1)); break; case storm::logic::ComparisonType::GreaterEqual: this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::less_equal(), value, std::placeholders::_1)); break; } } Add Bdd::toAdd() const { return Add(this->getDdManager(), this->getCuddBdd().Add(), this->getContainedMetaVariables()); } BDD Bdd::getCuddBdd() const { return this->cuddBdd; } DdNode* Bdd::getCuddDdNode() const { return this->getCuddBdd().getNode(); } bool Bdd::operator==(Bdd const& other) const { return this->getCuddBdd() == other.getCuddBdd(); } bool Bdd::operator!=(Bdd const& other) const { return !(*this == other); } Bdd Bdd::ite(Bdd const& thenDd, Bdd const& elseDd) const { std::set metaVariableNames; std::set_union(thenDd.getContainedMetaVariables().begin(), thenDd.getContainedMetaVariables().end(), elseDd.getContainedMetaVariables().begin(), elseDd.getContainedMetaVariables().end(), std::inserter(metaVariableNames, metaVariableNames.begin())); metaVariableNames.insert(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end()); return Bdd(this->getDdManager(), this->getCuddBdd().Ite(thenDd.getCuddBdd(), elseDd.getCuddBdd()), metaVariableNames); } Bdd Bdd::operator!() const { Bdd result(*this); result.complement(); return result; } Bdd& Bdd::complement() { this->cuddBdd = ~this->getCuddBdd(); return *this; } Bdd Bdd::operator&&(Bdd const& other) const { Bdd result(*this); result &= other; return result; } Bdd& Bdd::operator&=(Bdd const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddBdd = this->getCuddBdd() & other.getCuddBdd(); return *this; } Bdd Bdd::operator||(Bdd const& other) const { Bdd result(*this); result |= other; return result; } Bdd& Bdd::operator|=(Bdd const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddBdd = this->getCuddBdd() | other.getCuddBdd(); return *this; } Bdd Bdd::iff(Bdd const& other) const { std::set metaVariables(this->getContainedMetaVariables()); metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); return Bdd(this->getDdManager(), this->getCuddBdd().Xnor(other.getCuddBdd()), metaVariables); } Bdd Bdd::exclusiveOr(Bdd const& other) const { std::set metaVariables(this->getContainedMetaVariables()); metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); return Bdd(this->getDdManager(), this->getCuddBdd().Xor(other.getCuddBdd()), metaVariables); } Bdd Bdd::implies(Bdd const& other) const { std::set metaVariables(this->getContainedMetaVariables()); metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); return Bdd(this->getDdManager(), this->getCuddBdd().Ite(other.getCuddBdd(), this->getDdManager()->getBddOne().getCuddBdd()), metaVariables); } Bdd Bdd::existsAbstract(std::set const& metaVariables) const { Bdd cubeBdd = this->getDdManager()->getBddOne(); std::set newMetaVariables = this->getContainedMetaVariables(); for (auto const& metaVariable : metaVariables) { // First check whether the BDD contains the meta variable and erase it, if this is the case. STORM_LOG_THROW(this->containsMetaVariable(metaVariable), storm::exceptions::InvalidArgumentException, "Cannot abstract from meta variable '" << metaVariable.getName() << "' that is not present in the DD."); newMetaVariables.erase(metaVariable); DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeBdd &= ddMetaVariable.getCube(); } return Bdd(this->getDdManager(), this->getCuddBdd().ExistAbstract(cubeBdd.getCuddBdd()), newMetaVariables); } Bdd Bdd::existsAbstractRepresentative(std::set const& metaVariables) const { Bdd cubeBdd = this->getDdManager()->getBddOne(); std::set newMetaVariables = this->getContainedMetaVariables(); for (auto const& metaVariable : metaVariables) { // First check whether the BDD contains the meta variable and erase it, if this is the case. STORM_LOG_THROW(this->containsMetaVariable(metaVariable), storm::exceptions::InvalidArgumentException, "Cannot abstract from meta variable '" << metaVariable.getName() << "' that is not present in the DD."); newMetaVariables.erase(metaVariable); DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeBdd &= ddMetaVariable.getCube(); } return Bdd(this->getDdManager(), this->getCuddBdd().ExistAbstractRepresentative(cubeBdd.getCuddBdd()), newMetaVariables); } Bdd Bdd::universalAbstract(std::set const& metaVariables) const { Bdd cubeBdd = this->getDdManager()->getBddOne(); std::set newMetaVariables = this->getContainedMetaVariables(); for (auto const& metaVariable : metaVariables) { // First check whether the BDD contains the meta variable and erase it, if this is the case. STORM_LOG_THROW(this->containsMetaVariable(metaVariable), storm::exceptions::InvalidArgumentException, "Cannot abstract from meta variable '" << metaVariable.getName() << "' that is not present in the DD."); newMetaVariables.erase(metaVariable); DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeBdd &= ddMetaVariable.getCube(); } return Bdd(this->getDdManager(), this->getCuddBdd().UnivAbstract(cubeBdd.getCuddBdd()), newMetaVariables); } Bdd Bdd::swapVariables(std::vector> const& metaVariablePairs) const { std::set newContainedMetaVariables; std::vector from; std::vector to; for (auto const& metaVariablePair : metaVariablePairs) { DdMetaVariable const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); DdMetaVariable const& variable2 = this->getDdManager()->getMetaVariable(metaVariablePair.second); // Check if it's legal so swap the meta variables. STORM_LOG_THROW(variable1.getNumberOfDdVariables() == variable2.getNumberOfDdVariables(), storm::exceptions::InvalidArgumentException, "Unable to swap meta variables with different size."); // Keep track of the contained meta variables in the DD. if (this->containsMetaVariable(metaVariablePair.first)) { newContainedMetaVariables.insert(metaVariablePair.second); } if (this->containsMetaVariable(metaVariablePair.second)) { newContainedMetaVariables.insert(metaVariablePair.first); } // Add the variables to swap to the corresponding vectors. for (auto const& ddVariable : variable1.getDdVariables()) { from.push_back(ddVariable.getCuddBdd()); } for (auto const& ddVariable : variable2.getDdVariables()) { to.push_back(ddVariable.getCuddBdd()); } } // Finally, call CUDD to swap the variables. return Bdd(this->getDdManager(), this->getCuddBdd().SwapVariables(from, to), newContainedMetaVariables); } Bdd Bdd::andExists(Bdd const& other, std::set const& existentialVariables) const { Bdd cubeBdd(this->getDdManager()->getBddOne()); for (auto const& metaVariable : existentialVariables) { DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeBdd &= ddMetaVariable.getCube(); } std::set unionOfMetaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); std::set containedMetaVariables; std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), existentialVariables.begin(), existentialVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); return Bdd(this->getDdManager(), this->getCuddBdd().AndAbstract(other.getCuddBdd(), cubeBdd.getCuddBdd()), containedMetaVariables); } Bdd Bdd::constrain(Bdd const& constraint) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Bdd(this->getDdManager(), this->getCuddBdd().Constrain(constraint.getCuddBdd()), metaVariables); } Bdd Bdd::restrict(Bdd const& constraint) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Bdd(this->getDdManager(), this->getCuddBdd().Restrict(constraint.getCuddBdd()), metaVariables); } Bdd Bdd::getSupport() const { return Bdd(this->getDdManager(), this->getCuddBdd().Support(), this->getContainedMetaVariables()); } uint_fast64_t Bdd::getNonZeroCount() const { std::size_t numberOfDdVariables = 0; for (auto const& metaVariable : this->getContainedMetaVariables()) { numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariable).getNumberOfDdVariables(); } return static_cast(this->getCuddBdd().CountMinterm(static_cast(numberOfDdVariables))); } uint_fast64_t Bdd::getLeafCount() const { return static_cast(this->getCuddBdd().CountLeaves()); } uint_fast64_t Bdd::getNodeCount() const { return static_cast(this->getCuddBdd().nodeCount()); } bool Bdd::isOne() const { return this->getCuddBdd().IsOne(); } bool Bdd::isZero() const { return this->getCuddBdd().IsZero(); } uint_fast64_t Bdd::getIndex() const { return static_cast(this->getCuddBdd().NodeReadIndex()); } uint_fast64_t Bdd::getLevel() const { return static_cast(this->getDdManager()->getCuddManager().ReadPerm(this->getIndex())); } void Bdd::exportToDot(std::string const& filename) const { if (filename.empty()) { std::vector cuddBddVector = { this->getCuddBdd() }; this->getDdManager()->getCuddManager().DumpDot(cuddBddVector); } else { // 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 ddVariableNamesAsStrings = this->getDdManager()->getDdVariableNames(); 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 cuddBddVector = { this->getCuddBdd() }; this->getDdManager()->getCuddManager().DumpDot(cuddBddVector, &ddVariableNames[0], &ddNames[0], filePointer); fclose(filePointer); // Finally, delete the names. for (char* element : ddNames) { delete element; } for (char* element : ddVariableNames) { delete element; } } } template BDD Bdd::fromVector(std::shared_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables, std::function const& filter) { std::vector ddVariableIndices = getSortedVariableIndices(*ddManager, metaVariables); uint_fast64_t offset = 0; return BDD(ddManager->getCuddManager(), fromVectorRec(ddManager->getCuddManager().getManager(), offset, 0, ddVariableIndices.size(), values, odd, ddVariableIndices, filter)); } template DdNode* Bdd::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, std::function const& filter) { 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) { if (filter(values[currentOffset++])) { return Cudd_ReadOne(manager); } else { return Cudd_ReadLogicZero(manager); } } 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, filter); } else { elseSuccessor = Cudd_ReadLogicZero(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, filter); } else { thenSuccessor = Cudd_ReadLogicZero(manager); } Cudd_Ref(thenSuccessor); // Create a node representing ITE(currentVar, thenSuccessor, elseSuccessor); DdNode* result = Cudd_bddIthVar(manager, static_cast(ddVariableIndices[currentLevel])); Cudd_Ref(result); DdNode* newResult = Cudd_bddIte(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; } } storm::storage::BitVector Bdd::toVector(storm::dd::Odd const& rowOdd) const { std::vector ddVariableIndices = this->getSortedVariableIndices(); storm::storage::BitVector result(rowOdd.getTotalOffset()); this->toVectorRec(this->getCuddDdNode(), this->getDdManager()->getCuddManager(), result, rowOdd, Cudd_IsComplement(this->getCuddDdNode()), 0, ddVariableIndices.size(), 0, ddVariableIndices); return result; } std::pair, std::unordered_map, storm::expressions::Variable>> Bdd::toExpression(storm::expressions::ExpressionManager& manager, std::unordered_map const& indexToExpressionMap) const { std::pair, std::unordered_map, storm::expressions::Variable>> result; // Create (and maintain) a mapping from the DD nodes to a counter that says the how-many-th node (within the // nodes of equal index) the node was. std::unordered_map nodeToCounterMap; this->toExpressionRec(this->getCuddDdNode(), this->getDdManager()->getCuddManager(), manager, result.first, result.second, nodeToCounterMap); return result; } void Bdd::toExpressionRec(DdNode const* f, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap) const { DdNode const* F = Cudd_Regular(f); // Terminal cases. if (F == Cudd_ReadOne(ddManager.getManager())) { } // Non-terminal cases. // (1) Check if we have seen the node before. auto nodeIt = nodeToCounterMap.find(); } void Bdd::toVectorRec(DdNode const* dd, Cudd const& manager, storm::storage::BitVector& result, Odd const& rowOdd, bool complement, uint_fast64_t currentRowLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, std::vector const& ddRowVariableIndices) const { // If there are no more values to select, we can directly return. if (dd == Cudd_ReadLogicZero(manager.getManager()) && !complement) { return; } else if (dd == Cudd_ReadOne(manager.getManager()) && complement) { return; } // If we are at the maximal level, the value to be set is stored as a constant in the DD. if (currentRowLevel == maxLevel) { result.set(currentRowOffset, true); } else if (ddRowVariableIndices[currentRowLevel] < dd->index) { toVectorRec(dd, manager, result, rowOdd.getElseSuccessor(), complement, currentRowLevel + 1, maxLevel, currentRowOffset, ddRowVariableIndices); toVectorRec(dd, manager, result, rowOdd.getThenSuccessor(), complement, currentRowLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), ddRowVariableIndices); } else { // Otherwise, we compute the ODDs for both the then- and else successors. DdNode* elseDdNode = Cudd_E(dd); DdNode* thenDdNode = Cudd_T(dd); // Determine whether we have to evaluate the successors as if they were complemented. bool elseComplemented = Cudd_IsComplement(elseDdNode) ^ complement; bool thenComplemented = Cudd_IsComplement(thenDdNode) ^ complement; toVectorRec(Cudd_Regular(elseDdNode), manager, result, rowOdd.getElseSuccessor(), elseComplemented, currentRowLevel + 1, maxLevel, currentRowOffset, ddRowVariableIndices); toVectorRec(Cudd_Regular(thenDdNode), manager, result, rowOdd.getThenSuccessor(), thenComplemented, currentRowLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), ddRowVariableIndices); } } std::ostream& operator<<(std::ostream& out, const Bdd& bdd) { bdd.exportToDot(); return out; } } }