diff --git a/src/storage/dd/Add.h b/src/storage/dd/Add.h new file mode 100644 index 000000000..0c12fd586 --- /dev/null +++ b/src/storage/dd/Add.h @@ -0,0 +1,14 @@ +#ifndef STORM_STORAGE_DD_ADD_H_ +#define STORM_STORAGE_DD_ADD_H_ + +#include "src/storage/dd/Dd.h" +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare Add class so we can then specialize it for the different ADD types. + template<DdType Type> class Add; + } +} + +#endif /* STORM_STORAGE_DD_ADD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/Bdd.h b/src/storage/dd/Bdd.h new file mode 100644 index 000000000..47c48474c --- /dev/null +++ b/src/storage/dd/Bdd.h @@ -0,0 +1,14 @@ +#ifndef STORM_STORAGE_DD_BDD_H_ +#define STORM_STORAGE_DD_BDD_H_ + +#include "src/storage/dd/Dd.h" +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + // Declare Bdd class so we can then specialize it for the different BDD types. + template<DdType Type> class Bdd; + } +} + +#endif /* STORM_STORAGE_DD_BDD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddAdd.cpp b/src/storage/dd/CuddAdd.cpp new file mode 100644 index 000000000..31476bc8e --- /dev/null +++ b/src/storage/dd/CuddAdd.cpp @@ -0,0 +1,1174 @@ +#include <cstring> +#include <algorithm> + +#include "src/storage/dd/CuddDd.h" +#include "src/storage/dd/CuddOdd.h" +#include "src/storage/dd/CuddDdManager.h" +#include "src/utility/vector.h" +#include "src/utility/macros.h" + +#include "src/exceptions/IllegalFunctionCallException.h" +#include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/NotImplementedException.h" + +namespace storm { + namespace dd { + Dd<DdType::CUDD>::Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, BDD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables) : ddManager(ddManager), cuddDd(cuddDd), containedMetaVariables(containedMetaVariables) { + // Intentionally left empty. + } + + Dd<DdType::CUDD>::Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, ADD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables) : ddManager(ddManager), cuddDd(cuddDd), containedMetaVariables(containedMetaVariables) { + // Intentionally left empty. + } + + bool Dd<DdType::CUDD>::isBdd() const { + return this->cuddDd.which() == 0; + } + + bool Dd<DdType::CUDD>::isMtbdd() const { + return this->cuddDd.which() == 1; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::toBdd() const { + if (this->isBdd()) { + return *this; + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().BddPattern(), this->containedMetaVariables); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::toMtbdd() const { + if (this->isMtbdd()) { + return *this; + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Add(), this->containedMetaVariables); + } + } + + BDD Dd<DdType::CUDD>::getCuddBdd() const { + if (this->isBdd()) { + return boost::get<BDD>(this->cuddDd); + } else { + STORM_LOG_WARN_COND(false, "Implicitly converting MTBDD to BDD."); + return this->getCuddMtbdd().BddPattern(); + } + } + + ADD Dd<DdType::CUDD>::getCuddMtbdd() const { + if (this->isMtbdd()) { + return boost::get<ADD>(this->cuddDd); + } else { + STORM_LOG_WARN_COND(false, "Implicitly converting BDD to MTBDD."); + return this->getCuddBdd().Add(); + } + } + + ABDD const& Dd<DdType::CUDD>::getCuddDd() const { + if (this->isBdd()) { + return static_cast<ABDD const&>(boost::get<BDD>(this->cuddDd)); + } else { + return static_cast<ABDD const&>(boost::get<ADD>(this->cuddDd)); + } + } + + DdNode* Dd<DdType::CUDD>::getCuddDdNode() const { + if (this->isBdd()) { + return this->getCuddBdd().getNode(); + } else { + return this->getCuddMtbdd().getNode(); + } + } + + bool Dd<DdType::CUDD>::operator==(Dd<DdType::CUDD> const& other) const { + if (this->isBdd()) { + if (other.isBdd()) { + return this->getCuddBdd() == other.getCuddBdd(); + } else { + return false; + } + } else { + if (other.isMtbdd()) { + return this->getCuddMtbdd() == other.getCuddMtbdd(); + } else { + return false; + } + } + } + + bool Dd<DdType::CUDD>::operator!=(Dd<DdType::CUDD> const& other) const { + return !(*this == other); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::ite(Dd<DdType::CUDD> const& thenDd, Dd<DdType::CUDD> const& elseDd) const { + std::set<storm::expressions::Variable> metaVariableNames(this->getContainedMetaVariables()); + metaVariableNames.insert(thenDd.getContainedMetaVariables().begin(), thenDd.getContainedMetaVariables().end()); + metaVariableNames.insert(elseDd.getContainedMetaVariables().begin(), elseDd.getContainedMetaVariables().end()); + + // If all involved DDs are BDDs, the result is also going to be a BDD. + if (this->isBdd() && thenDd.isBdd() && elseDd.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Ite(thenDd.getCuddBdd(), elseDd.getCuddBdd()), metaVariableNames); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->toMtbdd().getCuddMtbdd().Ite(thenDd.toMtbdd().getCuddMtbdd(), elseDd.toMtbdd().getCuddMtbdd()), metaVariableNames); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator+(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result += other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator+=(Dd<DdType::CUDD> const& other) { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing addition on BDDs."); + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + this->cuddDd = this->getCuddMtbdd() + other.getCuddMtbdd(); + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator*(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result *= other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator*=(Dd<DdType::CUDD> const& other) { + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + if (this->isMtbdd() && other.isMtbdd()) { + this->cuddDd = this->getCuddMtbdd() * other.getCuddMtbdd(); + } else if (this->isBdd() && other.isBdd()) { + this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); + } else { + STORM_LOG_WARN_COND(false, "Performing multiplication on two DDs of different type."); + this->cuddDd = this->getCuddMtbdd() * other.getCuddMtbdd(); + } + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator-(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + Dd<DdType::CUDD> result(*this); + result -= other; + return result; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator-() const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + return this->getDdManager()->getZero(true) - *this; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator-=(Dd<DdType::CUDD> const& other) { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + this->cuddDd = this->getCuddMtbdd() - other.getCuddMtbdd(); + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator/(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + Dd<DdType::CUDD> result(*this); + result /= other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator/=(Dd<DdType::CUDD> const& other) { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + this->cuddDd = this->getCuddMtbdd().Divide(other.getCuddMtbdd()); + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator!() const { + Dd<DdType::CUDD> result(*this); + result.complement(); + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::complement() { + if (this->isBdd()) { + this->cuddDd = ~this->getCuddBdd(); + } else { + this->cuddDd = ~this->getCuddMtbdd(); + } + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator&&(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result &= other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator&=(Dd<DdType::CUDD> const& other) { + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + if (this->isBdd() && other.isBdd()) { + this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); + } else if (this->isMtbdd() && other.isMtbdd()) { + // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that + // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. + this->cuddDd = this->getCuddMtbdd() & other.getCuddMtbdd(); + } else { + STORM_LOG_WARN_COND(false, "Performing logical and on two DDs of different type."); + this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); + } + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::operator||(Dd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result |= other; + return result; + } + + Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator|=(Dd<DdType::CUDD> const& other) { + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + if (this->isBdd() && other.isBdd()) { + this->cuddDd = this->getCuddBdd() | other.getCuddBdd(); + } else if (this->isMtbdd() && other.isMtbdd()) { + // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that + // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. + this->cuddDd = this->getCuddMtbdd() | other.getCuddMtbdd(); + } else { + STORM_LOG_WARN_COND(false, "Performing logical and on two DDs of different type."); + this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); + } + return *this; + + STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operation on MTBDD(s)."); + this->cuddDd = this->getCuddBdd() | other.getCuddBdd(); + this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return *this; + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::iff(Dd<DdType::CUDD> const& other) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + if (this->isBdd() && other.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xnor(other.getCuddBdd()), metaVariables); + } else if (this->isMtbdd() && other.isMtbdd()) { + // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that + // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Xnor(other.getCuddMtbdd()), metaVariables); + } else { + STORM_LOG_WARN_COND(false, "Performing logical iff on two DDs of different type."); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xnor(other.getCuddBdd()), metaVariables); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::exclusiveOr(Dd<DdType::CUDD> const& other) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + if (this->isBdd() && other.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xor(other.getCuddBdd()), metaVariables); + } else if (this->isMtbdd() && other.isMtbdd()) { + // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that + // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Xor(other.getCuddMtbdd()), metaVariables); + } else { + STORM_LOG_WARN_COND(false, "Performing logical iff on two DDs of different type."); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xor(other.getCuddBdd()), metaVariables); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::implies(Dd<DdType::CUDD> const& other) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operatiorn on MTBDDs."); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Ite(other.getCuddBdd(), this->getDdManager()->getOne().getCuddBdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::equals(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Equals(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::notEquals(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().NotEquals(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::less(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().LessThan(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::lessOrEqual(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().LessThanOrEqual(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greater(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().GreaterThan(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greaterOrEqual(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().GreaterThanOrEqual(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::minimum(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Minimum(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::maximum(Dd<DdType::CUDD> const& other) const { + STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Maximum(other.getCuddMtbdd()), metaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::existsAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + STORM_LOG_WARN_COND(this->isBdd(), "Performing logical operation on MTBDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(false)); + std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().ExistAbstract(cubeDd.getCuddBdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::universalAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + STORM_LOG_WARN_COND(this->isBdd(), "Performing logical operation on MTBDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().UnivAbstract(cubeDd.getCuddBdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::sumAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(true)); + std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd *= ddMetaVariable.getCubeAsMtbdd(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().ExistAbstract(cubeDd.getCuddMtbdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::minAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(true)); + std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd *= ddMetaVariable.getCubeAsMtbdd(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().MinAbstract(cubeDd.getCuddMtbdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::maxAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(true)); + std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd *= ddMetaVariable.getCubeAsMtbdd(); + } + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().MaxAbstract(cubeDd.getCuddMtbdd()), newMetaVariables); + } + + bool Dd<DdType::CUDD>::equalModuloPrecision(Dd<DdType::CUDD> const& other, double precision, bool relative) const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + if (relative) { + return this->getCuddMtbdd().EqualSupNormRel(other.getCuddMtbdd(), precision); + } else { + return this->getCuddMtbdd().EqualSupNorm(other.getCuddMtbdd(), precision); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs) { + std::set<storm::expressions::Variable> newContainedMetaVariables; + if (this->isBdd()) { + std::vector<BDD> from; + std::vector<BDD> to; + for (auto const& metaVariablePair : metaVariablePairs) { + DdMetaVariable<DdType::CUDD> const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); + DdMetaVariable<DdType::CUDD> 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 Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().SwapVariables(from, to), newContainedMetaVariables); + } else { + std::vector<ADD> from; + std::vector<ADD> to; + for (auto const& metaVariablePair : metaVariablePairs) { + DdMetaVariable<DdType::CUDD> const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); + DdMetaVariable<DdType::CUDD> const& variable2 = this->getDdManager()->getMetaVariable(metaVariablePair.second); + + // Check if it's legal so swap the meta variables. + if (variable1.getNumberOfDdVariables() != variable2.getNumberOfDdVariables()) { + throw 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.getCuddMtbdd()); + } + for (auto const& ddVariable : variable2.getDdVariables()) { + to.push_back(ddVariable.getCuddMtbdd()); + } + } + + // Finally, call CUDD to swap the variables. + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().SwapVariables(from, to), newContainedMetaVariables); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::multiplyMatrix(Dd<DdType::CUDD> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const { + STORM_LOG_WARN_COND(this->isMtbdd() && otherMatrix.isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + // Create the CUDD summation variables. + std::vector<ADD> summationDdVariables; + for (auto const& metaVariable : summationMetaVariables) { + for (auto const& ddVariable : this->getDdManager()->getMetaVariable(metaVariable).getDdVariables()) { + summationDdVariables.push_back(ddVariable.getCuddMtbdd()); + } + } + + std::set<storm::expressions::Variable> unionOfMetaVariables; + std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), otherMatrix.getContainedMetaVariables().begin(), otherMatrix.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); + std::set<storm::expressions::Variable> containedMetaVariables; + std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), summationMetaVariables.begin(), summationMetaVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().MatrixMultiply(otherMatrix.getCuddMtbdd(), summationDdVariables), containedMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::andExists(Dd<DdType::CUDD> const& other, std::set<storm::expressions::Variable> const& existentialVariables) const { + STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operation on MTBDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + for (auto const& metaVariable : existentialVariables) { + DdMetaVariable<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + std::set<storm::expressions::Variable> unionOfMetaVariables; + std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); + std::set<storm::expressions::Variable> containedMetaVariables; + std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), existentialVariables.begin(), existentialVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().AndAbstract(other.getCuddBdd(), cubeDd.getCuddBdd()), containedMetaVariables); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greater(double value) const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().BddStrictThreshold(value), this->getContainedMetaVariables()); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::greaterOrEqual(double value) const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().BddThreshold(value), this->getContainedMetaVariables()); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::notZero() const { + return this->toBdd(); + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::constrain(Dd<DdType::CUDD> const& constraint) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end()); + + if (this->isBdd() && constraint.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Constrain(constraint.getCuddBdd()), metaVariables); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Constrain(constraint.getCuddMtbdd()), metaVariables); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::restrict(Dd<DdType::CUDD> const& constraint) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end()); + + if (this->isBdd() && constraint.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Restrict(constraint.getCuddBdd()), metaVariables); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Restrict(constraint.getCuddMtbdd()), metaVariables); + } + } + + Dd<DdType::CUDD> Dd<DdType::CUDD>::getSupport() const { + if (this->isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Support(), this->getContainedMetaVariables()); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Support(), this->getContainedMetaVariables()); + } + } + + uint_fast64_t Dd<DdType::CUDD>::getNonZeroCount() const { + std::size_t numberOfDdVariables = 0; + for (auto const& metaVariable : this->getContainedMetaVariables()) { + numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariable).getNumberOfDdVariables(); + } + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().CountMinterm(static_cast<int>(numberOfDdVariables))); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().CountMinterm(static_cast<int>(numberOfDdVariables))); + } + } + + uint_fast64_t Dd<DdType::CUDD>::getLeafCount() const { + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().CountLeaves()); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().CountLeaves()); + } + } + + uint_fast64_t Dd<DdType::CUDD>::getNodeCount() const { + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().nodeCount()); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().nodeCount()); + } + } + + double Dd<DdType::CUDD>::getMin() const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + ADD constantMinAdd = this->getCuddMtbdd().FindMin(); + return static_cast<double>(Cudd_V(constantMinAdd.getNode())); + } + + double Dd<DdType::CUDD>::getMax() const { + STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); + + ADD constantMaxAdd = this->getCuddMtbdd().FindMax(); + return static_cast<double>(Cudd_V(constantMaxAdd.getNode())); + } + + void Dd<DdType::CUDD>::setValue(storm::expressions::Variable const& metaVariable, int_fast64_t variableValue, double targetValue) { + std::map<storm::expressions::Variable, int_fast64_t> metaVariableToValueMap; + metaVariableToValueMap.emplace(metaVariable, variableValue); + this->setValue(metaVariableToValueMap, targetValue); + } + + void Dd<DdType::CUDD>::setValue(storm::expressions::Variable const& metaVariable1, int_fast64_t variableValue1, storm::expressions::Variable const& metaVariable2, int_fast64_t variableValue2, double targetValue) { + std::map<storm::expressions::Variable, int_fast64_t> metaVariableToValueMap; + metaVariableToValueMap.emplace(metaVariable1, variableValue1); + metaVariableToValueMap.emplace(metaVariable2, variableValue2); + this->setValue(metaVariableToValueMap, targetValue); + } + + void Dd<DdType::CUDD>::setValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap, double targetValue) { + if (this->isBdd()) { + STORM_LOG_THROW(targetValue == storm::utility::zero<double>() || targetValue == storm::utility::one<double>(), storm::exceptions::InvalidArgumentException, "Cannot set encoding to non-0, non-1 value " << targetValue << " in BDD."); + Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); + for (auto const& nameValuePair : metaVariableToValueMap) { + valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); + // Also record that the DD now contains the meta variable. + this->addContainedMetaVariable(nameValuePair.first); + } + if (targetValue == storm::utility::one<double>()) { + this->cuddDd = valueEncoding.getCuddBdd().Ite(this->getDdManager()->getOne().getCuddBdd(), this->getCuddBdd()); + } else { + this->cuddDd = valueEncoding.getCuddBdd().Ite(this->getDdManager()->getZero().getCuddBdd(), this->getCuddBdd()); + } + } else { + Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); + for (auto const& nameValuePair : metaVariableToValueMap) { + valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); + // Also record that the DD now contains the meta variable. + this->addContainedMetaVariable(nameValuePair.first); + } + + this->cuddDd = valueEncoding.toMtbdd().getCuddMtbdd().Ite(this->getDdManager()->getConstant(targetValue).getCuddMtbdd(), this->getCuddMtbdd()); + } + } + + double Dd<DdType::CUDD>::getValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap) const { + std::set<storm::expressions::Variable> remainingMetaVariables(this->getContainedMetaVariables()); + Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); + for (auto const& nameValuePair : metaVariableToValueMap) { + valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); + if (this->containsMetaVariable(nameValuePair.first)) { + remainingMetaVariables.erase(nameValuePair.first); + } + } + + STORM_LOG_THROW(remainingMetaVariables.empty(), storm::exceptions::InvalidArgumentException, "Cannot evaluate function for which not all inputs were given."); + + if (this->isBdd()) { + Dd<DdType::CUDD> value = *this && valueEncoding; + return value.isZero() ? 0 : 1; + } else { + Dd<DdType::CUDD> value = *this * valueEncoding.toMtbdd(); + value = value.sumAbstract(this->getContainedMetaVariables()); + return static_cast<double>(Cudd_V(value.getCuddMtbdd().getNode())); + } + } + + bool Dd<DdType::CUDD>::isOne() const { + if (this->isBdd()) { + return this->getCuddBdd().IsOne(); + } else { + return this->getCuddMtbdd().IsOne(); + } + } + + bool Dd<DdType::CUDD>::isZero() const { + if (this->isBdd()) { + return this->getCuddBdd().IsZero(); + } else { + return this->getCuddMtbdd().IsZero(); + } + } + + bool Dd<DdType::CUDD>::isConstant() const { + if (this->isBdd()) { + return Cudd_IsConstant(this->getCuddBdd().getNode()); + } else { + return Cudd_IsConstant(this->getCuddMtbdd().getNode()); + } + } + + uint_fast64_t Dd<DdType::CUDD>::getIndex() const { + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().NodeReadIndex()); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().NodeReadIndex()); + } + } + + template<typename ValueType> + std::vector<ValueType> Dd<DdType::CUDD>::toVector() const { + STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create vector from BDD."); + return this->toVector<ValueType>(Odd<DdType::CUDD>(*this)); + } + + template<typename ValueType> + std::vector<ValueType> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const { + STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create vector from BDD."); + std::vector<ValueType> result(rowOdd.getTotalOffset()); + std::vector<uint_fast64_t> ddVariableIndices = this->getSortedVariableIndices(); + addToVectorRec(this->getCuddDdNode(), 0, ddVariableIndices.size(), 0, rowOdd, ddVariableIndices, result); + return result; + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix() const { + STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); + std::set<storm::expressions::Variable> rowVariables; + std::set<storm::expressions::Variable> columnVariables; + + for (auto const& variable : this->getContainedMetaVariables()) { + if (variable.getName().size() > 0 && variable.getName().back() == '\'') { + columnVariables.insert(variable); + } else { + rowVariables.insert(variable); + } + } + + return toMatrix(rowVariables, columnVariables, Odd<DdType::CUDD>(this->existsAbstract(rowVariables)), Odd<DdType::CUDD>(this->existsAbstract(columnVariables))); + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { + STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); + std::set<storm::expressions::Variable> rowMetaVariables; + std::set<storm::expressions::Variable> columnMetaVariables; + + for (auto const& variable : this->getContainedMetaVariables()) { + if (variable.getName().size() > 0 && variable.getName().back() == '\'') { + columnMetaVariables.insert(variable); + } else { + rowMetaVariables.insert(variable); + } + } + + return toMatrix(rowMetaVariables, columnMetaVariables, rowOdd, columnOdd); + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { + STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); + std::vector<uint_fast64_t> ddRowVariableIndices; + std::vector<uint_fast64_t> ddColumnVariableIndices; + + for (auto const& variable : rowMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddRowVariableIndices.push_back(ddVariable.getIndex()); + } + } + for (auto const& variable : columnMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddColumnVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // Prepare the vectors that represent the matrix. + std::vector<uint_fast64_t> rowIndications(rowOdd.getTotalOffset() + 1); + std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>> columnsAndValues(this->getNonZeroCount()); + + // Create a trivial row grouping. + std::vector<uint_fast64_t> trivialRowGroupIndices(rowIndications.size()); + uint_fast64_t i = 0; + for (auto& entry : trivialRowGroupIndices) { + entry = i; + ++i; + } + + // Use the toMatrixRec function to compute the number of elements in each row. Using the flag, we prevent + // it from actually generating the entries in the entry vector. + toMatrixRec(this->getCuddDdNode(), rowIndications, columnsAndValues, trivialRowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, false); + + // TODO: counting might be faster by just summing over the primed variables and then using the ODD to convert + // the resulting (DD) vector to an explicit vector. + + // Now that we computed the number of entries in each row, compute the corresponding offsets in the entry vector. + uint_fast64_t tmp = 0; + uint_fast64_t tmp2 = 0; + for (uint_fast64_t i = 1; i < rowIndications.size(); ++i) { + tmp2 = rowIndications[i]; + rowIndications[i] = rowIndications[i - 1] + tmp; + std::swap(tmp, tmp2); + } + rowIndications[0] = 0; + + // Now actually fill the entry vector. + toMatrixRec(this->getCuddDdNode(), rowIndications, columnsAndValues, trivialRowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, true); + + // Since the last call to toMatrixRec modified the rowIndications, we need to restore the correct values. + for (uint_fast64_t i = rowIndications.size() - 1; i > 0; --i) { + rowIndications[i] = rowIndications[i - 1]; + } + rowIndications[0] = 0; + + // Construct matrix and return result. + return storm::storage::SparseMatrix<double>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(trivialRowGroupIndices)); + } + + storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, std::set<storm::expressions::Variable> const& groupMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { + STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); + std::vector<uint_fast64_t> ddRowVariableIndices; + std::vector<uint_fast64_t> ddColumnVariableIndices; + std::vector<uint_fast64_t> ddGroupVariableIndices; + std::set<storm::expressions::Variable> rowAndColumnMetaVariables; + + for (auto const& variable : rowMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddRowVariableIndices.push_back(ddVariable.getIndex()); + } + rowAndColumnMetaVariables.insert(variable); + } + for (auto const& variable : columnMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddColumnVariableIndices.push_back(ddVariable.getIndex()); + } + rowAndColumnMetaVariables.insert(variable); + } + for (auto const& variable : groupMetaVariables) { + DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddGroupVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // TODO: assert that the group variables are at the very top of the variable ordering? + + // Start by computing the offsets (in terms of rows) for each row group. + Dd<DdType::CUDD> stateToNumberOfChoices = this->notZero().existsAbstract(columnMetaVariables).sumAbstract(groupMetaVariables); + std::vector<uint_fast64_t> rowGroupIndices = stateToNumberOfChoices.toVector<uint_fast64_t>(rowOdd); + rowGroupIndices.resize(rowGroupIndices.size() + 1); + uint_fast64_t tmp = 0; + uint_fast64_t tmp2 = 0; + for (uint_fast64_t i = 1; i < rowGroupIndices.size(); ++i) { + tmp2 = rowGroupIndices[i]; + rowGroupIndices[i] = rowGroupIndices[i - 1] + tmp; + std::swap(tmp, tmp2); + } + rowGroupIndices[0] = 0; + + // Next, we split the matrix into one for each group. This only works if the group variables are at the very + // top. + std::vector<Dd<DdType::CUDD>> groups; + splitGroupsRec(this->getCuddDdNode(), groups, ddGroupVariableIndices, 0, ddGroupVariableIndices.size(), rowAndColumnMetaVariables); + + // Create the actual storage for the non-zero entries. + std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>> columnsAndValues(this->getNonZeroCount()); + + // Now compute the indices at which the individual rows start. + std::vector<uint_fast64_t> rowIndications(rowGroupIndices.back() + 1); + std::vector<storm::dd::Dd<DdType::CUDD>> statesWithGroupEnabled(groups.size()); + for (uint_fast64_t i = 0; i < groups.size(); ++i) { + auto const& dd = groups[i]; + + toMatrixRec(dd.getCuddDdNode(), rowIndications, columnsAndValues, rowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, false); + + statesWithGroupEnabled[i] = dd.notZero().existsAbstract(columnMetaVariables); + addToVectorRec(statesWithGroupEnabled[i].getCuddDdNode(), 0, ddRowVariableIndices.size(), 0, rowOdd, ddRowVariableIndices, rowGroupIndices); + } + + // Since we modified the rowGroupIndices, we need to restore the correct values. + for (uint_fast64_t i = rowGroupIndices.size() - 1; i > 0; --i) { + rowGroupIndices[i] = rowGroupIndices[i - 1]; + } + rowGroupIndices[0] = 0; + + // Now that we computed the number of entries in each row, compute the corresponding offsets in the entry vector. + tmp = 0; + tmp2 = 0; + for (uint_fast64_t i = 1; i < rowIndications.size(); ++i) { + tmp2 = rowIndications[i]; + rowIndications[i] = rowIndications[i - 1] + tmp; + std::swap(tmp, tmp2); + } + rowIndications[0] = 0; + + // Now actually fill the entry vector. + for (uint_fast64_t i = 0; i < groups.size(); ++i) { + auto const& dd = groups[i]; + + toMatrixRec(dd.getCuddDdNode(), rowIndications, columnsAndValues, rowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, true); + + addToVectorRec(statesWithGroupEnabled[i].getCuddDdNode(), 0, ddRowVariableIndices.size(), 0, rowOdd, ddRowVariableIndices, rowGroupIndices); + } + + // Since we modified the rowGroupIndices, we need to restore the correct values. + for (uint_fast64_t i = rowGroupIndices.size() - 1; i > 0; --i) { + rowGroupIndices[i] = rowGroupIndices[i - 1]; + } + rowGroupIndices[0] = 0; + + // Since the last call to toMatrixRec modified the rowIndications, we need to restore the correct values. + for (uint_fast64_t i = rowIndications.size() - 1; i > 0; --i) { + rowIndications[i] = rowIndications[i - 1]; + } + rowIndications[0] = 0; + + return storm::storage::SparseMatrix<double>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); + } + + void Dd<DdType::CUDD>::toMatrixRec(DdNode const* dd, std::vector<uint_fast64_t>& rowIndications, std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>>& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupOffsets, Odd<DdType::CUDD> const& rowOdd, Odd<DdType::CUDD> const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector<uint_fast64_t> const& ddRowVariableIndices, std::vector<uint_fast64_t> const& ddColumnVariableIndices, bool generateValues) const { + // For the empty DD, we do not need to add any entries. + if (dd == Cudd_ReadZero(this->getDdManager()->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<uint_fast64_t, double>(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. + toMatrixRec(elseElse, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getElseSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); + // Visit else-then. + toMatrixRec(elseThen, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getElseSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); + // Visit then-else. + toMatrixRec(thenElse, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getThenSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); + // Visit then-then. + toMatrixRec(thenThen, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getThenSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); + } + } + + void Dd<DdType::CUDD>::splitGroupsRec(DdNode* dd, std::vector<Dd<DdType::CUDD>>& groups, std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set<storm::expressions::Variable> const& remainingMetaVariables) const { + // For the empty DD, we do not need to create a group. + if (dd == Cudd_ReadZero(this->getDdManager()->getCuddManager().getManager())) { + return; + } + + if (currentLevel == maxLevel) { + groups.push_back(Dd<DdType::CUDD>(this->getDdManager(), ADD(this->getDdManager()->getCuddManager(), dd), remainingMetaVariables)); + } else if (ddGroupVariableIndices[currentLevel] < dd->index) { + splitGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + splitGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + } else { + splitGroupsRec(Cudd_E(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + splitGroupsRec(Cudd_T(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); + } + } + + template<typename ValueType> + void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<ValueType>& targetVector) const { + // For the empty DD, we do not need to add any entries. + if (dd == Cudd_ReadZero(this->getDdManager()->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) { + targetVector[currentOffset] += static_cast<ValueType>(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. + addToVectorRec(dd, currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector); + addToVectorRec(dd, currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector); + } else { + // Otherwise, we simply recursively call the function for both (different) cases. + addToVectorRec(Cudd_E(dd), currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector); + addToVectorRec(Cudd_T(dd), currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector); + } + } + + std::vector<uint_fast64_t> Dd<DdType::CUDD>::getSortedVariableIndices() const { + std::vector<uint_fast64_t> ddVariableIndices; + for (auto const& metaVariableName : this->getContainedMetaVariables()) { + auto const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // Next, we need to sort them, since they may be arbitrarily ordered otherwise. + std::sort(ddVariableIndices.begin(), ddVariableIndices.end()); + return ddVariableIndices; + } + + bool Dd<DdType::CUDD>::containsMetaVariable(storm::expressions::Variable const& metaVariable) const { + return containedMetaVariables.find(metaVariable) != containedMetaVariables.end(); + } + + bool Dd<DdType::CUDD>::containsMetaVariables(std::set<storm::expressions::Variable> const& metaVariables) const { + for (auto const& metaVariable : metaVariables) { + auto const& ddMetaVariable = containedMetaVariables.find(metaVariable); + + if (ddMetaVariable == containedMetaVariables.end()) { + return false; + } + } + return true; + } + + std::set<storm::expressions::Variable> const& Dd<DdType::CUDD>::getContainedMetaVariables() const { + return this->containedMetaVariables; + } + + std::set<storm::expressions::Variable>& Dd<DdType::CUDD>::getContainedMetaVariables() { + return this->containedMetaVariables; + } + + void Dd<DdType::CUDD>::exportToDot(std::string const& filename) const { + if (filename.empty()) { + if (this->isBdd()) { + std::vector<BDD> cuddBddVector = { this->getCuddBdd() }; + this->getDdManager()->getCuddManager().DumpDot(cuddBddVector); + } else { + std::vector<ADD> cuddAddVector = { this->getCuddMtbdd() }; + this->getDdManager()->getCuddManager().DumpDot(cuddAddVector); + } + } else { + // Build the name input of the DD. + std::vector<char*> 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<std::string> ddVariableNamesAsStrings = this->getDdManager()->getDdVariableNames(); + std::vector<char*> 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"); + if (this->isBdd()) { + std::vector<BDD> cuddBddVector = { this->getCuddBdd() }; + this->getDdManager()->getCuddManager().DumpDot(cuddBddVector, &ddVariableNames[0], &ddNames[0], filePointer); + } else { + std::vector<ADD> cuddAddVector = { this->getCuddMtbdd() }; + this->getDdManager()->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; + } + } + } + + void Dd<DdType::CUDD>::addContainedMetaVariable(storm::expressions::Variable const& metaVariable) { + this->getContainedMetaVariables().insert(metaVariable); + } + + void Dd<DdType::CUDD>::removeContainedMetaVariable(storm::expressions::Variable const& metaVariable) { + this->getContainedMetaVariables().erase(metaVariable); + } + + std::shared_ptr<DdManager<DdType::CUDD> const> Dd<DdType::CUDD>::getDdManager() const { + return this->ddManager; + } + + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const { + int* cube; + double value; + DdGen* generator = this->getCuddDd().FirstCube(&cube, &value); + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, (Cudd_IsGenEmpty(generator) != 0), &this->getContainedMetaVariables(), enumerateDontCareMetaVariables); + } + + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const { + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); + } + + storm::expressions::Expression Dd<DdType::CUDD>::toExpression() const { + return toExpressionRecur(this->getCuddDdNode(), this->getDdManager()->getDdVariables()); + } + + storm::expressions::Expression Dd<DdType::CUDD>::getMintermExpression() const { + // Note that we first transform the ADD into a BDD to convert all non-zero terminals to ones and therefore + // make the DD more compact. + return getMintermExpressionRecur(this->getDdManager()->getCuddManager().getManager(), this->toBdd().getCuddDdNode(), this->getDdManager()->getDdVariables()); + } + + storm::expressions::Expression Dd<DdType::CUDD>::toExpressionRecur(DdNode const* dd, std::vector<storm::expressions::Variable> const& variables) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This feature is currently unavailable."); + // If the DD is a terminal node, we can simply return a constant expression. + // if (Cudd_IsConstant(dd)) { + // return storm::expressions::Expression::createDoubleLiteral(static_cast<double>(Cudd_V(dd))); + // } else { + // return storm::expressions::Expression::createBooleanVariable(variableNames[dd->index]).ite(toExpressionRecur(Cudd_T(dd), variableNames), toExpressionRecur(Cudd_E(dd), variableNames)); + // } + } + + storm::expressions::Expression Dd<DdType::CUDD>::getMintermExpressionRecur(::DdManager* manager, DdNode const* dd, std::vector<storm::expressions::Variable> const& variables) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This feature is currently unavailable."); + // If the DD is a terminal node, we can simply return a constant expression. + // if (Cudd_IsConstant(dd)) { + // if (Cudd_IsComplement(dd)) { + // return storm::expressions::Expression::createBooleanLiteral(false); + // } else { + // return storm::expressions::Expression::createBooleanLiteral((dd == Cudd_ReadOne(manager)) ? true : false); + // } + // } else { + // // Get regular versions of the pointers. + // DdNode* regularDd = Cudd_Regular(dd); + // DdNode* thenDd = Cudd_T(regularDd); + // DdNode* elseDd = Cudd_E(regularDd); + // + // // Compute expression recursively. + // storm::expressions::Expression result = storm::expressions::Expression::createBooleanVariable(variableNames[dd->index]).ite(getMintermExpressionRecur(manager, thenDd, variableNames), getMintermExpressionRecur(manager, elseDd, variableNames)); + // if (Cudd_IsComplement(dd)) { + // result = !result; + // } + // + // return result; + // } + } + + std::ostream& operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) { + dd.exportToDot(); + return out; + } + + // Explicitly instantiate some templated functions. + template std::vector<double> Dd<DdType::CUDD>::toVector() const; + template std::vector<double> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const; + template void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<double>& targetVector) const; + template std::vector<uint_fast64_t> Dd<DdType::CUDD>::toVector() const; + template std::vector<uint_fast64_t> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const; + template void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<uint_fast64_t>& targetVector) const; + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddAdd.h b/src/storage/dd/CuddAdd.h new file mode 100644 index 000000000..f860e79bf --- /dev/null +++ b/src/storage/dd/CuddAdd.h @@ -0,0 +1,845 @@ +#ifndef STORM_STORAGE_DD_CUDDDD_H_ +#define STORM_STORAGE_DD_CUDDDD_H_ + +#include <map> +#include <set> +#include <memory> +#include <iostream> +#include <boost/variant.hpp> + +#include "src/storage/dd/Dd.h" +#include "src/storage/dd/CuddDdForwardIterator.h" +#include "src/storage/SparseMatrix.h" +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/Variable.h" +#include "src/utility/OsDetection.h" + +// Include the C++-interface of CUDD. +#include "cuddObj.hh" + +namespace storm { + namespace dd { + // Forward-declare some classes. + template<DdType Type> class DdManager; + template<DdType Type> class Odd; + + template<> + class Dd<DdType::CUDD> { + public: + // Declare the DdManager and DdIterator class as friend so it can access the internals of a DD. + friend class DdManager<DdType::CUDD>; + friend class DdForwardIterator<DdType::CUDD>; + friend class Odd<DdType::CUDD>; + + // Instantiate all copy/move constructors/assignments with the default implementation. + Dd() = default; + Dd(Dd<DdType::CUDD> const& other) = default; + Dd& operator=(Dd<DdType::CUDD> const& other) = default; +#ifndef WINDOWS + Dd(Dd<DdType::CUDD>&& other) = default; + Dd& operator=(Dd<DdType::CUDD>&& other) = default; +#endif + + /*! + * Retrieves whether the two DDs represent the same function. + * + * @param other The DD that is to be compared with the current one. + * @return True if the DDs represent the same function. + */ + bool operator==(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves whether the two DDs represent different functions. + * + * @param other The DD that is to be compared with the current one. + * @return True if the DDs represent the different functions. + */ + bool operator!=(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs an if-then-else with the given operands, i.e. maps all valuations that are mapped to a non-zero + * function value to the function values specified by the first DD and all others to the function values + * specified by the second DD. + */ + Dd<DdType::CUDD> ite(Dd<DdType::CUDD> const& thenDd, Dd<DdType::CUDD> const& elseDd) const; + + /*! + * Performs a logical or of the current and the given DD. + * + * @param other The second DD used for the operation. + * @return The logical or of the operands. + */ + Dd<DdType::CUDD> operator||(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical or of the current and the given DD and assigns it to the current DD. + * + * @param other The second DD used for the operation. + * @return A reference to the current DD after the operation + */ + Dd<DdType::CUDD>& operator|=(Dd<DdType::CUDD> const& other); + + /*! + * Performs a logical and of the current and the given DD. + * + * @param other The second DD used for the operation. + * @return The logical and of the operands. + */ + Dd<DdType::CUDD> operator&&(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical and of the current and the given DD and assigns it to the current DD. + * + * @param other The second DD used for the operation. + * @return A reference to the current DD after the operation + */ + Dd<DdType::CUDD>& operator&=(Dd<DdType::CUDD> const& other); + + /*! + * Performs a logical iff of the current and the given DD. + * + * @param other The second DD used for the operation. + * @return The logical iff of the operands. + */ + Dd<DdType::CUDD> iff(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical exclusive-or of the current and the given DD. + * + * @param other The second DD used for the operation. + * @return The logical exclusive-or of the operands. + */ + Dd<DdType::CUDD> exclusiveOr(Dd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical implication of the current and the given DD. + * + * @param other The second DD used for the operation. + * @return The logical implication of the operands. + */ + Dd<DdType::CUDD> implies(Dd<DdType::CUDD> const& other) const; + + /*! + * Adds the two DDs. + * + * @param other The DD to add to the current one. + * @return The result of the addition. + */ + Dd<DdType::CUDD> operator+(Dd<DdType::CUDD> const& other) const; + + /*! + * Adds the given DD to the current one. + * + * @param other The DD to add to the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator+=(Dd<DdType::CUDD> const& other); + + /*! + * Multiplies the two DDs. + * + * @param other The DD to multiply with the current one. + * @return The result of the multiplication. + */ + Dd<DdType::CUDD> operator*(Dd<DdType::CUDD> const& other) const; + + /*! + * Multiplies the given DD with the current one and assigns the result to the current DD. + * + * @param other The DD to multiply with the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator*=(Dd<DdType::CUDD> const& other); + + /*! + * Subtracts the given DD from the current one. + * + * @param other The DD to subtract from the current one. + * @return The result of the subtraction. + */ + Dd<DdType::CUDD> operator-(Dd<DdType::CUDD> const& other) const; + + /*! + * Subtracts the DD from the constant zero function. + * + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> operator-() const; + + /*! + * Subtracts the given DD from the current one and assigns the result to the current DD. + * + * @param other The DD to subtract from the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator-=(Dd<DdType::CUDD> const& other); + + /*! + * Divides the current DD by the given one. + * + * @param other The DD by which to divide the current one. + * @return The result of the division. + */ + Dd<DdType::CUDD> operator/(Dd<DdType::CUDD> const& other) const; + + /*! + * Divides the current DD by the given one and assigns the result to the current DD. + * + * @param other The DD by which to divide the current one. + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& operator/=(Dd<DdType::CUDD> const& other); + + /*! + * Retrieves the logical complement of the current DD. The result will map all encodings with a value + * unequal to zero to false and all others to true. + * + * @return The logical complement of the current DD. + */ + Dd<DdType::CUDD> operator!() const; + + /*! + * Logically complements the current DD. The result will map all encodings with a value + * unequal to zero to false and all others to true. + * + * @return A reference to the current DD after the operation. + */ + Dd<DdType::CUDD>& complement(); + + /*! + * Retrieves the function that maps all evaluations to one that have an identical function values. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> equals(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one that have distinct function values. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> notEquals(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are less + * than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> less(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are less or + * equal than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> lessOrEqual(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are greater + * than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> greater(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to one whose function value in the first DD are greater + * or equal than the one in the given DD. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> greaterOrEqual(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to the minimum of the function values of the two DDs. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> minimum(Dd<DdType::CUDD> const& other) const; + + /*! + * Retrieves the function that maps all evaluations to the maximum of the function values of the two DDs. + * + * @param other The DD with which to perform the operation. + * @return The resulting function represented as a DD. + */ + Dd<DdType::CUDD> maximum(Dd<DdType::CUDD> const& other) const; + + /*! + * Existentially abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Dd<DdType::CUDD> existsAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Universally abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Dd<DdType::CUDD> universalAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Sum-abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Dd<DdType::CUDD> sumAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Min-abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Dd<DdType::CUDD> minAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Max-abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Dd<DdType::CUDD> maxAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Checks whether the current and the given DD represent the same function modulo some given precision. + * + * @param other The DD with which to compare. + * @param precision An upper bound on the maximal difference between any two function values that is to be + * tolerated. + * @param relative If set to true, not the absolute values have to be within the precision, but the relative + * values. + */ + bool equalModuloPrecision(Dd<DdType::CUDD> const& other, double precision, bool relative = true) const; + + /*! + * Swaps the given pairs of meta variables in the DD. The pairs of meta variables must be guaranteed to have + * the same number of underlying DD variables. + * + * @param metaVariablePairs A vector of meta variable pairs that are to be swapped for one another. + * @return The resulting DD. + */ + Dd<DdType::CUDD> swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs); + + /*! + * Multiplies the current DD (representing a matrix) with the given matrix by summing over the given meta + * variables. + * + * @param otherMatrix The matrix with which to multiply. + * @param summationMetaVariables The names of the meta variables over which to sum during the matrix- + * matrix multiplication. + * @return A DD representing the result of the matrix-matrix multiplication. + */ + Dd<DdType::CUDD> multiplyMatrix(Dd<DdType::CUDD> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const; + + /*! + * Computes the logical and of the current and the given DD and existentially abstracts from the given set + * of variables. + * + * @param other The second DD for the logical and. + * @param existentialVariables The variables from which to existentially abstract. + * @return A DD representing the result. + */ + Dd<DdType::CUDD> andExists(Dd<DdType::CUDD> const& other, std::set<storm::expressions::Variable> const& existentialVariables) const; + + /*! + * Computes a DD that represents the function in which all assignments with a function value strictly larger + * than the given value are mapped to one and all others to zero. + * + * @param value The value used for the comparison. + * @return The resulting DD. + */ + Dd<DdType::CUDD> greater(double value) const; + + /*! + * Computes a DD that represents the function in which all assignments with a function value larger or equal + * to the given value are mapped to one and all others to zero. + * + * @param value The value used for the comparison. + * @return The resulting DD. + */ + Dd<DdType::CUDD> greaterOrEqual(double value) const; + + /*! + * Computes a DD that represents the function in which all assignments with a function value unequal to zero + * are mapped to one and all others to zero. + * + * @return The resulting DD. + */ + Dd<DdType::CUDD> notZero() const; + + /*! + * Computes the constraint of the current DD with the given constraint. That is, the function value of the + * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint + * and may be different otherwise. + * + * @param constraint The constraint to use for the operation. + * @return The resulting DD. + */ + Dd<DdType::CUDD> constrain(Dd<DdType::CUDD> const& constraint) const; + + /*! + * Computes the restriction of the current DD with the given constraint. That is, the function value of the + * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint + * and may be different otherwise. + * + * @param constraint The constraint to use for the operation. + * @return The resulting DD. + */ + Dd<DdType::CUDD> restrict(Dd<DdType::CUDD> const& constraint) const; + + /*! + * Retrieves the support of the current DD. + * + * @return The support represented as a DD. + */ + Dd<DdType::CUDD> getSupport() const; + + /*! + * Retrieves the number of encodings that are mapped to a non-zero value. + * + * @return The number of encodings that are mapped to a non-zero value. + */ + uint_fast64_t getNonZeroCount() const; + + /*! + * Retrieves the number of leaves of the DD. + * + * @return The number of leaves of the DD. + */ + uint_fast64_t getLeafCount() const; + + /*! + * Retrieves the number of nodes necessary to represent the DD. + * + * @return The number of nodes in this DD. + */ + uint_fast64_t getNodeCount() const; + + /*! + * Retrieves the lowest function value of any encoding. + * + * @return The lowest function value of any encoding. + */ + double getMin() const; + + /*! + * Retrieves the highest function value of any encoding. + * + * @return The highest function value of any encoding. + */ + double getMax() const; + + /*! + * Sets the function values of all encodings that have the given value of the meta variable to the given + * target value. + * + * @param metaVariable The meta variable that has to be equal to the given value. + * @param variableValue The value that the meta variable is supposed to have. This must be within the range + * of the meta variable. + * @param targetValue The new function value of the modified encodings. + */ + void setValue(storm::expressions::Variable const& metaVariable, int_fast64_t variableValue, double targetValue); + + /*! + * Sets the function values of all encodings that have the given values of the two meta variables to the + * given target value. + * + * @param metaVariable1 The first meta variable that has to be equal to the first given + * value. + * @param variableValue1 The value that the first meta variable is supposed to have. This must be within the + * range of the meta variable. + * @param metaVariable2 The second meta variable that has to be equal to the second given + * value. + * @param variableValue2 The value that the second meta variable is supposed to have. This must be within + * the range of the meta variable. + * @param targetValue The new function value of the modified encodings. + */ + void setValue(storm::expressions::Variable const& metaVariable1, int_fast64_t variableValue1, storm::expressions::Variable const& metaVariable2, int_fast64_t variableValue2, double targetValue); + + /*! + * Sets the function values of all encodings that have the given values of the given meta variables to the + * given target value. + * + * @param metaVariableToValueMap A mapping of meta variables to the values they are supposed to have. All + * values must be within the range of the respective meta variable. + * @param targetValue The new function value of the modified encodings. + */ + void setValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap = std::map<storm::expressions::Variable, int_fast64_t>(), double targetValue = 0); + + /*! + * Retrieves the value of the function when all meta variables are assigned the values of the given mapping. + * Note that the mapping must specify values for all meta variables contained in the DD. + * + * @param metaVariableToValueMap A mapping of meta variables to their values. + * @return The value of the function evaluated with the given input. + */ + double getValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap = std::map<storm::expressions::Variable, int_fast64_t>()) const; + + /*! + * Retrieves whether this DD represents the constant one function. + * + * @return True if this DD represents the constant one function. + */ + bool isOne() const; + + /*! + * Retrieves whether this DD represents the constant zero function. + * + * @return True if this DD represents the constant zero function. + */ + bool isZero() const; + + /*! + * Retrieves whether this DD represents a constant function. + * + * @return True if this DD represents a constants function. + */ + bool isConstant() const; + + /*! + * Retrieves the index of the topmost variable in the DD. + * + * @return The index of the topmost variable in DD. + */ + uint_fast64_t getIndex() const; + + /*! + * Converts the DD to a vector. + * + * @return The double vector that is represented by this DD. + */ + template<typename ValueType> + std::vector<ValueType> toVector() const; + + /*! + * Converts the DD to a vector. The given offset-labeled DD is used to determine the correct row of + * each entry. + * + * @param rowOdd The ODD used for determining the correct row. + * @return The double vector that is represented by this DD. + */ + template<typename ValueType> + std::vector<ValueType> toVector(storm::dd::Odd<DdType::CUDD> const& rowOdd) const; + + /*! + * Converts the DD to a (sparse) double matrix. All contained non-primed variables are assumed to encode the + * row, whereas all primed variables are assumed to encode the column. + * + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix() const; + + /*! + * Converts the DD to a (sparse) double matrix. All contained non-primed variables are assumed to encode the + * row, whereas all primed variables are assumed to encode the column. The given offset-labeled DDs are used + * to determine the correct row and column, respectively, for each entry. + * + * @param rowOdd The ODD used for determining the correct row. + * @param columnOdd The ODD used for determining the correct column. + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + + /*! + * Converts the DD to a (sparse) double matrix. The given offset-labeled DDs are used to determine the + * correct row and column, respectively, for each entry. + * + * @param rowMetaVariables The meta variables that encode the rows of the matrix. + * @param columnMetaVariables The meta variables that encode the columns of the matrix. + * @param rowOdd The ODD used for determining the correct row. + * @param columnOdd The ODD used for determining the correct column. + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + + /*! + * Converts the DD to a row-grouped (sparse) double matrix. The given offset-labeled DDs are used to + * determine the correct row and column, respectively, for each entry. Note: this function assumes that + * the meta variables used to distinguish different row groups are at the very top of the DD. + * + * @param rowMetaVariables The meta variables that encode the rows of the matrix. + * @param columnMetaVariables The meta variables that encode the columns of the matrix. + * @param groupMetaVariables The meta variables that are used to distinguish different row groups. + * @param rowOdd The ODD used for determining the correct row. + * @param columnOdd The ODD used for determining the correct column. + * @return The matrix that is represented by this DD. + */ + storm::storage::SparseMatrix<double> toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, std::set<storm::expressions::Variable> const& groupMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + + /*! + * Retrieves whether the given meta variable is contained in the DD. + * + * @param metaVariable The meta variable for which to query membership. + * @return True iff the meta variable is contained in the DD. + */ + bool containsMetaVariable(storm::expressions::Variable const& metaVariable) const; + + /*! + * Retrieves whether the given meta variables are all contained in the DD. + * + * @param metaVariables The meta variables for which to query membership. + * @return True iff all meta variables are contained in the DD. + */ + bool containsMetaVariables(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Retrieves the set of all meta variables contained in the DD. + * + * @return The set of all meta variables contained in the DD. + */ + std::set<storm::expressions::Variable> const& getContainedMetaVariables() const; + + /*! + * Retrieves the set of all meta variables contained in the DD. + * + * @return The set of all meta variables contained in the DD. + */ + std::set<storm::expressions::Variable>& getContainedMetaVariables(); + + /*! + * Exports the DD to the given file in the dot format. + * + * @param filename The name of the file to which the DD is to be exported. + */ + void exportToDot(std::string const& filename = "") const; + + /*! + * Retrieves the manager that is responsible for this DD. + * + * A pointer to the manager that is responsible for this DD. + */ + std::shared_ptr<DdManager<DdType::CUDD> const> getDdManager() const; + + /*! + * Retrieves an iterator that points to the first meta variable assignment with a non-zero function value. + * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. + * @return An iterator that points to the first meta variable assignment with a non-zero function value. + */ + DdForwardIterator<DdType::CUDD> begin(bool enumerateDontCareMetaVariables = true) const; + + /*! + * Retrieves an iterator that points past the end of the container. + * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. + * @return An iterator that points past the end of the container. + */ + DdForwardIterator<DdType::CUDD> end(bool enumerateDontCareMetaVariables = true) const; + + /*! + * Converts the DD into a (heavily nested) if-then-else expression that represents the very same function. + * The variable names used in the expression are derived from the meta variable name and are extended with a + * suffix ".i" if the meta variable is integer-valued, expressing that the variable is the i-th bit of the + * meta variable. + * + * @return The resulting expression. + */ + storm::expressions::Expression toExpression() const; + + /*! + * Converts the DD into a (heavily nested) if-then-else (with negations) expression that evaluates to true + * if and only if the assignment is minterm of the DD. The variable names used in the expression are derived + * from the meta variable name and are extended with a suffix ".i" if the meta variable is integer-valued, + * expressing that the variable is the i-th bit of the meta variable. + * + * @return The resulting expression. + */ + storm::expressions::Expression getMintermExpression() const; + + friend std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd); + + /*! + * Converts an MTBDD to a BDD by mapping all values unequal to zero to 1. This effectively does the same as + * a call to notZero(). + * + * @return The corresponding BDD. + */ + Dd<DdType::CUDD> toBdd() const; + + /*! + * Converts a BDD to an equivalent MTBDD. + * + * @return The corresponding MTBDD. + */ + Dd<DdType::CUDD> toMtbdd() const; + + /*! + * Retrieves whether this DD is a BDD. + * + * @return True iff this DD is a BDD. + */ + bool isBdd() const; + + /*! + * Retrieves whether this DD is an MTBDD. Even though MTBDDs technicall subsume BDDs, this will return false + * if the DD is actually a BDD. + * + * @return True iff this DD is an MTBDD. + */ + bool isMtbdd() const; + + private: + /*! + * Retrieves the CUDD BDD object associated with this DD. + * + * @return The CUDD BDD object associated with this DD. + */ + BDD getCuddBdd() const; + + /*! + * Retrieves the CUDD MTBDD object associated with this DD. + * + * @return The CUDD MTBDD object associated with this DD. + */ + ADD getCuddMtbdd() const; + + /*! + * Retrieves the CUDD object associated with this DD. + * + * @return The CUDD object associated with this DD. + */ + ABDD const& getCuddDd() const; + + /*! + * Retrieves the raw DD node of CUDD associated with this DD. + * + * @return The DD node of CUDD associated with this DD. + */ + DdNode* getCuddDdNode() const; + + /*! + * Adds the given meta variable to the set of meta variables that are contained in this DD. + * + * @param metaVariable The name of the meta variable to add. + */ + void addContainedMetaVariable(storm::expressions::Variable const& metaVariable); + + /*! + * Removes the given meta variable to the set of meta variables that are contained in this DD. + * + * @param metaVariable The name of the meta variable to remove. + */ + void removeContainedMetaVariable(storm::expressions::Variable const& metaVariable); + + /*! + * Performs the recursive step of toExpression on the given DD. + * + * @param dd The dd to translate into an expression. + * @param variables The variables to use in the expression. + * @return The resulting expression. + */ + static storm::expressions::Expression toExpressionRecur(DdNode const* dd, std::vector<storm::expressions::Variable> const& variables); + + /*! + * Performs the recursive step of getMintermExpression on the given DD. + * + * @param manager The manager of the DD. + * @param dd The dd whose minterms to translate into an expression. + * @param variables The variables to use in the expression. + * @return The resulting expression. + */ + static storm::expressions::Expression getMintermExpressionRecur(::DdManager* manager, DdNode const* dd, std::vector<storm::expressions::Variable> const& variables); + + /*! + * Creates a DD that encapsulates the given CUDD ADD. + * + * @param ddManager The manager responsible for this DD. + * @param cuddDd The CUDD ADD to store. + * @param containedMetaVariables The meta variables that appear in the DD. + */ + Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, ADD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>()); + + /*! + * Creates a DD that encapsulates the given CUDD ADD. + * + * @param ddManager The manager responsible for this DD. + * @param cuddDd The CUDD ADD to store. + * @param containedMetaVariables The meta variables that appear in the DD. + */ + Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, BDD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>()); + + /*! + * Helper function to convert the DD into a (sparse) matrix. + * + * @param dd The DD to convert. + * @param rowIndications A vector indicating at which position in the columnsAndValues vector the entries + * of row i start. Note: this vector is modified in the computation. More concretely, each entry i in the + * vector will be increased by the number of entries in the row. This can be used to count the number + * of entries in each row. If the values are not to be modified, a copy needs to be provided or the entries + * need to be restored afterwards. + * @param columnsAndValues The vector that will hold the columns and values of non-zero entries upon successful + * completion. + * @param rowGroupOffsets The row offsets at which a given row group starts. + * @param rowOdd The ODD used for the row translation. + * @param columnOdd The ODD used for the column translation. + * @param currentRowLevel The currently considered row level in the DD. + * @param currentColumnLevel The currently considered row level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param currentRowOffset The current row offset. + * @param currentColumnOffset The current row offset. + * @param ddRowVariableIndices The (sorted) indices of all DD row variables that need to be considered. + * @param ddColumnVariableIndices The (sorted) indices of all DD row variables that need to be considered. + * @param generateValues If set to true, the vector columnsAndValues is filled with the actual entries, which + * only works if the offsets given in rowIndications are already correct. If they need to be computed first, + * this flag needs to be false. + */ + void toMatrixRec(DdNode const* dd, std::vector<uint_fast64_t>& rowIndications, std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>>& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupOffsets, Odd<DdType::CUDD> const& rowOdd, Odd<DdType::CUDD> const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector<uint_fast64_t> const& ddRowVariableIndices, std::vector<uint_fast64_t> const& ddColumnVariableIndices, bool generateValues = true) const; + + /*! + * Splits the given matrix DD into the groups using the given group variables. + * + * @param dd The DD to split. + * @param groups A vector that is to be filled with the DDs for the individual groups. + * @param ddGroupVariableIndices The (sorted) indices of all DD group variables that need to be considered. + * @param currentLevel The currently considered level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param remainingMetaVariables The meta variables that remain in the DDs after the groups have been split. + */ + void splitGroupsRec(DdNode* dd, std::vector<Dd<DdType::CUDD>>& groups, std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set<storm::expressions::Variable> const& remainingMetaVariables) const; + + /*! + * Performs a recursive step to add the given DD-based vector to the given explicit vector. + * + * @param dd The DD to add to the explicit vector. + * @param currentLevel The currently considered level in the DD. + * @param maxLevel The number of levels that need to be considered. + * @param currentOffset The current offset. + * @param odd The ODD used for the translation. + * @param ddVariableIndices The (sorted) indices of all DD variables that need to be considered. + * @param targetVector The vector to which the translated DD-based vector is to be added. + */ + template<typename ValueType> + void addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<ValueType>& targetVector) const; + + /*! + * Retrieves the indices of all DD variables that are contained in this DD (not necessarily in the support, + * because they could be "don't cares"). Additionally, the indices are sorted to allow for easy access. + * + * @return The (sorted) indices of all DD variables that are contained in this DD. + */ + std::vector<uint_fast64_t> getSortedVariableIndices() const; + + // A pointer to the manager responsible for this DD. + std::shared_ptr<DdManager<DdType::CUDD> const> ddManager; + + // The ADD created by CUDD. + boost::variant<BDD, ADD> cuddDd; + + // The meta variables that appear in this DD. + std::set<storm::expressions::Variable> containedMetaVariables; + }; + } +} + +#endif /* STORM_STORAGE_DD_CUDDDD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp new file mode 100644 index 000000000..dd095af21 --- /dev/null +++ b/src/storage/dd/CuddBdd.cpp @@ -0,0 +1,321 @@ +#include <cstring> +#include <algorithm> + +#include "src/storage/dd/CuddBdd.h" +#include "src/storage/dd/CuddAdd.h" +#include "src/storage/dd/CuddDdManager.h" + +namespace storm { + namespace dd { + Bdd<DdType::CUDD>::Bdd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, BDD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables) : Dd<DdType::CUDD>(ddManager, containedMetaVariables) { + // Intentionally left empty. + } + + Add<DdType::CUDD> Bdd<DdType::CUDD>::toAdd() const { + return Add<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Add(), this->getContainedMetaVariables()); + } + + BDD Bdd<DdType::CUDD>::getCuddBdd() const { + return this->cuddBdd; + } + + DdNode* Bdd<DdType::CUDD>::getCuddDdNode() const { + return this->getCuddBdd().getNode(); + } + + bool Bdd<DdType::CUDD>::operator==(Bdd<DdType::CUDD> const& other) const { + return this->getCuddBdd() == other.getCuddBdd(); + } + + bool Bdd<DdType::CUDD>::operator!=(Bdd<DdType::CUDD> const& other) const { + return !(*this == other); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::ite(Bdd<DdType::CUDD> const& thenDd, Bdd<DdType::CUDD> const& elseDd) const { + std::set<storm::expressions::Variable> 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<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Ite(thenDd.getCuddBdd(), elseDd.getCuddBdd()), metaVariableNames); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::operator!() const { + Bdd<DdType::CUDD> result(*this); + result.complement(); + return result; + } + + Bdd<DdType::CUDD>& Bdd<DdType::CUDD>::complement() { + this->cuddBdd = ~this->getCuddBdd(); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::operator&&(Bdd<DdType::CUDD> const& other) const { + Dd<DdType::CUDD> result(*this); + result &= other; + return result; + } + + Bdd<DdType::CUDD>& Bdd<DdType::CUDD>::operator&=(Bdd<DdType::CUDD> const& other) { + this->addMetaVariables(other.getContainedMetaVariables()); + this->cuddBdd = this->getCuddBdd() & other.getCuddBdd(); + return *this; + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::operator||(Bdd<DdType::CUDD> const& other) const { + Bdd<DdType::CUDD> result(*this); + result |= other; + return result; + } + + Bdd<DdType::CUDD>& Bdd<DdType::CUDD>::operator|=(Bdd<DdType::CUDD> const& other) { + this->addMetaVariables(other.getContainedMetaVariables()); + this->cuddBdd = this->getCuddBdd() | other.getCuddBdd(); + return *this; + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::iff(Bdd<DdType::CUDD> const& other) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xnor(other.getCuddBdd()), metaVariables); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::exclusiveOr(Bdd<DdType::CUDD> const& other) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xor(other.getCuddBdd()), metaVariables); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::implies(Bdd<DdType::CUDD> const& other) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); + return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Ite(other.getCuddBdd(), this->getDdManager()->getBddOne().getCuddBdd()), metaVariables); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::existsAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getBddOne()); + + std::set<storm::expressions::Variable> 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().ExistAbstract(cubeDd.getCuddBdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Bdd<DdType::CUDD>::universalAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getBddOne()); + + std::set<storm::expressions::Variable> 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().UnivAbstract(cubeDd.getCuddBdd()), newMetaVariables); + } + + Dd<DdType::CUDD> Bdd<DdType::CUDD>::swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs) { + std::set<storm::expressions::Variable> newContainedMetaVariables; + std::vector<BDD> from; + std::vector<BDD> to; + for (auto const& metaVariablePair : metaVariablePairs) { + DdMetaVariable<DdType::CUDD> const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); + DdMetaVariable<DdType::CUDD> 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<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().SwapVariables(from, to), newContainedMetaVariables); + } + + Bdd<DdType::CUDD> Bdd<DdType::CUDD>::andExists(Bdd<DdType::CUDD> const& other, std::set<storm::expressions::Variable> const& existentialVariables) const { + STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operation on MTBDD(s)."); + + Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); + for (auto const& metaVariable : existentialVariables) { + DdMetaVariable<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + std::set<storm::expressions::Variable> unionOfMetaVariables; + std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); + std::set<storm::expressions::Variable> containedMetaVariables; + std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), existentialVariables.begin(), existentialVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); + + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().AndAbstract(other.getCuddBdd(), cubeDd.getCuddBdd()), containedMetaVariables); + } + + Dd<DdType::CUDD> Bdd<DdType::CUDD>::constrain(Dd<DdType::CUDD> const& constraint) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end()); + + if (this->isBdd() && constraint.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Constrain(constraint.getCuddBdd()), metaVariables); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Constrain(constraint.getCuddMtbdd()), metaVariables); + } + } + + Dd<DdType::CUDD> Bdd<DdType::CUDD>::restrict(Dd<DdType::CUDD> const& constraint) const { + std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); + metaVariables.insert(constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end()); + + if (this->isBdd() && constraint.isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Restrict(constraint.getCuddBdd()), metaVariables); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Restrict(constraint.getCuddMtbdd()), metaVariables); + } + } + + Dd<DdType::CUDD> Bdd<DdType::CUDD>::getSupport() const { + if (this->isBdd()) { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Support(), this->getContainedMetaVariables()); + } else { + return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Support(), this->getContainedMetaVariables()); + } + } + + uint_fast64_t Bdd<DdType::CUDD>::getNonZeroCount() const { + std::size_t numberOfDdVariables = 0; + for (auto const& metaVariable : this->getContainedMetaVariables()) { + numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariable).getNumberOfDdVariables(); + } + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().CountMinterm(static_cast<int>(numberOfDdVariables))); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().CountMinterm(static_cast<int>(numberOfDdVariables))); + } + } + + uint_fast64_t Bdd<DdType::CUDD>::getLeafCount() const { + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().CountLeaves()); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().CountLeaves()); + } + } + + uint_fast64_t Bdd<DdType::CUDD>::getNodeCount() const { + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().nodeCount()); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().nodeCount()); + } + } + + bool Bdd<DdType::CUDD>::isOne() const { + if (this->isBdd()) { + return this->getCuddBdd().IsOne(); + } else { + return this->getCuddMtbdd().IsOne(); + } + } + + bool Bdd<DdType::CUDD>::isZero() const { + if (this->isBdd()) { + return this->getCuddBdd().IsZero(); + } else { + return this->getCuddMtbdd().IsZero(); + } + } + + uint_fast64_t Bdd<DdType::CUDD>::getIndex() const { + if (this->isBdd()) { + return static_cast<uint_fast64_t>(this->getCuddBdd().NodeReadIndex()); + } else { + return static_cast<uint_fast64_t>(this->getCuddMtbdd().NodeReadIndex()); + } + } + + std::vector<uint_fast64_t> Bdd<DdType::CUDD>::getSortedVariableIndices() const { + std::vector<uint_fast64_t> ddVariableIndices; + for (auto const& metaVariableName : this->getContainedMetaVariables()) { + auto const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); + for (auto const& ddVariable : metaVariable.getDdVariables()) { + ddVariableIndices.push_back(ddVariable.getIndex()); + } + } + + // Next, we need to sort them, since they may be arbitrarily ordered otherwise. + std::sort(ddVariableIndices.begin(), ddVariableIndices.end()); + return ddVariableIndices; + } + + void Bdd<DdType::CUDD>::exportToDot(std::string const& filename) const { + if (filename.empty()) { + if (this->isBdd()) { + std::vector<BDD> cuddBddVector = { this->getCuddBdd() }; + this->getDdManager()->getCuddManager().DumpDot(cuddBddVector); + } else { + std::vector<ADD> cuddAddVector = { this->getCuddMtbdd() }; + this->getDdManager()->getCuddManager().DumpDot(cuddAddVector); + } + } else { + // Build the name input of the DD. + std::vector<char*> 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<std::string> ddVariableNamesAsStrings = this->getDdManager()->getDdVariableNames(); + std::vector<char*> 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"); + if (this->isBdd()) { + std::vector<BDD> cuddBddVector = { this->getCuddBdd() }; + this->getDdManager()->getCuddManager().DumpDot(cuddBddVector, &ddVariableNames[0], &ddNames[0], filePointer); + } else { + std::vector<ADD> cuddAddVector = { this->getCuddMtbdd() }; + this->getDdManager()->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; + } + } + } + + std::ostream& operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) { + dd.exportToDot(); + return out; + } + } +} \ No newline at end of file diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h new file mode 100644 index 000000000..19335a3f4 --- /dev/null +++ b/src/storage/dd/CuddBdd.h @@ -0,0 +1,275 @@ +#ifndef STORM_STORAGE_DD_CUDDBDD_H_ +#define STORM_STORAGE_DD_CUDDBDD_H_ + +#include "src/storage/dd/Bdd.h" +#include "src/storage/dd/CuddDd.h" +#include "src/storage/dd/CuddAdd.h" +#include "src/storage/expressions/Variable.h" +#include "src/utility/OsDetection.h" + +// Include the C++-interface of CUDD. +#include "cuddObj.hh" + +namespace storm { + namespace dd { + // Forward-declare some classes. + template<DdType Type> class DdManager; + template<DdType Type> class Odd; + template<DdType Type> class Add; + + template<> + class Bdd<DdType::CUDD> : public Dd<DdType::CUDD> { + public: + // Declare the DdManager and DdIterator class as friend so it can access the internals of a DD. + friend class DdManager<DdType::CUDD>; + friend class DdForwardIterator<DdType::CUDD>; + friend class Odd<DdType::CUDD>; + + // Instantiate all copy/move constructors/assignments with the default implementation. + Bdd() = default; + Bdd(Bdd<DdType::CUDD> const& other) = default; + Bdd& operator=(Bdd<DdType::CUDD> const& other) = default; +#ifndef WINDOWS + Bdd(Bdd<DdType::CUDD>&& other) = default; + Bdd& operator=(Bdd<DdType::CUDD>&& other) = default; +#endif + + /*! + * Retrieves whether the two BDDs represent the same function. + * + * @param other The BDD that is to be compared with the current one. + * @return True if the BDDs represent the same function. + */ + bool operator==(Bdd<DdType::CUDD> const& other) const; + + /*! + * Retrieves whether the two BDDs represent different functions. + * + * @param other The BDD that is to be compared with the current one. + * @return True if the BDDs represent the different functions. + */ + bool operator!=(Bdd<DdType::CUDD> const& other) const; + + /*! + * Performs an if-then-else with the given operands, i.e. maps all valuations that are mapped to a non-zero + * function value to the function values specified by the first DD and all others to the function values + * specified by the second DD. + * + * @param thenBdd The BDD defining the 'then' part. + * @param elseBdd The BDD defining the 'else' part. + * @return The resulting BDD. + */ + Bdd<DdType::CUDD> ite(Bdd<DdType::CUDD> const& thenBdd, Bdd<DdType::CUDD> const& elseBdd) const; + + /*! + * Performs a logical or of the current and the given BDD. + * + * @param other The second BDD used for the operation. + * @return The logical or of the operands. + */ + Bdd<DdType::CUDD> operator||(Bdd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical or of the current and the given BDD and assigns it to the current BDD. + * + * @param other The second BDD used for the operation. + * @return A reference to the current BDD after the operation + */ + Bdd<DdType::CUDD>& operator|=(Bdd<DdType::CUDD> const& other); + + /*! + * Performs a logical and of the current and the given BDD. + * + * @param other The second BDD used for the operation. + * @return The logical and of the operands. + */ + Bdd<DdType::CUDD> operator&&(Bdd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical and of the current and the given BDD and assigns it to the current BDD. + * + * @param other The second BDD used for the operation. + * @return A reference to the current BDD after the operation + */ + Bdd<DdType::CUDD>& operator&=(Bdd<DdType::CUDD> const& other); + + /*! + * Performs a logical iff of the current and the given BDD. + * + * @param other The second BDD used for the operation. + * @return The logical iff of the operands. + */ + Bdd<DdType::CUDD> iff(Bdd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical exclusive-or of the current and the given BDD. + * + * @param other The second BDD used for the operation. + * @return The logical exclusive-or of the operands. + */ + Bdd<DdType::CUDD> exclusiveOr(Bdd<DdType::CUDD> const& other) const; + + /*! + * Performs a logical implication of the current and the given BDD. + * + * @param other The second BDD used for the operation. + * @return The logical implication of the operands. + */ + Bdd<DdType::CUDD> implies(Bdd<DdType::CUDD> const& other) const; + + /*! + * Logically inverts the current BDD. + * + * @return The resulting BDD. + */ + Bdd<DdType::CUDD> operator!() const; + + /*! + * Logically complements the current BDD. + * + * @return A reference to the current BDD after the operation. + */ + Bdd<DdType::CUDD>& complement(); + + /*! + * Existentially abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Bdd<DdType::CUDD> existsAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Universally abstracts from the given meta variables. + * + * @param metaVariables The meta variables from which to abstract. + */ + Bdd<DdType::CUDD> universalAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; + + /*! + * Swaps the given pairs of meta variables in the BDD. The pairs of meta variables must be guaranteed to have + * the same number of underlying BDD variables. + * + * @param metaVariablePairs A vector of meta variable pairs that are to be swapped for one another. + * @return The resulting BDD. + */ + Bdd<DdType::CUDD> swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs); + + /*! + * Computes the logical and of the current and the given BDD and existentially abstracts from the given set + * of variables. + * + * @param other The second BDD for the logical and. + * @param existentialVariables The variables from which to existentially abstract. + * @return A BDD representing the result. + */ + Bdd<DdType::CUDD> andExists(Bdd<DdType::CUDD> const& other, std::set<storm::expressions::Variable> const& existentialVariables) const; + + /*! + * Computes the constraint of the current BDD with the given constraint. That is, the function value of the + * resulting BDD will be the same as the current ones for all assignments mapping to one in the constraint + * and may be different otherwise. + * + * @param constraint The constraint to use for the operation. + * @return The resulting BDD. + */ + Bdd<DdType::CUDD> constrain(Bdd<DdType::CUDD> const& constraint) const; + + /*! + * Computes the restriction of the current BDD with the given constraint. That is, the function value of the + * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint + * and may be different otherwise. + * + * @param constraint The constraint to use for the operation. + * @return The resulting BDD. + */ + Bdd<DdType::CUDD> restrict(Bdd<DdType::CUDD> const& constraint) const; + + /*! + * Retrieves the support of the current BDD. + * + * @return The support represented as a BDD. + */ + virtual Bdd<DdType::CUDD> getSupport() const override; + + /*! + * Retrieves the number of encodings that are mapped to a non-zero value. + * + * @return The number of encodings that are mapped to a non-zero value. + */ + virtual uint_fast64_t getNonZeroCount() const override; + + /*! + * Retrieves the number of nodes necessary to represent the DD. + * + * @return The number of nodes in this DD. + */ + virtual uint_fast64_t getNodeCount() const override; + + /*! + * Retrieves whether this DD represents the constant one function. + * + * @return True if this DD represents the constant one function. + */ + bool isOne() const; + + /*! + * Retrieves whether this DD represents the constant zero function. + * + * @return True if this DD represents the constant zero function. + */ + bool isZero() const; + + /*! + * Retrieves the index of the topmost variable in the BDD. + * + * @return The index of the topmost variable in BDD. + */ + virtual uint_fast64_t getIndex() const override; + + /*! + * Exports the BDD to the given file in the dot format. + * + * @param filename The name of the file to which the BDD is to be exported. + */ + virtual void exportToDot(std::string const& filename = "") const override; + + friend std::ostream & operator<<(std::ostream& out, const Bdd<DdType::CUDD>& bdd); + + /*! + * Converts a BDD to an equivalent ADD. + * + * @return The corresponding ADD. + */ + Add<DdType::CUDD> toAdd() const; + + private: + /*! + * Retrieves the CUDD BDD object associated with this DD. + * + * @return The CUDD BDD object associated with this DD. + */ + BDD getCuddBdd() const; + + /*! + * Retrieves the raw DD node of CUDD associated with this BDD. + * + * @return The DD node of CUDD associated with this BDD. + */ + DdNode* getCuddDdNode() const; + + /*! + * Creates a DD that encapsulates the given CUDD ADD. + * + * @param ddManager The manager responsible for this DD. + * @param cuddDd The CUDD ADD to store. + * @param containedMetaVariables The meta variables that appear in the DD. + */ + Bdd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, BDD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>()); + + // The BDD created by CUDD. + BDD cuddBdd; + }; + } +} + +#endif /* STORM_STORAGE_DD_CUDDBDD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddDd.cpp b/src/storage/dd/CuddDd.cpp index a169403d0..3ec7328f4 100644 --- a/src/storage/dd/CuddDd.cpp +++ b/src/storage/dd/CuddDd.cpp @@ -1,1040 +1,20 @@ -#include <cstring> #include <algorithm> #include "src/storage/dd/CuddDd.h" -#include "src/storage/dd/CuddOdd.h" #include "src/storage/dd/CuddDdManager.h" -#include "src/utility/vector.h" -#include "src/utility/macros.h" - -#include "src/exceptions/IllegalFunctionCallException.h" -#include "src/exceptions/InvalidArgumentException.h" -#include "src/exceptions/NotImplementedException.h" namespace storm { namespace dd { - Dd<DdType::CUDD>::Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, BDD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables) : ddManager(ddManager), cuddDd(cuddDd), containedMetaVariables(containedMetaVariables) { - // Intentionally left empty. - } - - Dd<DdType::CUDD>::Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, ADD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables) : ddManager(ddManager), cuddDd(cuddDd), containedMetaVariables(containedMetaVariables) { + Dd<DdType::CUDD>::Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, std::set<storm::expressions::Variable> const& containedMetaVariables) : ddManager(ddManager), containedMetaVariables(containedMetaVariables) { // Intentionally left empty. } - - bool Dd<DdType::CUDD>::isBdd() const { - return this->cuddDd.which() == 0; - } - - bool Dd<DdType::CUDD>::isMtbdd() const { - return this->cuddDd.which() == 1; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::toBdd() const { - if (this->isBdd()) { - return *this; - } else { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().BddPattern(), this->containedMetaVariables); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::toMtbdd() const { - if (this->isMtbdd()) { - return *this; - } else { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Add(), this->containedMetaVariables); - } - } - - BDD Dd<DdType::CUDD>::getCuddBdd() const { - if (this->isBdd()) { - return boost::get<BDD>(this->cuddDd); - } else { - STORM_LOG_WARN_COND(false, "Implicitly converting MTBDD to BDD."); - return this->getCuddMtbdd().BddPattern(); - } - } - - ADD Dd<DdType::CUDD>::getCuddMtbdd() const { - if (this->isMtbdd()) { - return boost::get<ADD>(this->cuddDd); - } else { - STORM_LOG_WARN_COND(false, "Implicitly converting BDD to MTBDD."); - return this->getCuddBdd().Add(); - } - } - - ABDD const& Dd<DdType::CUDD>::getCuddDd() const { - if (this->isBdd()) { - return static_cast<ABDD const&>(boost::get<BDD>(this->cuddDd)); - } else { - return static_cast<ABDD const&>(boost::get<ADD>(this->cuddDd)); - } - } - - DdNode* Dd<DdType::CUDD>::getCuddDdNode() const { - if (this->isBdd()) { - return this->getCuddBdd().getNode(); - } else { - return this->getCuddMtbdd().getNode(); - } - } - - bool Dd<DdType::CUDD>::operator==(Dd<DdType::CUDD> const& other) const { - if (this->isBdd()) { - if (other.isBdd()) { - return this->getCuddBdd() == other.getCuddBdd(); - } else { - return false; - } - } else { - if (other.isMtbdd()) { - return this->getCuddMtbdd() == other.getCuddMtbdd(); - } else { - return false; - } - } - } - - bool Dd<DdType::CUDD>::operator!=(Dd<DdType::CUDD> const& other) const { - return !(*this == other); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::ite(Dd<DdType::CUDD> const& thenDd, Dd<DdType::CUDD> const& elseDd) const { - std::set<storm::expressions::Variable> metaVariableNames(this->getContainedMetaVariables()); - metaVariableNames.insert(thenDd.getContainedMetaVariables().begin(), thenDd.getContainedMetaVariables().end()); - metaVariableNames.insert(elseDd.getContainedMetaVariables().begin(), elseDd.getContainedMetaVariables().end()); - - // If all involved DDs are BDDs, the result is also going to be a BDD. - if (this->isBdd() && thenDd.isBdd() && elseDd.isBdd()) { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Ite(thenDd.getCuddBdd(), elseDd.getCuddBdd()), metaVariableNames); - } else { - return Dd<DdType::CUDD>(this->getDdManager(), this->toMtbdd().getCuddMtbdd().Ite(thenDd.toMtbdd().getCuddMtbdd(), elseDd.toMtbdd().getCuddMtbdd()), metaVariableNames); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator+(Dd<DdType::CUDD> const& other) const { - Dd<DdType::CUDD> result(*this); - result += other; - return result; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator+=(Dd<DdType::CUDD> const& other) { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing addition on BDDs."); - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - this->cuddDd = this->getCuddMtbdd() + other.getCuddMtbdd(); - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator*(Dd<DdType::CUDD> const& other) const { - Dd<DdType::CUDD> result(*this); - result *= other; - return result; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator*=(Dd<DdType::CUDD> const& other) { - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - if (this->isMtbdd() && other.isMtbdd()) { - this->cuddDd = this->getCuddMtbdd() * other.getCuddMtbdd(); - } else if (this->isBdd() && other.isBdd()) { - this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); - } else { - STORM_LOG_WARN_COND(false, "Performing multiplication on two DDs of different type."); - this->cuddDd = this->getCuddMtbdd() * other.getCuddMtbdd(); - } - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator-(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - Dd<DdType::CUDD> result(*this); - result -= other; - return result; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator-() const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - return this->getDdManager()->getZero(true) - *this; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator-=(Dd<DdType::CUDD> const& other) { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - this->cuddDd = this->getCuddMtbdd() - other.getCuddMtbdd(); - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator/(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - Dd<DdType::CUDD> result(*this); - result /= other; - return result; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator/=(Dd<DdType::CUDD> const& other) { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - this->cuddDd = this->getCuddMtbdd().Divide(other.getCuddMtbdd()); - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator!() const { - Dd<DdType::CUDD> result(*this); - result.complement(); - return result; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::complement() { - if (this->isBdd()) { - this->cuddDd = ~this->getCuddBdd(); - } else { - this->cuddDd = ~this->getCuddMtbdd(); - } - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator&&(Dd<DdType::CUDD> const& other) const { - Dd<DdType::CUDD> result(*this); - result &= other; - return result; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator&=(Dd<DdType::CUDD> const& other) { - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - if (this->isBdd() && other.isBdd()) { - this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); - } else if (this->isMtbdd() && other.isMtbdd()) { - // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that - // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. - this->cuddDd = this->getCuddMtbdd() & other.getCuddMtbdd(); - } else { - STORM_LOG_WARN_COND(false, "Performing logical and on two DDs of different type."); - this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); - } - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::operator||(Dd<DdType::CUDD> const& other) const { - Dd<DdType::CUDD> result(*this); - result |= other; - return result; - } - - Dd<DdType::CUDD>& Dd<DdType::CUDD>::operator|=(Dd<DdType::CUDD> const& other) { - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - if (this->isBdd() && other.isBdd()) { - this->cuddDd = this->getCuddBdd() | other.getCuddBdd(); - } else if (this->isMtbdd() && other.isMtbdd()) { - // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that - // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. - this->cuddDd = this->getCuddMtbdd() | other.getCuddMtbdd(); - } else { - STORM_LOG_WARN_COND(false, "Performing logical and on two DDs of different type."); - this->cuddDd = this->getCuddBdd() & other.getCuddBdd(); - } - return *this; - - STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operation on MTBDD(s)."); - this->cuddDd = this->getCuddBdd() | other.getCuddBdd(); - this->getContainedMetaVariables().insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return *this; - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::iff(Dd<DdType::CUDD> const& other) const { - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - if (this->isBdd() && other.isBdd()) { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xnor(other.getCuddBdd()), metaVariables); - } else if (this->isMtbdd() && other.isMtbdd()) { - // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that - // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Xnor(other.getCuddMtbdd()), metaVariables); - } else { - STORM_LOG_WARN_COND(false, "Performing logical iff on two DDs of different type."); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xnor(other.getCuddBdd()), metaVariables); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::exclusiveOr(Dd<DdType::CUDD> const& other) const { - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - if (this->isBdd() && other.isBdd()) { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xor(other.getCuddBdd()), metaVariables); - } else if (this->isMtbdd() && other.isMtbdd()) { - // We also tolerate (without a warning) if the two arguments are MTBDDs. The caller has to make sure that - // the two given DDs are 0-1-MTBDDs, because the operation may produce wrong results otherwise. - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Xor(other.getCuddMtbdd()), metaVariables); - } else { - STORM_LOG_WARN_COND(false, "Performing logical iff on two DDs of different type."); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Xor(other.getCuddBdd()), metaVariables); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::implies(Dd<DdType::CUDD> const& other) const { - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operatiorn on MTBDDs."); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Ite(other.getCuddBdd(), this->getDdManager()->getOne().getCuddBdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::equals(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Equals(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::notEquals(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().NotEquals(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::less(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().LessThan(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::lessOrEqual(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().LessThanOrEqual(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::greater(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().GreaterThan(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::greaterOrEqual(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().GreaterThanOrEqual(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::minimum(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Minimum(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::maximum(Dd<DdType::CUDD> const& other) const { - STORM_LOG_WARN_COND(this->isMtbdd() && other.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end()); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Maximum(other.getCuddMtbdd()), metaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::existsAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { - STORM_LOG_WARN_COND(this->isBdd(), "Performing logical operation on MTBDD(s)."); - - Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(false)); - std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); - for (auto const& metaVariable : metaVariables) { - // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); - cubeDd &= ddMetaVariable.getCube(); - } - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().ExistAbstract(cubeDd.getCuddBdd()), newMetaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::universalAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { - STORM_LOG_WARN_COND(this->isBdd(), "Performing logical operation on MTBDD(s)."); - - Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); - std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); - for (auto const& metaVariable : metaVariables) { - // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); - cubeDd &= ddMetaVariable.getCube(); - } - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().UnivAbstract(cubeDd.getCuddBdd()), newMetaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::sumAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(true)); - std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); - for (auto const& metaVariable : metaVariables) { - // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); - cubeDd *= ddMetaVariable.getCubeAsMtbdd(); - } - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().ExistAbstract(cubeDd.getCuddMtbdd()), newMetaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::minAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(true)); - std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); - for (auto const& metaVariable : metaVariables) { - // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); - cubeDd *= ddMetaVariable.getCubeAsMtbdd(); - } - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().MinAbstract(cubeDd.getCuddMtbdd()), newMetaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::maxAbstract(std::set<storm::expressions::Variable> const& metaVariables) const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne(true)); - std::set<storm::expressions::Variable> newMetaVariables = this->getContainedMetaVariables(); - for (auto const& metaVariable : metaVariables) { - // First check whether the DD 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<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); - cubeDd *= ddMetaVariable.getCubeAsMtbdd(); - } - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().MaxAbstract(cubeDd.getCuddMtbdd()), newMetaVariables); - } - - bool Dd<DdType::CUDD>::equalModuloPrecision(Dd<DdType::CUDD> const& other, double precision, bool relative) const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - if (relative) { - return this->getCuddMtbdd().EqualSupNormRel(other.getCuddMtbdd(), precision); - } else { - return this->getCuddMtbdd().EqualSupNorm(other.getCuddMtbdd(), precision); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs) { - std::set<storm::expressions::Variable> newContainedMetaVariables; - if (this->isBdd()) { - std::vector<BDD> from; - std::vector<BDD> to; - for (auto const& metaVariablePair : metaVariablePairs) { - DdMetaVariable<DdType::CUDD> const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); - DdMetaVariable<DdType::CUDD> 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 Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().SwapVariables(from, to), newContainedMetaVariables); - } else { - std::vector<ADD> from; - std::vector<ADD> to; - for (auto const& metaVariablePair : metaVariablePairs) { - DdMetaVariable<DdType::CUDD> const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); - DdMetaVariable<DdType::CUDD> const& variable2 = this->getDdManager()->getMetaVariable(metaVariablePair.second); - - // Check if it's legal so swap the meta variables. - if (variable1.getNumberOfDdVariables() != variable2.getNumberOfDdVariables()) { - throw 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.getCuddMtbdd()); - } - for (auto const& ddVariable : variable2.getDdVariables()) { - to.push_back(ddVariable.getCuddMtbdd()); - } - } - - // Finally, call CUDD to swap the variables. - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().SwapVariables(from, to), newContainedMetaVariables); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::multiplyMatrix(Dd<DdType::CUDD> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const { - STORM_LOG_WARN_COND(this->isMtbdd() && otherMatrix.isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - // Create the CUDD summation variables. - std::vector<ADD> summationDdVariables; - for (auto const& metaVariable : summationMetaVariables) { - for (auto const& ddVariable : this->getDdManager()->getMetaVariable(metaVariable).getDdVariables()) { - summationDdVariables.push_back(ddVariable.getCuddMtbdd()); - } - } - - std::set<storm::expressions::Variable> unionOfMetaVariables; - std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), otherMatrix.getContainedMetaVariables().begin(), otherMatrix.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); - std::set<storm::expressions::Variable> containedMetaVariables; - std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), summationMetaVariables.begin(), summationMetaVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().MatrixMultiply(otherMatrix.getCuddMtbdd(), summationDdVariables), containedMetaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::andExists(Dd<DdType::CUDD> const& other, std::set<storm::expressions::Variable> const& existentialVariables) const { - STORM_LOG_WARN_COND(this->isBdd() && other.isBdd(), "Performing logical operation on MTBDD(s)."); - - Dd<DdType::CUDD> cubeDd(this->getDdManager()->getOne()); - for (auto const& metaVariable : existentialVariables) { - DdMetaVariable<DdType::CUDD> const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); - cubeDd &= ddMetaVariable.getCube(); - } - - std::set<storm::expressions::Variable> unionOfMetaVariables; - std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); - std::set<storm::expressions::Variable> containedMetaVariables; - std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), existentialVariables.begin(), existentialVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); - - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().AndAbstract(other.getCuddBdd(), cubeDd.getCuddBdd()), containedMetaVariables); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::greater(double value) const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().BddStrictThreshold(value), this->getContainedMetaVariables()); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::greaterOrEqual(double value) const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().BddThreshold(value), this->getContainedMetaVariables()); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::notZero() const { - return this->toBdd(); - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::constrain(Dd<DdType::CUDD> const& constraint) const { - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end()); - - if (this->isBdd() && constraint.isBdd()) { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Constrain(constraint.getCuddBdd()), metaVariables); - } else { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Constrain(constraint.getCuddMtbdd()), metaVariables); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::restrict(Dd<DdType::CUDD> const& constraint) const { - std::set<storm::expressions::Variable> metaVariables(this->getContainedMetaVariables()); - metaVariables.insert(constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end()); - - if (this->isBdd() && constraint.isBdd()) { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Restrict(constraint.getCuddBdd()), metaVariables); - } else { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Restrict(constraint.getCuddMtbdd()), metaVariables); - } - } - - Dd<DdType::CUDD> Dd<DdType::CUDD>::getSupport() const { - if (this->isBdd()) { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddBdd().Support(), this->getContainedMetaVariables()); - } else { - return Dd<DdType::CUDD>(this->getDdManager(), this->getCuddMtbdd().Support(), this->getContainedMetaVariables()); - } - } - - uint_fast64_t Dd<DdType::CUDD>::getNonZeroCount() const { - std::size_t numberOfDdVariables = 0; - for (auto const& metaVariable : this->getContainedMetaVariables()) { - numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariable).getNumberOfDdVariables(); - } - if (this->isBdd()) { - return static_cast<uint_fast64_t>(this->getCuddBdd().CountMinterm(static_cast<int>(numberOfDdVariables))); - } else { - return static_cast<uint_fast64_t>(this->getCuddMtbdd().CountMinterm(static_cast<int>(numberOfDdVariables))); - } - } - - uint_fast64_t Dd<DdType::CUDD>::getLeafCount() const { - if (this->isBdd()) { - return static_cast<uint_fast64_t>(this->getCuddBdd().CountLeaves()); - } else { - return static_cast<uint_fast64_t>(this->getCuddMtbdd().CountLeaves()); - } - } - - uint_fast64_t Dd<DdType::CUDD>::getNodeCount() const { - if (this->isBdd()) { - return static_cast<uint_fast64_t>(this->getCuddBdd().nodeCount()); - } else { - return static_cast<uint_fast64_t>(this->getCuddMtbdd().nodeCount()); - } - } - - double Dd<DdType::CUDD>::getMin() const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - ADD constantMinAdd = this->getCuddMtbdd().FindMin(); - return static_cast<double>(Cudd_V(constantMinAdd.getNode())); - } - - double Dd<DdType::CUDD>::getMax() const { - STORM_LOG_WARN_COND(this->isMtbdd(), "Performing arithmetical operation on BDD(s)."); - - ADD constantMaxAdd = this->getCuddMtbdd().FindMax(); - return static_cast<double>(Cudd_V(constantMaxAdd.getNode())); - } - - void Dd<DdType::CUDD>::setValue(storm::expressions::Variable const& metaVariable, int_fast64_t variableValue, double targetValue) { - std::map<storm::expressions::Variable, int_fast64_t> metaVariableToValueMap; - metaVariableToValueMap.emplace(metaVariable, variableValue); - this->setValue(metaVariableToValueMap, targetValue); - } - - void Dd<DdType::CUDD>::setValue(storm::expressions::Variable const& metaVariable1, int_fast64_t variableValue1, storm::expressions::Variable const& metaVariable2, int_fast64_t variableValue2, double targetValue) { - std::map<storm::expressions::Variable, int_fast64_t> metaVariableToValueMap; - metaVariableToValueMap.emplace(metaVariable1, variableValue1); - metaVariableToValueMap.emplace(metaVariable2, variableValue2); - this->setValue(metaVariableToValueMap, targetValue); - } - - void Dd<DdType::CUDD>::setValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap, double targetValue) { - if (this->isBdd()) { - STORM_LOG_THROW(targetValue == storm::utility::zero<double>() || targetValue == storm::utility::one<double>(), storm::exceptions::InvalidArgumentException, "Cannot set encoding to non-0, non-1 value " << targetValue << " in BDD."); - Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); - for (auto const& nameValuePair : metaVariableToValueMap) { - valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); - // Also record that the DD now contains the meta variable. - this->addContainedMetaVariable(nameValuePair.first); - } - if (targetValue == storm::utility::one<double>()) { - this->cuddDd = valueEncoding.getCuddBdd().Ite(this->getDdManager()->getOne().getCuddBdd(), this->getCuddBdd()); - } else { - this->cuddDd = valueEncoding.getCuddBdd().Ite(this->getDdManager()->getZero().getCuddBdd(), this->getCuddBdd()); - } - } else { - Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); - for (auto const& nameValuePair : metaVariableToValueMap) { - valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); - // Also record that the DD now contains the meta variable. - this->addContainedMetaVariable(nameValuePair.first); - } - this->cuddDd = valueEncoding.toMtbdd().getCuddMtbdd().Ite(this->getDdManager()->getConstant(targetValue).getCuddMtbdd(), this->getCuddMtbdd()); - } - } - - double Dd<DdType::CUDD>::getValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap) const { - std::set<storm::expressions::Variable> remainingMetaVariables(this->getContainedMetaVariables()); - Dd<DdType::CUDD> valueEncoding(this->getDdManager()->getOne()); - for (auto const& nameValuePair : metaVariableToValueMap) { - valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second); - if (this->containsMetaVariable(nameValuePair.first)) { - remainingMetaVariables.erase(nameValuePair.first); - } - } - - STORM_LOG_THROW(remainingMetaVariables.empty(), storm::exceptions::InvalidArgumentException, "Cannot evaluate function for which not all inputs were given."); - - if (this->isBdd()) { - Dd<DdType::CUDD> value = *this && valueEncoding; - return value.isZero() ? 0 : 1; - } else { - Dd<DdType::CUDD> value = *this * valueEncoding.toMtbdd(); - value = value.sumAbstract(this->getContainedMetaVariables()); - return static_cast<double>(Cudd_V(value.getCuddMtbdd().getNode())); - } - } - - bool Dd<DdType::CUDD>::isOne() const { - if (this->isBdd()) { - return this->getCuddBdd().IsOne(); - } else { - return this->getCuddMtbdd().IsOne(); - } - } - - bool Dd<DdType::CUDD>::isZero() const { - if (this->isBdd()) { - return this->getCuddBdd().IsZero(); - } else { - return this->getCuddMtbdd().IsZero(); - } - } - - bool Dd<DdType::CUDD>::isConstant() const { - if (this->isBdd()) { - return Cudd_IsConstant(this->getCuddBdd().getNode()); - } else { - return Cudd_IsConstant(this->getCuddMtbdd().getNode()); - } - } - - uint_fast64_t Dd<DdType::CUDD>::getIndex() const { - if (this->isBdd()) { - return static_cast<uint_fast64_t>(this->getCuddBdd().NodeReadIndex()); - } else { - return static_cast<uint_fast64_t>(this->getCuddMtbdd().NodeReadIndex()); - } - } - - template<typename ValueType> - std::vector<ValueType> Dd<DdType::CUDD>::toVector() const { - STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create vector from BDD."); - return this->toVector<ValueType>(Odd<DdType::CUDD>(*this)); - } - - template<typename ValueType> - std::vector<ValueType> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const { - STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create vector from BDD."); - std::vector<ValueType> result(rowOdd.getTotalOffset()); - std::vector<uint_fast64_t> ddVariableIndices = this->getSortedVariableIndices(); - addToVectorRec(this->getCuddDdNode(), 0, ddVariableIndices.size(), 0, rowOdd, ddVariableIndices, result); - return result; - } - - storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix() const { - STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); - std::set<storm::expressions::Variable> rowVariables; - std::set<storm::expressions::Variable> columnVariables; - - for (auto const& variable : this->getContainedMetaVariables()) { - if (variable.getName().size() > 0 && variable.getName().back() == '\'') { - columnVariables.insert(variable); - } else { - rowVariables.insert(variable); - } - } - - return toMatrix(rowVariables, columnVariables, Odd<DdType::CUDD>(this->existsAbstract(rowVariables)), Odd<DdType::CUDD>(this->existsAbstract(columnVariables))); - } - - storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { - STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); - std::set<storm::expressions::Variable> rowMetaVariables; - std::set<storm::expressions::Variable> columnMetaVariables; - - for (auto const& variable : this->getContainedMetaVariables()) { - if (variable.getName().size() > 0 && variable.getName().back() == '\'') { - columnMetaVariables.insert(variable); - } else { - rowMetaVariables.insert(variable); - } - } - - return toMatrix(rowMetaVariables, columnMetaVariables, rowOdd, columnOdd); - } - - storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { - STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); - std::vector<uint_fast64_t> ddRowVariableIndices; - std::vector<uint_fast64_t> ddColumnVariableIndices; - - for (auto const& variable : rowMetaVariables) { - DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); - for (auto const& ddVariable : metaVariable.getDdVariables()) { - ddRowVariableIndices.push_back(ddVariable.getIndex()); - } - } - for (auto const& variable : columnMetaVariables) { - DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); - for (auto const& ddVariable : metaVariable.getDdVariables()) { - ddColumnVariableIndices.push_back(ddVariable.getIndex()); - } - } - - // Prepare the vectors that represent the matrix. - std::vector<uint_fast64_t> rowIndications(rowOdd.getTotalOffset() + 1); - std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>> columnsAndValues(this->getNonZeroCount()); - - // Create a trivial row grouping. - std::vector<uint_fast64_t> trivialRowGroupIndices(rowIndications.size()); - uint_fast64_t i = 0; - for (auto& entry : trivialRowGroupIndices) { - entry = i; - ++i; - } - - // Use the toMatrixRec function to compute the number of elements in each row. Using the flag, we prevent - // it from actually generating the entries in the entry vector. - toMatrixRec(this->getCuddDdNode(), rowIndications, columnsAndValues, trivialRowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, false); - - // TODO: counting might be faster by just summing over the primed variables and then using the ODD to convert - // the resulting (DD) vector to an explicit vector. - - // Now that we computed the number of entries in each row, compute the corresponding offsets in the entry vector. - uint_fast64_t tmp = 0; - uint_fast64_t tmp2 = 0; - for (uint_fast64_t i = 1; i < rowIndications.size(); ++i) { - tmp2 = rowIndications[i]; - rowIndications[i] = rowIndications[i - 1] + tmp; - std::swap(tmp, tmp2); - } - rowIndications[0] = 0; - - // Now actually fill the entry vector. - toMatrixRec(this->getCuddDdNode(), rowIndications, columnsAndValues, trivialRowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, true); - - // Since the last call to toMatrixRec modified the rowIndications, we need to restore the correct values. - for (uint_fast64_t i = rowIndications.size() - 1; i > 0; --i) { - rowIndications[i] = rowIndications[i - 1]; - } - rowIndications[0] = 0; - - // Construct matrix and return result. - return storm::storage::SparseMatrix<double>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(trivialRowGroupIndices)); - } - - storm::storage::SparseMatrix<double> Dd<DdType::CUDD>::toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, std::set<storm::expressions::Variable> const& groupMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const { - STORM_LOG_THROW(this->isMtbdd(), storm::exceptions::IllegalFunctionCallException, "Cannot create matrix from BDD."); - std::vector<uint_fast64_t> ddRowVariableIndices; - std::vector<uint_fast64_t> ddColumnVariableIndices; - std::vector<uint_fast64_t> ddGroupVariableIndices; - std::set<storm::expressions::Variable> rowAndColumnMetaVariables; - - for (auto const& variable : rowMetaVariables) { - DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); - for (auto const& ddVariable : metaVariable.getDdVariables()) { - ddRowVariableIndices.push_back(ddVariable.getIndex()); - } - rowAndColumnMetaVariables.insert(variable); - } - for (auto const& variable : columnMetaVariables) { - DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); - for (auto const& ddVariable : metaVariable.getDdVariables()) { - ddColumnVariableIndices.push_back(ddVariable.getIndex()); - } - rowAndColumnMetaVariables.insert(variable); - } - for (auto const& variable : groupMetaVariables) { - DdMetaVariable<DdType::CUDD> const& metaVariable = this->getDdManager()->getMetaVariable(variable); - for (auto const& ddVariable : metaVariable.getDdVariables()) { - ddGroupVariableIndices.push_back(ddVariable.getIndex()); - } - } - - // TODO: assert that the group variables are at the very top of the variable ordering? - - // Start by computing the offsets (in terms of rows) for each row group. - Dd<DdType::CUDD> stateToNumberOfChoices = this->notZero().existsAbstract(columnMetaVariables).sumAbstract(groupMetaVariables); - std::vector<uint_fast64_t> rowGroupIndices = stateToNumberOfChoices.toVector<uint_fast64_t>(rowOdd); - rowGroupIndices.resize(rowGroupIndices.size() + 1); - uint_fast64_t tmp = 0; - uint_fast64_t tmp2 = 0; - for (uint_fast64_t i = 1; i < rowGroupIndices.size(); ++i) { - tmp2 = rowGroupIndices[i]; - rowGroupIndices[i] = rowGroupIndices[i - 1] + tmp; - std::swap(tmp, tmp2); - } - rowGroupIndices[0] = 0; - - // Next, we split the matrix into one for each group. This only works if the group variables are at the very - // top. - std::vector<Dd<DdType::CUDD>> groups; - splitGroupsRec(this->getCuddDdNode(), groups, ddGroupVariableIndices, 0, ddGroupVariableIndices.size(), rowAndColumnMetaVariables); - - // Create the actual storage for the non-zero entries. - std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>> columnsAndValues(this->getNonZeroCount()); - - // Now compute the indices at which the individual rows start. - std::vector<uint_fast64_t> rowIndications(rowGroupIndices.back() + 1); - std::vector<storm::dd::Dd<DdType::CUDD>> statesWithGroupEnabled(groups.size()); - for (uint_fast64_t i = 0; i < groups.size(); ++i) { - auto const& dd = groups[i]; - - toMatrixRec(dd.getCuddDdNode(), rowIndications, columnsAndValues, rowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, false); - - statesWithGroupEnabled[i] = dd.notZero().existsAbstract(columnMetaVariables); - addToVectorRec(statesWithGroupEnabled[i].getCuddDdNode(), 0, ddRowVariableIndices.size(), 0, rowOdd, ddRowVariableIndices, rowGroupIndices); - } - - // Since we modified the rowGroupIndices, we need to restore the correct values. - for (uint_fast64_t i = rowGroupIndices.size() - 1; i > 0; --i) { - rowGroupIndices[i] = rowGroupIndices[i - 1]; - } - rowGroupIndices[0] = 0; - - // Now that we computed the number of entries in each row, compute the corresponding offsets in the entry vector. - tmp = 0; - tmp2 = 0; - for (uint_fast64_t i = 1; i < rowIndications.size(); ++i) { - tmp2 = rowIndications[i]; - rowIndications[i] = rowIndications[i - 1] + tmp; - std::swap(tmp, tmp2); - } - rowIndications[0] = 0; - - // Now actually fill the entry vector. - for (uint_fast64_t i = 0; i < groups.size(); ++i) { - auto const& dd = groups[i]; - - toMatrixRec(dd.getCuddDdNode(), rowIndications, columnsAndValues, rowGroupIndices, rowOdd, columnOdd, 0, 0, ddRowVariableIndices.size() + ddColumnVariableIndices.size(), 0, 0, ddRowVariableIndices, ddColumnVariableIndices, true); - - addToVectorRec(statesWithGroupEnabled[i].getCuddDdNode(), 0, ddRowVariableIndices.size(), 0, rowOdd, ddRowVariableIndices, rowGroupIndices); - } - - // Since we modified the rowGroupIndices, we need to restore the correct values. - for (uint_fast64_t i = rowGroupIndices.size() - 1; i > 0; --i) { - rowGroupIndices[i] = rowGroupIndices[i - 1]; - } - rowGroupIndices[0] = 0; - - // Since the last call to toMatrixRec modified the rowIndications, we need to restore the correct values. - for (uint_fast64_t i = rowIndications.size() - 1; i > 0; --i) { - rowIndications[i] = rowIndications[i - 1]; - } - rowIndications[0] = 0; - - return storm::storage::SparseMatrix<double>(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); - } - - void Dd<DdType::CUDD>::toMatrixRec(DdNode const* dd, std::vector<uint_fast64_t>& rowIndications, std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>>& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupOffsets, Odd<DdType::CUDD> const& rowOdd, Odd<DdType::CUDD> const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector<uint_fast64_t> const& ddRowVariableIndices, std::vector<uint_fast64_t> const& ddColumnVariableIndices, bool generateValues) const { - // For the empty DD, we do not need to add any entries. - if (dd == Cudd_ReadZero(this->getDdManager()->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<uint_fast64_t, double>(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. - toMatrixRec(elseElse, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getElseSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); - // Visit else-then. - toMatrixRec(elseThen, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getElseSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset, currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); - // Visit then-else. - toMatrixRec(thenElse, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getThenSuccessor(), columnOdd.getElseSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset, ddRowVariableIndices, ddColumnVariableIndices, generateValues); - // Visit then-then. - toMatrixRec(thenThen, rowIndications, columnsAndValues, rowGroupOffsets, rowOdd.getThenSuccessor(), columnOdd.getThenSuccessor(), currentRowLevel + 1, currentColumnLevel + 1, maxLevel, currentRowOffset + rowOdd.getElseOffset(), currentColumnOffset + columnOdd.getElseOffset(), ddRowVariableIndices, ddColumnVariableIndices, generateValues); - } - } - - void Dd<DdType::CUDD>::splitGroupsRec(DdNode* dd, std::vector<Dd<DdType::CUDD>>& groups, std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set<storm::expressions::Variable> const& remainingMetaVariables) const { - // For the empty DD, we do not need to create a group. - if (dd == Cudd_ReadZero(this->getDdManager()->getCuddManager().getManager())) { - return; - } - - if (currentLevel == maxLevel) { - groups.push_back(Dd<DdType::CUDD>(this->getDdManager(), ADD(this->getDdManager()->getCuddManager(), dd), remainingMetaVariables)); - } else if (ddGroupVariableIndices[currentLevel] < dd->index) { - splitGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); - splitGroupsRec(dd, groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); - } else { - splitGroupsRec(Cudd_E(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); - splitGroupsRec(Cudd_T(dd), groups, ddGroupVariableIndices, currentLevel + 1, maxLevel, remainingMetaVariables); - } - } - - template<typename ValueType> - void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<ValueType>& targetVector) const { - // For the empty DD, we do not need to add any entries. - if (dd == Cudd_ReadZero(this->getDdManager()->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) { - targetVector[currentOffset] += static_cast<ValueType>(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. - addToVectorRec(dd, currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector); - addToVectorRec(dd, currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector); - } else { - // Otherwise, we simply recursively call the function for both (different) cases. - addToVectorRec(Cudd_E(dd), currentLevel + 1, maxLevel, currentOffset, odd.getElseSuccessor(), ddVariableIndices, targetVector); - addToVectorRec(Cudd_T(dd), currentLevel + 1, maxLevel, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), ddVariableIndices, targetVector); - } - } - - std::vector<uint_fast64_t> Dd<DdType::CUDD>::getSortedVariableIndices() const { - std::vector<uint_fast64_t> ddVariableIndices; - for (auto const& metaVariableName : this->getContainedMetaVariables()) { - auto const& metaVariable = this->getDdManager()->getMetaVariable(metaVariableName); - for (auto const& ddVariable : metaVariable.getDdVariables()) { - ddVariableIndices.push_back(ddVariable.getIndex()); - } - } - - // Next, we need to sort them, since they may be arbitrarily ordered otherwise. - std::sort(ddVariableIndices.begin(), ddVariableIndices.end()); - return ddVariableIndices; - } - bool Dd<DdType::CUDD>::containsMetaVariable(storm::expressions::Variable const& metaVariable) const { return containedMetaVariables.find(metaVariable) != containedMetaVariables.end(); } bool Dd<DdType::CUDD>::containsMetaVariables(std::set<storm::expressions::Variable> const& metaVariables) const { - for (auto const& metaVariable : metaVariables) { - auto const& ddMetaVariable = containedMetaVariables.find(metaVariable); - - if (ddMetaVariable == containedMetaVariables.end()) { - return false; - } - } - return true; + return std::includes(containedMetaVariables.begin(), containedMetaVariables.end(), metaVariables.begin(), metaVariables.end()); } std::set<storm::expressions::Variable> const& Dd<DdType::CUDD>::getContainedMetaVariables() const { @@ -1045,130 +25,28 @@ namespace storm { return this->containedMetaVariables; } - void Dd<DdType::CUDD>::exportToDot(std::string const& filename) const { - if (filename.empty()) { - if (this->isBdd()) { - std::vector<BDD> cuddBddVector = { this->getCuddBdd() }; - this->getDdManager()->getCuddManager().DumpDot(cuddBddVector); - } else { - std::vector<ADD> cuddAddVector = { this->getCuddMtbdd() }; - this->getDdManager()->getCuddManager().DumpDot(cuddAddVector); - } - } else { - // Build the name input of the DD. - std::vector<char*> 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<std::string> ddVariableNamesAsStrings = this->getDdManager()->getDdVariableNames(); - std::vector<char*> 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"); - if (this->isBdd()) { - std::vector<BDD> cuddBddVector = { this->getCuddBdd() }; - this->getDdManager()->getCuddManager().DumpDot(cuddBddVector, &ddVariableNames[0], &ddNames[0], filePointer); - } else { - std::vector<ADD> cuddAddVector = { this->getCuddMtbdd() }; - this->getDdManager()->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; - } - } - } - - void Dd<DdType::CUDD>::addContainedMetaVariable(storm::expressions::Variable const& metaVariable) { - this->getContainedMetaVariables().insert(metaVariable); - } - - void Dd<DdType::CUDD>::removeContainedMetaVariable(storm::expressions::Variable const& metaVariable) { - this->getContainedMetaVariables().erase(metaVariable); - } - std::shared_ptr<DdManager<DdType::CUDD> const> Dd<DdType::CUDD>::getDdManager() const { return this->ddManager; } - - DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const { - int* cube; - double value; - DdGen* generator = this->getCuddDd().FirstCube(&cube, &value); - return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, (Cudd_IsGenEmpty(generator) != 0), &this->getContainedMetaVariables(), enumerateDontCareMetaVariables); - } - - DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const { - return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); - } - - storm::expressions::Expression Dd<DdType::CUDD>::toExpression() const { - return toExpressionRecur(this->getCuddDdNode(), this->getDdManager()->getDdVariables()); - } - - storm::expressions::Expression Dd<DdType::CUDD>::getMintermExpression() const { - // Note that we first transform the ADD into a BDD to convert all non-zero terminals to ones and therefore - // make the DD more compact. - return getMintermExpressionRecur(this->getDdManager()->getCuddManager().getManager(), this->toBdd().getCuddDdNode(), this->getDdManager()->getDdVariables()); + + void Dd<DdType::CUDD>::addMetaVariables(std::set<storm::expressions::Variable> const& metaVariables) { + std::set<storm::expressions::Variable> result; + std::set_union(containedMetaVariables.begin(), containedMetaVariables.end(), metaVariables.begin(), metaVariables.end(), std::inserter(result, result.begin())); + containedMetaVariables = std::move(result); } - - storm::expressions::Expression Dd<DdType::CUDD>::toExpressionRecur(DdNode const* dd, std::vector<storm::expressions::Variable> const& variables) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This feature is currently unavailable."); - // If the DD is a terminal node, we can simply return a constant expression. -// if (Cudd_IsConstant(dd)) { -// return storm::expressions::Expression::createDoubleLiteral(static_cast<double>(Cudd_V(dd))); -// } else { -// return storm::expressions::Expression::createBooleanVariable(variableNames[dd->index]).ite(toExpressionRecur(Cudd_T(dd), variableNames), toExpressionRecur(Cudd_E(dd), variableNames)); -// } + + void Dd<DdType::CUDD>::addMetaVariable(storm::expressions::Variable const& metaVariable) { + this->getContainedMetaVariables().insert(metaVariable); } - - storm::expressions::Expression Dd<DdType::CUDD>::getMintermExpressionRecur(::DdManager* manager, DdNode const* dd, std::vector<storm::expressions::Variable> const& variables) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This feature is currently unavailable."); - // If the DD is a terminal node, we can simply return a constant expression. -// if (Cudd_IsConstant(dd)) { -// if (Cudd_IsComplement(dd)) { -// return storm::expressions::Expression::createBooleanLiteral(false); -// } else { -// return storm::expressions::Expression::createBooleanLiteral((dd == Cudd_ReadOne(manager)) ? true : false); -// } -// } else { -// // Get regular versions of the pointers. -// DdNode* regularDd = Cudd_Regular(dd); -// DdNode* thenDd = Cudd_T(regularDd); -// DdNode* elseDd = Cudd_E(regularDd); -// -// // Compute expression recursively. -// storm::expressions::Expression result = storm::expressions::Expression::createBooleanVariable(variableNames[dd->index]).ite(getMintermExpressionRecur(manager, thenDd, variableNames), getMintermExpressionRecur(manager, elseDd, variableNames)); -// if (Cudd_IsComplement(dd)) { -// result = !result; -// } -// -// return result; -// } + + void Dd<DdType::CUDD>::removeMetaVariable(storm::expressions::Variable const& metaVariable) { + this->getContainedMetaVariables().erase(metaVariable); } - - std::ostream& operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) { - dd.exportToDot(); - return out; + + void Dd<DdType::CUDD>::removeMetaVariables(std::set<storm::expressions::Variable> const& metaVariables) { + std::set<storm::expressions::Variable> result; + std::set_difference(containedMetaVariables.begin(), containedMetaVariables.end(), metaVariables.begin(), metaVariables.end(), std::inserter(result, result.begin())); + containedMetaVariables = std::move(result); } - - // Explicitly instantiate some templated functions. - template std::vector<double> Dd<DdType::CUDD>::toVector() const; - template std::vector<double> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const; - template void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<double>& targetVector) const; - template std::vector<uint_fast64_t> Dd<DdType::CUDD>::toVector() const; - template std::vector<uint_fast64_t> Dd<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const; - template void Dd<DdType::CUDD>::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<uint_fast64_t>& targetVector) const; } } \ No newline at end of file diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h index fadbe06a9..197d2454c 100644 --- a/src/storage/dd/CuddDd.h +++ b/src/storage/dd/CuddDd.h @@ -1,16 +1,10 @@ #ifndef STORM_STORAGE_DD_CUDDDD_H_ #define STORM_STORAGE_DD_CUDDDD_H_ -#include <map> #include <set> #include <memory> -#include <iostream> -#include <boost/variant.hpp> #include "src/storage/dd/Dd.h" -#include "src/storage/dd/CuddDdForwardIterator.h" -#include "src/storage/SparseMatrix.h" -#include "src/storage/expressions/Expression.h" #include "src/storage/expressions/Variable.h" #include "src/utility/OsDetection.h" @@ -22,14 +16,13 @@ namespace storm { // Forward-declare some classes. template<DdType Type> class DdManager; template<DdType Type> class Odd; + template<DdType Type> class Bdd; template<> class Dd<DdType::CUDD> { public: - // Declare the DdManager and DdIterator class as friend so it can access the internals of a DD. + // Declare the DdManager so it can access the internals of a DD. friend class DdManager<DdType::CUDD>; - friend class DdForwardIterator<DdType::CUDD>; - friend class Odd<DdType::CUDD>; // Instantiate all copy/move constructors/assignments with the default implementation. Dd() = default; @@ -40,540 +33,40 @@ namespace storm { Dd& operator=(Dd<DdType::CUDD>&& other) = default; #endif - /*! - * Retrieves whether the two DDs represent the same function. - * - * @param other The DD that is to be compared with the current one. - * @return True if the DDs represent the same function. - */ - bool operator==(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves whether the two DDs represent different functions. - * - * @param other The DD that is to be compared with the current one. - * @return True if the DDs represent the different functions. - */ - bool operator!=(Dd<DdType::CUDD> const& other) const; - - /*! - * Performs an if-then-else with the given operands, i.e. maps all valuations that are mapped to a non-zero - * function value to the function values specified by the first DD and all others to the function values - * specified by the second DD. - */ - Dd<DdType::CUDD> ite(Dd<DdType::CUDD> const& thenDd, Dd<DdType::CUDD> const& elseDd) const; - - /*! - * Performs a logical or of the current and the given DD. - * - * @param other The second DD used for the operation. - * @return The logical or of the operands. - */ - Dd<DdType::CUDD> operator||(Dd<DdType::CUDD> const& other) const; - - /*! - * Performs a logical or of the current and the given DD and assigns it to the current DD. - * - * @param other The second DD used for the operation. - * @return A reference to the current DD after the operation - */ - Dd<DdType::CUDD>& operator|=(Dd<DdType::CUDD> const& other); - - /*! - * Performs a logical and of the current and the given DD. - * - * @param other The second DD used for the operation. - * @return The logical and of the operands. - */ - Dd<DdType::CUDD> operator&&(Dd<DdType::CUDD> const& other) const; - - /*! - * Performs a logical and of the current and the given DD and assigns it to the current DD. - * - * @param other The second DD used for the operation. - * @return A reference to the current DD after the operation - */ - Dd<DdType::CUDD>& operator&=(Dd<DdType::CUDD> const& other); - - /*! - * Performs a logical iff of the current and the given DD. - * - * @param other The second DD used for the operation. - * @return The logical iff of the operands. - */ - Dd<DdType::CUDD> iff(Dd<DdType::CUDD> const& other) const; - - /*! - * Performs a logical exclusive-or of the current and the given DD. - * - * @param other The second DD used for the operation. - * @return The logical exclusive-or of the operands. - */ - Dd<DdType::CUDD> exclusiveOr(Dd<DdType::CUDD> const& other) const; - - /*! - * Performs a logical implication of the current and the given DD. - * - * @param other The second DD used for the operation. - * @return The logical implication of the operands. - */ - Dd<DdType::CUDD> implies(Dd<DdType::CUDD> const& other) const; - - /*! - * Adds the two DDs. - * - * @param other The DD to add to the current one. - * @return The result of the addition. - */ - Dd<DdType::CUDD> operator+(Dd<DdType::CUDD> const& other) const; - - /*! - * Adds the given DD to the current one. - * - * @param other The DD to add to the current one. - * @return A reference to the current DD after the operation. - */ - Dd<DdType::CUDD>& operator+=(Dd<DdType::CUDD> const& other); - - /*! - * Multiplies the two DDs. - * - * @param other The DD to multiply with the current one. - * @return The result of the multiplication. - */ - Dd<DdType::CUDD> operator*(Dd<DdType::CUDD> const& other) const; - - /*! - * Multiplies the given DD with the current one and assigns the result to the current DD. - * - * @param other The DD to multiply with the current one. - * @return A reference to the current DD after the operation. - */ - Dd<DdType::CUDD>& operator*=(Dd<DdType::CUDD> const& other); - - /*! - * Subtracts the given DD from the current one. - * - * @param other The DD to subtract from the current one. - * @return The result of the subtraction. - */ - Dd<DdType::CUDD> operator-(Dd<DdType::CUDD> const& other) const; - - /*! - * Subtracts the DD from the constant zero function. - * - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> operator-() const; - - /*! - * Subtracts the given DD from the current one and assigns the result to the current DD. - * - * @param other The DD to subtract from the current one. - * @return A reference to the current DD after the operation. - */ - Dd<DdType::CUDD>& operator-=(Dd<DdType::CUDD> const& other); - - /*! - * Divides the current DD by the given one. - * - * @param other The DD by which to divide the current one. - * @return The result of the division. - */ - Dd<DdType::CUDD> operator/(Dd<DdType::CUDD> const& other) const; - - /*! - * Divides the current DD by the given one and assigns the result to the current DD. - * - * @param other The DD by which to divide the current one. - * @return A reference to the current DD after the operation. - */ - Dd<DdType::CUDD>& operator/=(Dd<DdType::CUDD> const& other); - - /*! - * Retrieves the logical complement of the current DD. The result will map all encodings with a value - * unequal to zero to false and all others to true. - * - * @return The logical complement of the current DD. - */ - Dd<DdType::CUDD> operator!() const; - - /*! - * Logically complements the current DD. The result will map all encodings with a value - * unequal to zero to false and all others to true. - * - * @return A reference to the current DD after the operation. - */ - Dd<DdType::CUDD>& complement(); - - /*! - * Retrieves the function that maps all evaluations to one that have an identical function values. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> equals(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to one that have distinct function values. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> notEquals(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to one whose function value in the first DD are less - * than the one in the given DD. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> less(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to one whose function value in the first DD are less or - * equal than the one in the given DD. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> lessOrEqual(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to one whose function value in the first DD are greater - * than the one in the given DD. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> greater(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to one whose function value in the first DD are greater - * or equal than the one in the given DD. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> greaterOrEqual(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to the minimum of the function values of the two DDs. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> minimum(Dd<DdType::CUDD> const& other) const; - - /*! - * Retrieves the function that maps all evaluations to the maximum of the function values of the two DDs. - * - * @param other The DD with which to perform the operation. - * @return The resulting function represented as a DD. - */ - Dd<DdType::CUDD> maximum(Dd<DdType::CUDD> const& other) const; - - /*! - * Existentially abstracts from the given meta variables. - * - * @param metaVariables The meta variables from which to abstract. - */ - Dd<DdType::CUDD> existsAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; - - /*! - * Universally abstracts from the given meta variables. - * - * @param metaVariables The meta variables from which to abstract. - */ - Dd<DdType::CUDD> universalAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; - - /*! - * Sum-abstracts from the given meta variables. - * - * @param metaVariables The meta variables from which to abstract. - */ - Dd<DdType::CUDD> sumAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; - - /*! - * Min-abstracts from the given meta variables. - * - * @param metaVariables The meta variables from which to abstract. - */ - Dd<DdType::CUDD> minAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; - - /*! - * Max-abstracts from the given meta variables. - * - * @param metaVariables The meta variables from which to abstract. - */ - Dd<DdType::CUDD> maxAbstract(std::set<storm::expressions::Variable> const& metaVariables) const; - - /*! - * Checks whether the current and the given DD represent the same function modulo some given precision. - * - * @param other The DD with which to compare. - * @param precision An upper bound on the maximal difference between any two function values that is to be - * tolerated. - * @param relative If set to true, not the absolute values have to be within the precision, but the relative - * values. - */ - bool equalModuloPrecision(Dd<DdType::CUDD> const& other, double precision, bool relative = true) const; - - /*! - * Swaps the given pairs of meta variables in the DD. The pairs of meta variables must be guaranteed to have - * the same number of underlying DD variables. - * - * @param metaVariablePairs A vector of meta variable pairs that are to be swapped for one another. - * @return The resulting DD. - */ - Dd<DdType::CUDD> swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs); - - /*! - * Multiplies the current DD (representing a matrix) with the given matrix by summing over the given meta - * variables. - * - * @param otherMatrix The matrix with which to multiply. - * @param summationMetaVariables The names of the meta variables over which to sum during the matrix- - * matrix multiplication. - * @return A DD representing the result of the matrix-matrix multiplication. - */ - Dd<DdType::CUDD> multiplyMatrix(Dd<DdType::CUDD> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const; - - /*! - * Computes the logical and of the current and the given DD and existentially abstracts from the given set - * of variables. - * - * @param other The second DD for the logical and. - * @param existentialVariables The variables from which to existentially abstract. - * @return A DD representing the result. - */ - Dd<DdType::CUDD> andExists(Dd<DdType::CUDD> const& other, std::set<storm::expressions::Variable> const& existentialVariables) const; - - /*! - * Computes a DD that represents the function in which all assignments with a function value strictly larger - * than the given value are mapped to one and all others to zero. - * - * @param value The value used for the comparison. - * @return The resulting DD. - */ - Dd<DdType::CUDD> greater(double value) const; - - /*! - * Computes a DD that represents the function in which all assignments with a function value larger or equal - * to the given value are mapped to one and all others to zero. - * - * @param value The value used for the comparison. - * @return The resulting DD. - */ - Dd<DdType::CUDD> greaterOrEqual(double value) const; - - /*! - * Computes a DD that represents the function in which all assignments with a function value unequal to zero - * are mapped to one and all others to zero. - * - * @return The resulting DD. - */ - Dd<DdType::CUDD> notZero() const; - - /*! - * Computes the constraint of the current DD with the given constraint. That is, the function value of the - * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint - * and may be different otherwise. - * - * @param constraint The constraint to use for the operation. - * @return The resulting DD. - */ - Dd<DdType::CUDD> constrain(Dd<DdType::CUDD> const& constraint) const; - - /*! - * Computes the restriction of the current DD with the given constraint. That is, the function value of the - * resulting DD will be the same as the current ones for all assignments mapping to one in the constraint - * and may be different otherwise. - * - * @param constraint The constraint to use for the operation. - * @return The resulting DD. - */ - Dd<DdType::CUDD> restrict(Dd<DdType::CUDD> const& constraint) const; - /*! * Retrieves the support of the current DD. * - * @return The support represented as a DD. + * @return The support represented as a BDD. */ - Dd<DdType::CUDD> getSupport() const; + virtual Bdd<DdType::CUDD> getSupport() const = 0; /*! * Retrieves the number of encodings that are mapped to a non-zero value. * * @return The number of encodings that are mapped to a non-zero value. */ - uint_fast64_t getNonZeroCount() const; + virtual uint_fast64_t getNonZeroCount() const = 0; /*! * Retrieves the number of leaves of the DD. * * @return The number of leaves of the DD. */ - uint_fast64_t getLeafCount() const; + virtual uint_fast64_t getLeafCount() const = 0; /*! * Retrieves the number of nodes necessary to represent the DD. * * @return The number of nodes in this DD. */ - uint_fast64_t getNodeCount() const; - - /*! - * Retrieves the lowest function value of any encoding. - * - * @return The lowest function value of any encoding. - */ - double getMin() const; - - /*! - * Retrieves the highest function value of any encoding. - * - * @return The highest function value of any encoding. - */ - double getMax() const; - - /*! - * Sets the function values of all encodings that have the given value of the meta variable to the given - * target value. - * - * @param metaVariable The meta variable that has to be equal to the given value. - * @param variableValue The value that the meta variable is supposed to have. This must be within the range - * of the meta variable. - * @param targetValue The new function value of the modified encodings. - */ - void setValue(storm::expressions::Variable const& metaVariable, int_fast64_t variableValue, double targetValue); - - /*! - * Sets the function values of all encodings that have the given values of the two meta variables to the - * given target value. - * - * @param metaVariable1 The first meta variable that has to be equal to the first given - * value. - * @param variableValue1 The value that the first meta variable is supposed to have. This must be within the - * range of the meta variable. - * @param metaVariable2 The second meta variable that has to be equal to the second given - * value. - * @param variableValue2 The value that the second meta variable is supposed to have. This must be within - * the range of the meta variable. - * @param targetValue The new function value of the modified encodings. - */ - void setValue(storm::expressions::Variable const& metaVariable1, int_fast64_t variableValue1, storm::expressions::Variable const& metaVariable2, int_fast64_t variableValue2, double targetValue); - - /*! - * Sets the function values of all encodings that have the given values of the given meta variables to the - * given target value. - * - * @param metaVariableToValueMap A mapping of meta variables to the values they are supposed to have. All - * values must be within the range of the respective meta variable. - * @param targetValue The new function value of the modified encodings. - */ - void setValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap = std::map<storm::expressions::Variable, int_fast64_t>(), double targetValue = 0); - - /*! - * Retrieves the value of the function when all meta variables are assigned the values of the given mapping. - * Note that the mapping must specify values for all meta variables contained in the DD. - * - * @param metaVariableToValueMap A mapping of meta variables to their values. - * @return The value of the function evaluated with the given input. - */ - double getValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap = std::map<storm::expressions::Variable, int_fast64_t>()) const; - - /*! - * Retrieves whether this DD represents the constant one function. - * - * @return True if this DD represents the constant one function. - */ - bool isOne() const; - - /*! - * Retrieves whether this DD represents the constant zero function. - * - * @return True if this DD represents the constant zero function. - */ - bool isZero() const; - - /*! - * Retrieves whether this DD represents a constant function. - * - * @return True if this DD represents a constants function. - */ - bool isConstant() const; + virtual uint_fast64_t getNodeCount() const = 0; /*! * Retrieves the index of the topmost variable in the DD. * * @return The index of the topmost variable in DD. */ - uint_fast64_t getIndex() const; - - /*! - * Converts the DD to a vector. - * - * @return The double vector that is represented by this DD. - */ - template<typename ValueType> - std::vector<ValueType> toVector() const; - - /*! - * Converts the DD to a vector. The given offset-labeled DD is used to determine the correct row of - * each entry. - * - * @param rowOdd The ODD used for determining the correct row. - * @return The double vector that is represented by this DD. - */ - template<typename ValueType> - std::vector<ValueType> toVector(storm::dd::Odd<DdType::CUDD> const& rowOdd) const; - - /*! - * Converts the DD to a (sparse) double matrix. All contained non-primed variables are assumed to encode the - * row, whereas all primed variables are assumed to encode the column. - * - * @return The matrix that is represented by this DD. - */ - storm::storage::SparseMatrix<double> toMatrix() const; - - /*! - * Converts the DD to a (sparse) double matrix. All contained non-primed variables are assumed to encode the - * row, whereas all primed variables are assumed to encode the column. The given offset-labeled DDs are used - * to determine the correct row and column, respectively, for each entry. - * - * @param rowOdd The ODD used for determining the correct row. - * @param columnOdd The ODD used for determining the correct column. - * @return The matrix that is represented by this DD. - */ - storm::storage::SparseMatrix<double> toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; - - /*! - * Converts the DD to a (sparse) double matrix. The given offset-labeled DDs are used to determine the - * correct row and column, respectively, for each entry. - * - * @param rowMetaVariables The meta variables that encode the rows of the matrix. - * @param columnMetaVariables The meta variables that encode the columns of the matrix. - * @param rowOdd The ODD used for determining the correct row. - * @param columnOdd The ODD used for determining the correct column. - * @return The matrix that is represented by this DD. - */ - storm::storage::SparseMatrix<double> toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; - - /*! - * Converts the DD to a row-grouped (sparse) double matrix. The given offset-labeled DDs are used to - * determine the correct row and column, respectively, for each entry. Note: this function assumes that - * the meta variables used to distinguish different row groups are at the very top of the DD. - * - * @param rowMetaVariables The meta variables that encode the rows of the matrix. - * @param columnMetaVariables The meta variables that encode the columns of the matrix. - * @param groupMetaVariables The meta variables that are used to distinguish different row groups. - * @param rowOdd The ODD used for determining the correct row. - * @param columnOdd The ODD used for determining the correct column. - * @return The matrix that is represented by this DD. - */ - storm::storage::SparseMatrix<double> toMatrix(std::set<storm::expressions::Variable> const& rowMetaVariables, std::set<storm::expressions::Variable> const& columnMetaVariables, std::set<storm::expressions::Variable> const& groupMetaVariables, storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const; + virtual uint_fast64_t getIndex() const = 0; /*! * Retrieves whether the given meta variable is contained in the DD. @@ -598,19 +91,12 @@ namespace storm { */ std::set<storm::expressions::Variable> const& getContainedMetaVariables() const; - /*! - * Retrieves the set of all meta variables contained in the DD. - * - * @return The set of all meta variables contained in the DD. - */ - std::set<storm::expressions::Variable>& getContainedMetaVariables(); - /*! * Exports the DD to the given file in the dot format. * * @param filename The name of the file to which the DD is to be exported. */ - void exportToDot(std::string const& filename = "") const; + virtual void exportToDot(std::string const& filename = "") const = 0; /*! * Retrieves the manager that is responsible for this DD. @@ -618,224 +104,59 @@ namespace storm { * A pointer to the manager that is responsible for this DD. */ std::shared_ptr<DdManager<DdType::CUDD> const> getDdManager() const; + + protected: /*! - * Retrieves an iterator that points to the first meta variable assignment with a non-zero function value. - * - * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even - * if a meta variable does not at all influence the the function value. - * @return An iterator that points to the first meta variable assignment with a non-zero function value. - */ - DdForwardIterator<DdType::CUDD> begin(bool enumerateDontCareMetaVariables = true) const; - - /*! - * Retrieves an iterator that points past the end of the container. - * - * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even - * if a meta variable does not at all influence the the function value. - * @return An iterator that points past the end of the container. - */ - DdForwardIterator<DdType::CUDD> end(bool enumerateDontCareMetaVariables = true) const; - - /*! - * Converts the DD into a (heavily nested) if-then-else expression that represents the very same function. - * The variable names used in the expression are derived from the meta variable name and are extended with a - * suffix ".i" if the meta variable is integer-valued, expressing that the variable is the i-th bit of the - * meta variable. - * - * @return The resulting expression. - */ - storm::expressions::Expression toExpression() const; - - /*! - * Converts the DD into a (heavily nested) if-then-else (with negations) expression that evaluates to true - * if and only if the assignment is minterm of the DD. The variable names used in the expression are derived - * from the meta variable name and are extended with a suffix ".i" if the meta variable is integer-valued, - * expressing that the variable is the i-th bit of the meta variable. - * - * @return The resulting expression. - */ - storm::expressions::Expression getMintermExpression() const; - - friend std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd); - - /*! - * Converts an MTBDD to a BDD by mapping all values unequal to zero to 1. This effectively does the same as - * a call to notZero(). - * - * @return The corresponding BDD. - */ - Dd<DdType::CUDD> toBdd() const; - - /*! - * Converts a BDD to an equivalent MTBDD. - * - * @return The corresponding MTBDD. - */ - Dd<DdType::CUDD> toMtbdd() const; - - /*! - * Retrieves whether this DD is a BDD. - * - * @return True iff this DD is a BDD. - */ - bool isBdd() const; - - /*! - * Retrieves whether this DD is an MTBDD. Even though MTBDDs technicall subsume BDDs, this will return false - * if the DD is actually a BDD. - * - * @return True iff this DD is an MTBDD. - */ - bool isMtbdd() const; - - private: - /*! - * Retrieves the CUDD BDD object associated with this DD. - * - * @return The CUDD BDD object associated with this DD. - */ - BDD getCuddBdd() const; - - /*! - * Retrieves the CUDD MTBDD object associated with this DD. - * - * @return The CUDD MTBDD object associated with this DD. - */ - ADD getCuddMtbdd() const; - - /*! - * Retrieves the CUDD object associated with this DD. + * Retrieves the set of all meta variables contained in the DD. * - * @return The CUDD object associated with this DD. + * @return The set of all meta variables contained in the DD. */ - ABDD const& getCuddDd() const; + std::set<storm::expressions::Variable>& getContainedMetaVariables(); /*! - * Retrieves the raw DD node of CUDD associated with this DD. + * Adds the given set of meta variables to the DD. * - * @return The DD node of CUDD associated with this DD. + * @param metaVariables The set of meta variables to add. */ - DdNode* getCuddDdNode() const; + void addMetaVariables(std::set<storm::expressions::Variable> const& metaVariables); /*! * Adds the given meta variable to the set of meta variables that are contained in this DD. * * @param metaVariable The name of the meta variable to add. */ - void addContainedMetaVariable(storm::expressions::Variable const& metaVariable); + void addMetaVariable(storm::expressions::Variable const& metaVariable); /*! * Removes the given meta variable to the set of meta variables that are contained in this DD. * * @param metaVariable The name of the meta variable to remove. */ - void removeContainedMetaVariable(storm::expressions::Variable const& metaVariable); + void removeMetaVariable(storm::expressions::Variable const& metaVariable); /*! - * Performs the recursive step of toExpression on the given DD. + * Removes the given set of meta variables from the DD. * - * @param dd The dd to translate into an expression. - * @param variables The variables to use in the expression. - * @return The resulting expression. + * @param metaVariables The set of meta variables to remove. */ - static storm::expressions::Expression toExpressionRecur(DdNode const* dd, std::vector<storm::expressions::Variable> const& variables); + void removeMetaVariables(std::set<storm::expressions::Variable> const& metaVariables); - /*! - * Performs the recursive step of getMintermExpression on the given DD. - * - * @param manager The manager of the DD. - * @param dd The dd whose minterms to translate into an expression. - * @param variables The variables to use in the expression. - * @return The resulting expression. - */ - static storm::expressions::Expression getMintermExpressionRecur(::DdManager* manager, DdNode const* dd, std::vector<storm::expressions::Variable> const& variables); + protected: /*! - * Creates a DD that encapsulates the given CUDD ADD. - * - * @param ddManager The manager responsible for this DD. - * @param cuddDd The CUDD ADD to store. - * @param containedMetaVariables The meta variables that appear in the DD. - */ - Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, ADD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>()); - - /*! - * Creates a DD that encapsulates the given CUDD ADD. + * Creates a DD with the given manager and meta variables. * * @param ddManager The manager responsible for this DD. - * @param cuddDd The CUDD ADD to store. * @param containedMetaVariables The meta variables that appear in the DD. */ - Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, BDD cuddDd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>()); - - /*! - * Helper function to convert the DD into a (sparse) matrix. - * - * @param dd The DD to convert. - * @param rowIndications A vector indicating at which position in the columnsAndValues vector the entries - * of row i start. Note: this vector is modified in the computation. More concretely, each entry i in the - * vector will be increased by the number of entries in the row. This can be used to count the number - * of entries in each row. If the values are not to be modified, a copy needs to be provided or the entries - * need to be restored afterwards. - * @param columnsAndValues The vector that will hold the columns and values of non-zero entries upon successful - * completion. - * @param rowGroupOffsets The row offsets at which a given row group starts. - * @param rowOdd The ODD used for the row translation. - * @param columnOdd The ODD used for the column translation. - * @param currentRowLevel The currently considered row level in the DD. - * @param currentColumnLevel The currently considered row level in the DD. - * @param maxLevel The number of levels that need to be considered. - * @param currentRowOffset The current row offset. - * @param currentColumnOffset The current row offset. - * @param ddRowVariableIndices The (sorted) indices of all DD row variables that need to be considered. - * @param ddColumnVariableIndices The (sorted) indices of all DD row variables that need to be considered. - * @param generateValues If set to true, the vector columnsAndValues is filled with the actual entries, which - * only works if the offsets given in rowIndications are already correct. If they need to be computed first, - * this flag needs to be false. - */ - void toMatrixRec(DdNode const* dd, std::vector<uint_fast64_t>& rowIndications, std::vector<storm::storage::MatrixEntry<uint_fast64_t, double>>& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupOffsets, Odd<DdType::CUDD> const& rowOdd, Odd<DdType::CUDD> const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector<uint_fast64_t> const& ddRowVariableIndices, std::vector<uint_fast64_t> const& ddColumnVariableIndices, bool generateValues = true) const; - - /*! - * Splits the given matrix DD into the groups using the given group variables. - * - * @param dd The DD to split. - * @param groups A vector that is to be filled with the DDs for the individual groups. - * @param ddGroupVariableIndices The (sorted) indices of all DD group variables that need to be considered. - * @param currentLevel The currently considered level in the DD. - * @param maxLevel The number of levels that need to be considered. - * @param remainingMetaVariables The meta variables that remain in the DDs after the groups have been split. - */ - void splitGroupsRec(DdNode* dd, std::vector<Dd<DdType::CUDD>>& groups, std::vector<uint_fast64_t> const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set<storm::expressions::Variable> const& remainingMetaVariables) const; + Dd(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>()); - /*! - * Performs a recursive step to add the given DD-based vector to the given explicit vector. - * - * @param dd The DD to add to the explicit vector. - * @param currentLevel The currently considered level in the DD. - * @param maxLevel The number of levels that need to be considered. - * @param currentOffset The current offset. - * @param odd The ODD used for the translation. - * @param ddVariableIndices The (sorted) indices of all DD variables that need to be considered. - * @param targetVector The vector to which the translated DD-based vector is to be added. - */ - template<typename ValueType> - void addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<ValueType>& targetVector) const; - - /*! - * Retrieves the indices of all DD variables that are contained in this DD (not necessarily in the support, - * because they could be "don't cares"). Additionally, the indices are sorted to allow for easy access. - * - * @return The (sorted) indices of all DD variables that are contained in this DD. - */ - std::vector<uint_fast64_t> getSortedVariableIndices() const; + private: // A pointer to the manager responsible for this DD. std::shared_ptr<DdManager<DdType::CUDD> const> ddManager; - // The ADD created by CUDD. - boost::variant<BDD, ADD> cuddDd; - // The meta variables that appear in this DD. std::set<storm::expressions::Variable> containedMetaVariables; };