#include #include #include "src/storage/dd/CuddAdd.h" #include "src/storage/dd/CuddBdd.h" #include "src/storage/dd/CuddOdd.h" #include "src/storage/dd/CuddDdManager.h" #include "src/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::Add(std::shared_ptr const> ddManager, ADD cuddAdd, std::set const& containedMetaVariables) : Dd(ddManager, containedMetaVariables), cuddAdd(cuddAdd) { // Intentionally left empty. } Add::Add(std::shared_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables) : Dd(ddManager, metaVariables) { cuddAdd = fromVector(ddManager, values, odd, metaVariables); } Bdd Add::toBdd() const { return Bdd(this->getDdManager(), this->getCuddAdd().BddPattern(), this->getContainedMetaVariables()); } ADD Add::getCuddAdd() const { return this->cuddAdd; } DdNode* Add::getCuddDdNode() const { return this->getCuddAdd().getNode(); } bool Add::operator==(Add const& other) const { return this->getCuddAdd() == other.getCuddAdd(); } bool Add::operator!=(Add const& other) const { return !(*this == other); } Add Add::ite(Add const& thenDd, Add const& elseDd) const { std::set metaVariableNames; std::set_union(thenDd.getContainedMetaVariables().begin(), thenDd.getContainedMetaVariables().end(), elseDd.getContainedMetaVariables().begin(), elseDd.getContainedMetaVariables().end(), std::inserter(metaVariableNames, metaVariableNames.begin())); metaVariableNames.insert(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end()); return Add(this->getDdManager(), this->getCuddAdd().Ite(thenDd.getCuddAdd(), elseDd.getCuddAdd()), metaVariableNames); } Add Add::operator!() const { return Add(this->getDdManager(), ~this->getCuddAdd(), this->getContainedMetaVariables()); } Add Add::operator||(Add const& other) const { Add result(*this); result |= other; return result; } Add& Add::operator|=(Add const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddAdd = this->getCuddAdd() | other.getCuddAdd(); return *this; } Add Add::operator+(Add const& other) const { Add result(*this); result += other; return result; } Add& Add::operator+=(Add const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddAdd = this->getCuddAdd() + other.getCuddAdd(); return *this; } Add Add::operator*(Add const& other) const { Add result(*this); result *= other; return result; } Add& Add::operator*=(Add const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddAdd = this->getCuddAdd() * other.getCuddAdd(); return *this; } Add Add::operator-(Add const& other) const { Add result(*this); result -= other; return result; } Add Add::operator-() const { return this->getDdManager()->getAddZero() - *this; } Add& Add::operator-=(Add const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddAdd = this->getCuddAdd() - other.getCuddAdd(); return *this; } Add Add::operator/(Add const& other) const { Add result(*this); result /= other; return result; } Add& Add::operator/=(Add const& other) { this->addMetaVariables(other.getContainedMetaVariables()); this->cuddAdd = this->getCuddAdd().Divide(other.getCuddAdd()); return *this; } Add Add::equals(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Equals(other.getCuddAdd()), metaVariables); } Add Add::notEquals(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().NotEquals(other.getCuddAdd()), metaVariables); } Add Add::less(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().LessThan(other.getCuddAdd()), metaVariables); } Add Add::lessOrEqual(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().LessThanOrEqual(other.getCuddAdd()), metaVariables); } Add Add::greater(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().GreaterThan(other.getCuddAdd()), metaVariables); } Add Add::greaterOrEqual(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().GreaterThanOrEqual(other.getCuddAdd()), metaVariables); } Add Add::pow(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Pow(other.getCuddAdd()), metaVariables); } Add Add::mod(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Mod(other.getCuddAdd()), metaVariables); } Add Add::logxy(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().LogXY(other.getCuddAdd()), metaVariables); } Add Add::floor() const { return Add(this->getDdManager(), this->getCuddAdd().Floor(), this->getContainedMetaVariables()); } Add Add::ceil() const { return Add(this->getDdManager(), this->getCuddAdd().Ceil(), this->getContainedMetaVariables()); } Add Add::minimum(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Minimum(other.getCuddAdd()), metaVariables); } Add Add::maximum(Add const& other) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), other.getContainedMetaVariables().begin(), other.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Maximum(other.getCuddAdd()), metaVariables); } Add Add::sumAbstract(std::set const& metaVariables) const { Bdd cubeDd = this->getDdManager()->getBddOne(); std::set 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 const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeDd &= ddMetaVariable.getCube(); } return Add(this->getDdManager(), this->getCuddAdd().ExistAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables); } Add Add::minAbstract(std::set const& metaVariables) const { Bdd cubeDd = this->getDdManager()->getBddOne(); std::set 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 const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeDd &= ddMetaVariable.getCube(); } return Add(this->getDdManager(), this->getCuddAdd().MinAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables); } Add Add::maxAbstract(std::set const& metaVariables) const { Bdd cubeDd = this->getDdManager()->getBddOne(); std::set 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 const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); cubeDd &= ddMetaVariable.getCube(); } return Add(this->getDdManager(), this->getCuddAdd().MaxAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables); } bool Add::equalModuloPrecision(Add 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 Add::swapVariables(std::vector> const& metaVariablePairs) { std::set newContainedMetaVariables; std::vector from; std::vector to; for (auto const& metaVariablePair : metaVariablePairs) { DdMetaVariable const& variable1 = this->getDdManager()->getMetaVariable(metaVariablePair.first); DdMetaVariable const& variable2 = this->getDdManager()->getMetaVariable(metaVariablePair.second); // Check if it's legal so swap the meta variables. 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(this->getDdManager(), this->getCuddAdd().SwapVariables(from, to), newContainedMetaVariables); } Add Add::multiplyMatrix(Add const& otherMatrix, std::set const& summationMetaVariables) const { // Create the CUDD summation variables. std::vector summationDdVariables; for (auto const& metaVariable : summationMetaVariables) { for (auto const& ddVariable : this->getDdManager()->getMetaVariable(metaVariable).getDdVariables()) { summationDdVariables.push_back(ddVariable.toAdd().getCuddAdd()); } } std::set unionOfMetaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), otherMatrix.getContainedMetaVariables().begin(), otherMatrix.getContainedMetaVariables().end(), std::inserter(unionOfMetaVariables, unionOfMetaVariables.begin())); std::set containedMetaVariables; std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), summationMetaVariables.begin(), summationMetaVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().MatrixMultiply(otherMatrix.getCuddAdd(), summationDdVariables), containedMetaVariables); } Bdd Add::greater(double value) const { return Bdd(this->getDdManager(), this->getCuddAdd().BddStrictThreshold(value), this->getContainedMetaVariables()); } Bdd Add::greaterOrEqual(double value) const { return Bdd(this->getDdManager(), this->getCuddAdd().BddThreshold(value), this->getContainedMetaVariables()); } Bdd Add::less(double value) const { return Bdd(this->getDdManager(), ~this->getCuddAdd().BddThreshold(value), this->getContainedMetaVariables()); } Bdd Add::lessOrEqual(double value) const { return Bdd(this->getDdManager(), ~this->getCuddAdd().BddStrictThreshold(value), this->getContainedMetaVariables()); } Bdd Add::notZero() const { return this->toBdd(); } Add Add::constrain(Add const& constraint) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Constrain(constraint.getCuddAdd()), metaVariables); } Add Add::restrict(Add const& constraint) const { std::set metaVariables; std::set_union(this->getContainedMetaVariables().begin(), this->getContainedMetaVariables().end(), constraint.getContainedMetaVariables().begin(), constraint.getContainedMetaVariables().end(), std::inserter(metaVariables, metaVariables.begin())); return Add(this->getDdManager(), this->getCuddAdd().Restrict(constraint.getCuddAdd()), metaVariables); } Bdd Add::getSupport() const { return Bdd(this->getDdManager(), this->getCuddAdd().Support(), this->getContainedMetaVariables()); } uint_fast64_t Add::getNonZeroCount() const { std::size_t numberOfDdVariables = 0; for (auto const& metaVariable : this->getContainedMetaVariables()) { numberOfDdVariables += this->getDdManager()->getMetaVariable(metaVariable).getNumberOfDdVariables(); } return static_cast(this->getCuddAdd().CountMinterm(static_cast(numberOfDdVariables))); } uint_fast64_t Add::getLeafCount() const { return static_cast(this->getCuddAdd().CountLeaves()); } uint_fast64_t Add::getNodeCount() const { return static_cast(this->getCuddAdd().nodeCount()); } double Add::getMin() const { ADD constantMinAdd = this->getCuddAdd().FindMin(); return static_cast(Cudd_V(constantMinAdd.getNode())); } double Add::getMax() const { ADD constantMaxAdd = this->getCuddAdd().FindMax(); return static_cast(Cudd_V(constantMaxAdd.getNode())); } void Add::setValue(storm::expressions::Variable const& metaVariable, int_fast64_t variableValue, double targetValue) { std::map metaVariableToValueMap; metaVariableToValueMap.emplace(metaVariable, variableValue); this->setValue(metaVariableToValueMap, targetValue); } void Add::setValue(storm::expressions::Variable const& metaVariable1, int_fast64_t variableValue1, storm::expressions::Variable const& metaVariable2, int_fast64_t variableValue2, double targetValue) { std::map metaVariableToValueMap; metaVariableToValueMap.emplace(metaVariable1, variableValue1); metaVariableToValueMap.emplace(metaVariable2, variableValue2); this->setValue(metaVariableToValueMap, targetValue); } void Add::setValue(std::map const& metaVariableToValueMap, double targetValue) { Bdd 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::getValue(std::map const& metaVariableToValueMap) const { std::set remainingMetaVariables(this->getContainedMetaVariables()); Bdd 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 value = *this * valueEncoding.toAdd(); value = value.sumAbstract(this->getContainedMetaVariables()); return static_cast(Cudd_V(value.getCuddAdd().getNode())); } bool Add::isOne() const { return this->getCuddAdd().IsOne(); } bool Add::isZero() const { return this->getCuddAdd().IsZero(); } bool Add::isConstant() const { return Cudd_IsConstant(this->getCuddAdd().getNode()); } uint_fast64_t Add::getIndex() const { return static_cast(this->getCuddAdd().NodeReadIndex()); } template std::vector Add::toVector() const { return this->toVector(Odd(*this)); } template std::vector Add::toVector(Odd const& rowOdd) const { std::vector result(rowOdd.getTotalOffset()); std::vector ddVariableIndices = this->getSortedVariableIndices(); addToVectorRec(this->getCuddDdNode(), 0, ddVariableIndices.size(), 0, rowOdd, ddVariableIndices, result); return result; } storm::storage::SparseMatrix Add::toMatrix() const { std::set rowVariables; std::set 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(this->sumAbstract(rowVariables)), Odd(this->sumAbstract(columnVariables))); } storm::storage::SparseMatrix Add::toMatrix(storm::dd::Odd const& rowOdd, storm::dd::Odd const& columnOdd) const { std::set rowMetaVariables; std::set 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 Add::toMatrix(std::set const& rowMetaVariables, std::set const& columnMetaVariables, storm::dd::Odd const& rowOdd, storm::dd::Odd const& columnOdd) const { std::vector ddRowVariableIndices; std::vector ddColumnVariableIndices; for (auto const& variable : rowMetaVariables) { DdMetaVariable 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 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 rowIndications(rowOdd.getTotalOffset() + 1); std::vector> columnsAndValues(this->getNonZeroCount()); // Create a trivial row grouping. std::vector 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(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(trivialRowGroupIndices)); } storm::storage::SparseMatrix Add::toMatrix(std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& groupMetaVariables, storm::dd::Odd const& rowOdd, storm::dd::Odd const& columnOdd) const { std::vector ddRowVariableIndices; std::vector ddColumnVariableIndices; std::vector ddGroupVariableIndices; std::set rowAndColumnMetaVariables; for (auto const& variable : rowMetaVariables) { DdMetaVariable 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 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 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 stateToNumberOfChoices = this->notZero().existsAbstract(columnMetaVariables).toAdd().sumAbstract(groupMetaVariables); std::vector rowGroupIndices = stateToNumberOfChoices.toVector(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> groups; splitGroupsRec(this->getCuddDdNode(), groups, ddGroupVariableIndices, 0, ddGroupVariableIndices.size(), rowAndColumnMetaVariables); // Create the actual storage for the non-zero entries. std::vector> columnsAndValues(this->getNonZeroCount()); // Now compute the indices at which the individual rows start. std::vector rowIndications(rowGroupIndices.back() + 1); std::vector> 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(columnOdd.getTotalOffset(), std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices)); } void Add::toMatrixRec(DdNode const* dd, std::vector& rowIndications, std::vector>& columnsAndValues, std::vector const& rowGroupOffsets, Odd const& rowOdd, Odd const& columnOdd, uint_fast64_t currentRowLevel, uint_fast64_t currentColumnLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, uint_fast64_t currentColumnOffset, std::vector const& ddRowVariableIndices, std::vector const& ddColumnVariableIndices, bool generateValues) const { // For the empty DD, we do not need to add any entries. if (dd == Cudd_ReadZero(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(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::splitGroupsRec(DdNode* dd, std::vector>& groups, std::vector const& ddGroupVariableIndices, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::set 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(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 void Add::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd const& odd, std::vector const& ddVariableIndices, std::vector& 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(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 ADD Add::fromVector(std::shared_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables) { std::vector 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 DdNode* Add::fromVectorRec(::DdManager* manager, uint_fast64_t& currentOffset, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector const& values, Odd const& odd, std::vector const& ddVariableIndices) { if (currentLevel == maxLevel) { // If we are in a terminal node of the ODD, we need to check whether the then-offset of the ODD is one // (meaning the encoding is a valid one) or zero (meaning the encoding is not valid). Consequently, we // need to copy the next value of the vector iff the then-offset is greater than zero. if (odd.getThenOffset() > 0) { return Cudd_addConst(manager, values[currentOffset++]); } else { return Cudd_ReadZero(manager); } } else { // If the total offset is zero, we can just return the constant zero DD. if (odd.getThenOffset() + odd.getElseOffset() == 0) { return Cudd_ReadZero(manager); } // Determine the new else-successor. DdNode* elseSuccessor = nullptr; if (odd.getElseOffset() > 0) { elseSuccessor = fromVectorRec(manager, currentOffset, currentLevel + 1, maxLevel, values, odd.getElseSuccessor(), ddVariableIndices); } else { elseSuccessor = Cudd_ReadZero(manager); } Cudd_Ref(elseSuccessor); // Determine the new then-successor. DdNode* thenSuccessor = nullptr; if (odd.getThenOffset() > 0) { thenSuccessor = fromVectorRec(manager, currentOffset, currentLevel + 1, maxLevel, values, odd.getThenSuccessor(), ddVariableIndices); } else { thenSuccessor = Cudd_ReadZero(manager); } Cudd_Ref(thenSuccessor); // Create a node representing ITE(currentVar, thenSuccessor, elseSuccessor); DdNode* result = Cudd_addIthVar(manager, static_cast(ddVariableIndices[currentLevel])); Cudd_Ref(result); DdNode* newResult = Cudd_addIte(manager, result, thenSuccessor, elseSuccessor); Cudd_Ref(newResult); // Dispose of the intermediate results Cudd_RecursiveDeref(manager, result); Cudd_RecursiveDeref(manager, thenSuccessor); Cudd_RecursiveDeref(manager, elseSuccessor); // Before returning, we remove the protection imposed by the previous call to Cudd_Ref. Cudd_Deref(newResult); return newResult; } } void Add::exportToDot(std::string const& filename) const { if (filename.empty()) { std::vector cuddAddVector = { this->getCuddAdd() }; this->getDdManager()->getCuddManager().DumpDot(cuddAddVector); } else { // Build the name input of the DD. std::vector ddNames; std::string ddName("f"); ddNames.push_back(new char[ddName.size() + 1]); std::copy(ddName.c_str(), ddName.c_str() + 2, ddNames.back()); // Now build the variables names. std::vector ddVariableNamesAsStrings = this->getDdManager()->getDdVariableNames(); std::vector ddVariableNames; for (auto const& element : ddVariableNamesAsStrings) { ddVariableNames.push_back(new char[element.size() + 1]); std::copy(element.c_str(), element.c_str() + element.size() + 1, ddVariableNames.back()); } // Open the file, dump the DD and close it again. FILE* filePointer = fopen(filename.c_str() , "w"); std::vector 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 Add::begin(bool enumerateDontCareMetaVariables) const { int* cube; double value; DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value); return DdForwardIterator(this->getDdManager(), generator, cube, value, (Cudd_IsGenEmpty(generator) != 0), &this->getContainedMetaVariables(), enumerateDontCareMetaVariables); } DdForwardIterator Add::end(bool enumerateDontCareMetaVariables) const { return DdForwardIterator(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); } std::ostream& operator<<(std::ostream& out, const Add& add) { add.exportToDot(); return out; } // Explicitly instantiate some templated functions. template std::vector Add::toVector() const; template std::vector Add::toVector(Odd const& rowOdd) const; template void Add::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd const& odd, std::vector const& ddVariableIndices, std::vector& targetVector) const; template std::vector Add::toVector() const; template std::vector Add::toVector(Odd const& rowOdd) const; template void Add::addToVectorRec(DdNode const* dd, uint_fast64_t currentLevel, uint_fast64_t maxLevel, uint_fast64_t currentOffset, Odd const& odd, std::vector const& ddVariableIndices, std::vector& targetVector) const; } }