You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

838 lines
52 KiB

#include <cstring>
#include <algorithm>
#include "src/storage/dd/CuddAdd.h"
#include "src/storage/dd/CuddBdd.h"
#include "src/storage/dd/CuddOdd.h"
#include "src/storage/dd/CuddDdManager.h"
#include "src/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 {
Add<DdType::CUDD>::Add(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, ADD cuddAdd, std::set<storm::expressions::Variable> const& containedMetaVariables) : Dd<DdType::CUDD>(ddManager, containedMetaVariables), cuddAdd(cuddAdd) {
// Intentionally left empty.
}
Add<DdType::CUDD>::Add(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, std::vector<double> const& values, Odd<DdType::CUDD> const& odd, std::set<storm::expressions::Variable> const& metaVariables) : Dd<DdType::CUDD>(ddManager, metaVariables) {
cuddAdd = fromVector(ddManager, values, odd, metaVariables);
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::toBdd() const {
return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().BddPattern(), this->getContainedMetaVariables());
}
ADD Add<DdType::CUDD>::getCuddAdd() const {
return this->cuddAdd;
}
DdNode* Add<DdType::CUDD>::getCuddDdNode() const {
return this->getCuddAdd().getNode();
}
bool Add<DdType::CUDD>::operator==(Add<DdType::CUDD> const& other) const {
return this->getCuddAdd() == other.getCuddAdd();
}
bool Add<DdType::CUDD>::operator!=(Add<DdType::CUDD> const& other) const {
return !(*this == other);
}
Add<DdType::CUDD> Add<DdType::CUDD>::ite(Add<DdType::CUDD> const& thenDd, Add<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 Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Ite(thenDd.getCuddAdd(), elseDd.getCuddAdd()), metaVariableNames);
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator!() const {
return Add<DdType::CUDD>(this->getDdManager(), ~this->getCuddAdd(), this->getContainedMetaVariables());
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator||(Add<DdType::CUDD> const& other) const {
Add<DdType::CUDD> result(*this);
result |= other;
return result;
}
Add<DdType::CUDD>& Add<DdType::CUDD>::operator|=(Add<DdType::CUDD> const& other) {
this->addMetaVariables(other.getContainedMetaVariables());
this->cuddAdd = this->getCuddAdd() | other.getCuddAdd();
return *this;
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator+(Add<DdType::CUDD> const& other) const {
Add<DdType::CUDD> result(*this);
result += other;
return result;
}
Add<DdType::CUDD>& Add<DdType::CUDD>::operator+=(Add<DdType::CUDD> const& other) {
this->addMetaVariables(other.getContainedMetaVariables());
this->cuddAdd = this->getCuddAdd() + other.getCuddAdd();
return *this;
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator*(Add<DdType::CUDD> const& other) const {
Add<DdType::CUDD> result(*this);
result *= other;
return result;
}
Add<DdType::CUDD>& Add<DdType::CUDD>::operator*=(Add<DdType::CUDD> const& other) {
this->addMetaVariables(other.getContainedMetaVariables());
this->cuddAdd = this->getCuddAdd() * other.getCuddAdd();
return *this;
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator-(Add<DdType::CUDD> const& other) const {
Add<DdType::CUDD> result(*this);
result -= other;
return result;
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator-() const {
return this->getDdManager()->getAddZero() - *this;
}
Add<DdType::CUDD>& Add<DdType::CUDD>::operator-=(Add<DdType::CUDD> const& other) {
this->addMetaVariables(other.getContainedMetaVariables());
this->cuddAdd = this->getCuddAdd() - other.getCuddAdd();
return *this;
}
Add<DdType::CUDD> Add<DdType::CUDD>::operator/(Add<DdType::CUDD> const& other) const {
Add<DdType::CUDD> result(*this);
result /= other;
return result;
}
Add<DdType::CUDD>& Add<DdType::CUDD>::operator/=(Add<DdType::CUDD> const& other) {
this->addMetaVariables(other.getContainedMetaVariables());
this->cuddAdd = this->getCuddAdd().Divide(other.getCuddAdd());
return *this;
}
Add<DdType::CUDD> Add<DdType::CUDD>::equals(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Equals(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::notEquals(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().NotEquals(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::less(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().LessThan(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::lessOrEqual(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().LessThanOrEqual(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::greater(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().GreaterThan(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::greaterOrEqual(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().GreaterThanOrEqual(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::pow(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Pow(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::mod(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Mod(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::logxy(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().LogXY(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::floor() const {
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Floor(), this->getContainedMetaVariables());
}
Add<DdType::CUDD> Add<DdType::CUDD>::ceil() const {
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Ceil(), this->getContainedMetaVariables());
}
Add<DdType::CUDD> Add<DdType::CUDD>::minimum(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Minimum(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::maximum(Add<DdType::CUDD> const& other) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Maximum(other.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::sumAbstract(std::set<storm::expressions::Variable> const& metaVariables) const {
Bdd<DdType::CUDD> cubeDd = this->getDdManager()->getBddOne();
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 Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().ExistAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::minAbstract(std::set<storm::expressions::Variable> const& metaVariables) const {
Bdd<DdType::CUDD> cubeDd = this->getDdManager()->getBddOne();
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 Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().MinAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::maxAbstract(std::set<storm::expressions::Variable> const& metaVariables) const {
Bdd<DdType::CUDD> cubeDd = this->getDdManager()->getBddOne();
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 Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().MaxAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables);
}
bool Add<DdType::CUDD>::equalModuloPrecision(Add<DdType::CUDD> const& other, double precision, bool relative) const {
if (relative) {
return this->getCuddAdd().EqualSupNormRel(other.getCuddAdd(), precision);
} else {
return this->getCuddAdd().EqualSupNorm(other.getCuddAdd(), precision);
}
}
Add<DdType::CUDD> Add<DdType::CUDD>::swapVariables(std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& metaVariablePairs) {
std::set<storm::expressions::Variable> newContainedMetaVariables;
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.toAdd().getCuddAdd());
}
for (auto const& ddVariable : variable2.getDdVariables()) {
to.push_back(ddVariable.toAdd().getCuddAdd());
}
}
// Finally, call CUDD to swap the variables.
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().SwapVariables(from, to), newContainedMetaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::multiplyMatrix(Add<DdType::CUDD> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const {
// 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.toAdd().getCuddAdd());
}
}
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 Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().MatrixMultiply(otherMatrix.getCuddAdd(), summationDdVariables), containedMetaVariables);
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::greater(double value) const {
return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().BddStrictThreshold(value), this->getContainedMetaVariables());
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::greaterOrEqual(double value) const {
return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().BddThreshold(value), this->getContainedMetaVariables());
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::less(double value) const {
return Bdd<DdType::CUDD>(this->getDdManager(), ~this->getCuddAdd().BddThreshold(value), this->getContainedMetaVariables());
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::lessOrEqual(double value) const {
return Bdd<DdType::CUDD>(this->getDdManager(), ~this->getCuddAdd().BddStrictThreshold(value), this->getContainedMetaVariables());
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::notZero() const {
return this->toBdd();
}
Add<DdType::CUDD> Add<DdType::CUDD>::constrain(Add<DdType::CUDD> const& constraint) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Constrain(constraint.getCuddAdd()), metaVariables);
}
Add<DdType::CUDD> Add<DdType::CUDD>::restrict(Add<DdType::CUDD> const& constraint) const {
std::set<storm::expressions::Variable> metaVariables;
std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin()));
return Add<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Restrict(constraint.getCuddAdd()), metaVariables);
}
Bdd<DdType::CUDD> Add<DdType::CUDD>::getSupport() const {
return Bdd<DdType::CUDD>(this->getDdManager(), this->getCuddAdd().Support(), this->getContainedMetaVariables());
}
uint_fast64_t Add<DdType::CUDD>::getNonZeroCount() const {
std::size_t numberOfDdVariables = 0;
for (auto const& metaVariable : this->getContainedMetaVariables()) {
numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariable).getNumberOfDdVariables();
}
return static_cast<uint_fast64_t>(this->getCuddAdd().CountMinterm(static_cast<int>(numberOfDdVariables)));
}
uint_fast64_t Add<DdType::CUDD>::getLeafCount() const {
return static_cast<uint_fast64_t>(this->getCuddAdd().CountLeaves());
}
uint_fast64_t Add<DdType::CUDD>::getNodeCount() const {
return static_cast<uint_fast64_t>(this->getCuddAdd().nodeCount());
}
double Add<DdType::CUDD>::getMin() const {
ADD constantMinAdd = this->getCuddAdd().FindMin();
return static_cast<double>(Cudd_V(constantMinAdd.getNode()));
}
double Add<DdType::CUDD>::getMax() const {
ADD constantMaxAdd = this->getCuddAdd().FindMax();
return static_cast<double>(Cudd_V(constantMaxAdd.getNode()));
}
void Add<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 Add<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 Add<DdType::CUDD>::setValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap, double targetValue) {
Bdd<DdType::CUDD> valueEncoding = this->getDdManager()->getBddOne();
for (auto const& nameValuePair : metaVariableToValueMap) {
valueEncoding &= this->getDdManager()->getEncoding(nameValuePair.first, nameValuePair.second);
// Also record that the DD now contains the meta variable.
this->addMetaVariable(nameValuePair.first);
}
this->cuddAdd = valueEncoding.toAdd().getCuddAdd().Ite(this->getDdManager()->getConstant(targetValue).getCuddAdd(), this->getCuddAdd());
}
double Add<DdType::CUDD>::getValue(std::map<storm::expressions::Variable, int_fast64_t> const& metaVariableToValueMap) const {
std::set<storm::expressions::Variable> remainingMetaVariables(this->getContainedMetaVariables());
Bdd<DdType::CUDD> valueEncoding = this->getDdManager()->getBddOne();
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.");
Add<DdType::CUDD> value = *this * valueEncoding.toAdd();
value = value.sumAbstract(this->getContainedMetaVariables());
return static_cast<double>(Cudd_V(value.getCuddAdd().getNode()));
}
bool Add<DdType::CUDD>::isOne() const {
return this->getCuddAdd().IsOne();
}
bool Add<DdType::CUDD>::isZero() const {
return this->getCuddAdd().IsZero();
}
bool Add<DdType::CUDD>::isConstant() const {
return Cudd_IsConstant(this->getCuddAdd().getNode());
}
uint_fast64_t Add<DdType::CUDD>::getIndex() const {
return static_cast<uint_fast64_t>(this->getCuddAdd().NodeReadIndex());
}
template<typename ValueType>
std::vector<ValueType> Add<DdType::CUDD>::toVector() const {
return this->toVector<ValueType>(Odd<DdType::CUDD>(*this));
}
template<typename ValueType>
std::vector<ValueType> Add<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const {
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> Add<DdType::CUDD>::toMatrix() const {
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->sumAbstract(rowVariables)), Odd<DdType::CUDD>(this->sumAbstract(columnVariables)));
}
storm::storage::SparseMatrix<double> Add<DdType::CUDD>::toMatrix(storm::dd::Odd<DdType::CUDD> const& rowOdd, storm::dd::Odd<DdType::CUDD> const& columnOdd) const {
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> Add<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 {
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());
}
}
std::sort(ddRowVariableIndices.begin(), ddRowVariableIndices.end());
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());
}
}
std::sort(ddColumnVariableIndices.begin(), ddColumnVariableIndices.end());
// 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> Add<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 {
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);
}
std::sort(ddRowVariableIndices.begin(), ddRowVariableIndices.end());
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);
}
std::sort(ddColumnVariableIndices.begin(), ddColumnVariableIndices.end());
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());
}
}
std::sort(ddGroupVariableIndices.begin(), ddGroupVariableIndices.end());
// 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.
Add<DdType::CUDD> stateToNumberOfChoices = this->notZero().existsAbstract(columnMetaVariables).toAdd().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<Add<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::Add<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).toAdd();
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 Add<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 Add<DdType::CUDD>::splitGroupsRec(DdNode* dd, std::vector<Add<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(Add<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 Add<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);
}
}
template<typename ValueType>
ADD Add<DdType::CUDD>::fromVector(std::shared_ptr<DdManager<DdType::CUDD> const> ddManager, std::vector<ValueType> const& values, Odd<DdType::CUDD> const& odd, std::set<storm::expressions::Variable> const& metaVariables) {
std::vector<uint_fast64_t> ddVariableIndices = getSortedVariableIndices(*ddManager, metaVariables);
uint_fast64_t offset = 0;
return ADD(ddManager->getCuddManager(), fromVectorRec(ddManager->getCuddManager().getManager(), offset, 0, ddVariableIndices.size(), values, odd, ddVariableIndices));
}
template<typename ValueType>
DdNode* Add<DdType::CUDD>::fromVectorRec(::DdManager* manager, uint_fast64_t& currentOffset, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector<ValueType> const& values, Odd<DdType::CUDD> const& odd, std::vector<uint_fast64_t> const& ddVariableIndices) {
if (currentLevel == maxLevel) {
// If we are in a terminal node of the ODD, we need to check whether the then-offset of the ODD is one
// (meaning the encoding is a valid one) or zero (meaning the encoding is not valid). Consequently, we
// need to copy the next value of the vector iff the then-offset is greater than zero.
if (odd.getThenOffset() > 0) {
return Cudd_addConst(manager, values[currentOffset++]);
} else {
return Cudd_ReadZero(manager);
}
} else {
// If the total offset is zero, we can just return the constant zero DD.
if (odd.getThenOffset() + odd.getElseOffset() == 0) {
return Cudd_ReadZero(manager);
}
// Determine the new else-successor.
DdNode* elseSuccessor = nullptr;
if (odd.getElseOffset() > 0) {
elseSuccessor = fromVectorRec(manager, currentOffset, currentLevel + 1, maxLevel, values, odd.getElseSuccessor(), ddVariableIndices);
} else {
elseSuccessor = Cudd_ReadZero(manager);
}
Cudd_Ref(elseSuccessor);
// Determine the new then-successor.
DdNode* thenSuccessor = nullptr;
if (odd.getThenOffset() > 0) {
thenSuccessor = fromVectorRec(manager, currentOffset, currentLevel + 1, maxLevel, values, odd.getThenSuccessor(), ddVariableIndices);
} else {
thenSuccessor = Cudd_ReadZero(manager);
}
Cudd_Ref(thenSuccessor);
// Create a node representing ITE(currentVar, thenSuccessor, elseSuccessor);
DdNode* result = Cudd_addIthVar(manager, static_cast<int>(ddVariableIndices[currentLevel]));
Cudd_Ref(result);
DdNode* newResult = Cudd_addIte(manager, result, thenSuccessor, elseSuccessor);
Cudd_Ref(newResult);
// Dispose of the intermediate results
Cudd_RecursiveDeref(manager, result);
Cudd_RecursiveDeref(manager, thenSuccessor);
Cudd_RecursiveDeref(manager, elseSuccessor);
// Before returning, we remove the protection imposed by the previous call to Cudd_Ref.
Cudd_Deref(newResult);
return newResult;
}
}
void Add<DdType::CUDD>::exportToDot(std::string const& filename) const {
if (filename.empty()) {
std::vector<ADD> cuddAddVector = { this->getCuddAdd() };
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");
std::vector<ADD> cuddAddVector = { this->getCuddAdd() };
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;
}
}
}
DdForwardIterator<DdType::CUDD> Add<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const {
int* cube;
double value;
DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value);
return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, (Cudd_IsGenEmpty(generator) != 0), &this->getContainedMetaVariables(), enumerateDontCareMetaVariables);
}
DdForwardIterator<DdType::CUDD> Add<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const {
return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables);
}
std::ostream& operator<<(std::ostream& out, const Add<DdType::CUDD>& add) {
add.exportToDot();
return out;
}
// Explicitly instantiate some templated functions.
template std::vector<double> Add<DdType::CUDD>::toVector() const;
template std::vector<double> Add<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const;
template void Add<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> Add<DdType::CUDD>::toVector() const;
template std::vector<uint_fast64_t> Add<DdType::CUDD>::toVector(Odd<DdType::CUDD> const& rowOdd) const;
template void Add<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;
}
}