Browse Source
restructured ODD to be independent of the DD library being used
restructured ODD to be independent of the DD library being used
Former-commit-id: 83f08ba203
main
25 changed files with 680 additions and 935 deletions
-
19src/modelchecker/csl/helper/HybridCtmcCslHelper.cpp
-
4src/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp
-
2src/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp
-
12src/modelchecker/prctl/helper/HybridDtmcPrctlHelper.cpp
-
12src/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp
-
10src/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp
-
1src/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp
-
10src/modelchecker/results/HybridQuantitativeCheckResult.cpp
-
8src/modelchecker/results/HybridQuantitativeCheckResult.h
-
29src/storage/dd/Add.cpp
-
30src/storage/dd/Add.h
-
27src/storage/dd/Bdd.cpp
-
26src/storage/dd/Bdd.h
-
4src/storage/dd/Dd.h
-
4src/storage/dd/DdManager.h
-
144src/storage/dd/Odd.cpp
-
145src/storage/dd/Odd.h
-
332src/storage/dd/cudd/CuddOdd.cpp
-
235src/storage/dd/cudd/CuddOdd.h
-
291src/storage/dd/cudd/InternalCuddAdd.cpp
-
48src/storage/dd/cudd/InternalCuddAdd.h
-
133src/storage/dd/cudd/InternalCuddBdd.cpp
-
59src/storage/dd/cudd/InternalCuddBdd.h
-
4src/storage/dd/cudd/InternalCuddDdManager.h
-
26test/functional/storage/CuddDdTest.cpp
@ -1 +1,143 @@ |
|||
#include "src/storage/dd/Odd.h"
|
|||
#include "src/storage/dd/Odd.h"
|
|||
|
|||
#include <set>
|
|||
#include <fstream>
|
|||
#include <boost/algorithm/string/join.hpp>
|
|||
|
|||
#include "src/utility/macros.h"
|
|||
#include "src/exceptions/InvalidArgumentException.h"
|
|||
|
|||
namespace storm { |
|||
namespace dd { |
|||
Odd::Odd(std::shared_ptr<Odd> elseNode, uint_fast64_t elseOffset, std::shared_ptr<Odd> thenNode, uint_fast64_t thenOffset) : elseNode(elseNode), thenNode(thenNode), elseOffset(elseOffset), thenOffset(thenOffset) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
Odd const& Odd::getThenSuccessor() const { |
|||
return *this->thenNode; |
|||
} |
|||
|
|||
Odd const& Odd::getElseSuccessor() const { |
|||
return *this->elseNode; |
|||
} |
|||
|
|||
uint_fast64_t Odd::getElseOffset() const { |
|||
return this->elseOffset; |
|||
} |
|||
|
|||
void Odd::setElseOffset(uint_fast64_t newOffset) { |
|||
this->elseOffset = newOffset; |
|||
} |
|||
|
|||
uint_fast64_t Odd::getThenOffset() const { |
|||
return this->thenOffset; |
|||
} |
|||
|
|||
void Odd::setThenOffset(uint_fast64_t newOffset) { |
|||
this->thenOffset = newOffset; |
|||
} |
|||
|
|||
uint_fast64_t Odd::getTotalOffset() const { |
|||
return this->elseOffset + this->thenOffset; |
|||
} |
|||
|
|||
uint_fast64_t Odd::getNodeCount() const { |
|||
// If the ODD contains a constant (and thus has no children), the size is 1.
|
|||
if (this->elseNode == nullptr && this->thenNode == nullptr) { |
|||
return 1; |
|||
} |
|||
|
|||
// If the two successors are actually the same, we need to count the subnodes only once.
|
|||
if (this->elseNode == this->thenNode) { |
|||
return this->elseNode->getNodeCount(); |
|||
} else { |
|||
return this->elseNode->getNodeCount() + this->thenNode->getNodeCount(); |
|||
} |
|||
} |
|||
|
|||
uint_fast64_t Odd::getHeight() const { |
|||
if (this->isTerminalNode()) { |
|||
return 1; |
|||
} else { |
|||
return 1 + this->getElseSuccessor().getHeight(); |
|||
} |
|||
} |
|||
|
|||
bool Odd::isTerminalNode() const { |
|||
return this->elseNode == nullptr && this->thenNode == nullptr; |
|||
} |
|||
|
|||
void Odd::expandExplicitVector(storm::dd::Odd const& newOdd, std::vector<double> const& oldValues, std::vector<double>& newValues) const { |
|||
expandValuesToVectorRec(0, *this, oldValues, 0, newOdd, newValues); |
|||
} |
|||
|
|||
void Odd::expandValuesToVectorRec(uint_fast64_t oldOffset, storm::dd::Odd const& oldOdd, std::vector<double> const& oldValues, uint_fast64_t newOffset, storm::dd::Odd const& newOdd, std::vector<double>& newValues) { |
|||
if (oldOdd.isTerminalNode()) { |
|||
STORM_LOG_THROW(newOdd.isTerminalNode(), storm::exceptions::InvalidArgumentException, "The ODDs for the translation must have the same height."); |
|||
if (oldOdd.getThenOffset() != 0) { |
|||
newValues[newOffset] += oldValues[oldOffset]; |
|||
} |
|||
} else { |
|||
expandValuesToVectorRec(oldOffset, oldOdd.getElseSuccessor(), oldValues, newOffset, newOdd.getElseSuccessor(), newValues); |
|||
expandValuesToVectorRec(oldOffset + oldOdd.getElseOffset(), oldOdd.getThenSuccessor(), oldValues, newOffset + newOdd.getElseOffset(), newOdd.getThenSuccessor(), newValues); |
|||
} |
|||
} |
|||
|
|||
void Odd::exportToDot(std::string const& filename) const { |
|||
std::ofstream dotFile; |
|||
dotFile.open (filename); |
|||
|
|||
// Print header.
|
|||
dotFile << "digraph \"ODD\" {" << std::endl << "center=true;" << std::endl << "edge [dir = none];" << std::endl; |
|||
|
|||
// Print levels as ranks.
|
|||
dotFile << "{ node [shape = plaintext];" << std::endl << "edge [style = invis];" << std::endl; |
|||
std::vector<std::string> levelNames; |
|||
for (uint_fast64_t level = 0; level < this->getHeight(); ++level) { |
|||
levelNames.push_back("\"" + std::to_string(level) + "\""); |
|||
} |
|||
dotFile << boost::join(levelNames, " -> ") << ";"; |
|||
dotFile << "}" << std::endl; |
|||
|
|||
std::map<uint_fast64_t, std::vector<std::reference_wrapper<storm::dd::Odd const>>> levelToOddNodesMap; |
|||
this->addToLevelToOddNodesMap(levelToOddNodesMap); |
|||
|
|||
for (auto const& levelNodes : levelToOddNodesMap) { |
|||
dotFile << "{ rank = same; \"" << levelNodes.first << "\"" << std::endl;; |
|||
for (auto const& node : levelNodes.second) { |
|||
dotFile << "\"" << &node.get() << "\";" << std::endl; |
|||
} |
|||
dotFile << "}" << std::endl; |
|||
} |
|||
|
|||
std::set<storm::dd::Odd const*> printedNodes; |
|||
for (auto const& levelNodes : levelToOddNodesMap) { |
|||
for (auto const& node : levelNodes.second) { |
|||
if (printedNodes.find(&node.get()) != printedNodes.end()) { |
|||
continue; |
|||
} else { |
|||
printedNodes.insert(&node.get()); |
|||
} |
|||
|
|||
dotFile << "\"" << &node.get() << "\" [label=\"" << levelNodes.first << "\"];" << std::endl; |
|||
if (!node.get().isTerminalNode()) { |
|||
dotFile << "\"" << &node.get() << "\" -> \"" << &node.get().getElseSuccessor() << "\" [style=dashed, label=\"0\"];" << std::endl; |
|||
dotFile << "\"" << &node.get() << "\" -> \"" << &node.get().getThenSuccessor() << "\" [style=solid, label=\"" << node.get().getElseOffset() << "\"];" << std::endl; |
|||
} |
|||
} |
|||
} |
|||
|
|||
dotFile << "}" << std::endl; |
|||
|
|||
dotFile.close(); |
|||
} |
|||
|
|||
void Odd::addToLevelToOddNodesMap(std::map<uint_fast64_t, std::vector<std::reference_wrapper<storm::dd::Odd const>>>& levelToOddNodesMap, uint_fast64_t level) const { |
|||
levelToOddNodesMap[level].push_back(*this); |
|||
if (!this->isTerminalNode()) { |
|||
this->getElseSuccessor().addToLevelToOddNodesMap(levelToOddNodesMap, level + 1); |
|||
this->getThenSuccessor().addToLevelToOddNodesMap(levelToOddNodesMap, level + 1); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,12 +1,151 @@ |
|||
#ifndef STORM_STORAGE_DD_ODD_H_ |
|||
#define STORM_STORAGE_DD_ODD_H_ |
|||
|
|||
#include "src/storage/dd/DdType.h" |
|||
#include <vector> |
|||
#include <map> |
|||
|
|||
namespace storm { |
|||
namespace dd { |
|||
template<DdType Type> |
|||
class Odd; |
|||
class Odd { |
|||
public: |
|||
/*! |
|||
* Constructs an offset-labeled DD with the given topmost DD node, else- and then-successor. |
|||
* |
|||
* @param dd The DD node associated with this ODD node. |
|||
* @param elseNode The else-successor of thie ODD node. |
|||
* @param elseOffset The offset of the else-successor. |
|||
* @param thenNode The then-successor of thie ODD node. |
|||
* @param thenOffset The offset of the then-successor. |
|||
*/ |
|||
Odd(std::shared_ptr<Odd> elseNode, uint_fast64_t elseOffset, std::shared_ptr<Odd> thenNode, uint_fast64_t thenOffset); |
|||
|
|||
// Instantiate all copy/move constructors/assignments with the default implementation. |
|||
Odd() = default; |
|||
Odd(Odd const& other) = default; |
|||
Odd& operator=(Odd const& other) = default; |
|||
#ifndef WINDOWS |
|||
Odd(Odd&& other) = default; |
|||
Odd& operator=(Odd&& other) = default; |
|||
#endif |
|||
|
|||
/*! |
|||
* Retrieves the then-successor of this ODD node. |
|||
* |
|||
* @return The then-successor of this ODD node. |
|||
*/ |
|||
Odd const& getThenSuccessor() const; |
|||
|
|||
/*! |
|||
* Retrieves the else-successor of this ODD node. |
|||
* |
|||
* @return The else-successor of this ODD node. |
|||
*/ |
|||
Odd const& getElseSuccessor() const; |
|||
|
|||
/*! |
|||
* Retrieves the else-offset of this ODD node. |
|||
* |
|||
* @return The else-offset of this ODD node. |
|||
*/ |
|||
uint_fast64_t getElseOffset() const; |
|||
|
|||
/*! |
|||
* Sets the else-offset of this ODD node. |
|||
* |
|||
* @param newOffset The new else-offset of this ODD node. |
|||
*/ |
|||
void setElseOffset(uint_fast64_t newOffset); |
|||
|
|||
/*! |
|||
* Retrieves the then-offset of this ODD node. |
|||
* |
|||
* @return The then-offset of this ODD node. |
|||
*/ |
|||
uint_fast64_t getThenOffset() const; |
|||
|
|||
/*! |
|||
* Sets the then-offset of this ODD node. |
|||
* |
|||
* @param newOffset The new then-offset of this ODD node. |
|||
*/ |
|||
void setThenOffset(uint_fast64_t newOffset); |
|||
|
|||
/*! |
|||
* Retrieves the total offset, i.e., the sum of the then- and else-offset. |
|||
* |
|||
* @return The total offset of this ODD. |
|||
*/ |
|||
uint_fast64_t getTotalOffset() const; |
|||
|
|||
/*! |
|||
* Retrieves the size of the ODD. Note: the size is computed by a traversal, so this may be costlier than |
|||
* expected. |
|||
* |
|||
* @return The size (in nodes) of this ODD. |
|||
*/ |
|||
uint_fast64_t getNodeCount() const; |
|||
|
|||
/*! |
|||
* Retrieves the height of the ODD. |
|||
* |
|||
* @return The height of the ODD. |
|||
*/ |
|||
uint_fast64_t getHeight() const; |
|||
|
|||
/*! |
|||
* Checks whether the given ODD node is a terminal node, i.e. has no successors. |
|||
* |
|||
* @return True iff the node is terminal. |
|||
*/ |
|||
bool isTerminalNode() const; |
|||
|
|||
/*! |
|||
* Adds the old values to the new values. It does so by writing the old values at their correct positions |
|||
* wrt. to the new ODD. |
|||
* |
|||
* @param newOdd The new ODD to use. |
|||
* @param oldValues The old vector of values (which is being read). |
|||
* @param newValues The new vector of values (which is being written). |
|||
*/ |
|||
void expandExplicitVector(storm::dd::Odd const& newOdd, std::vector<double> const& oldValues, std::vector<double>& newValues) const; |
|||
|
|||
/*! |
|||
* Exports the ODD in the dot format to the given file. |
|||
* |
|||
* @param filename The name of the file to which to write the dot output. |
|||
*/ |
|||
void exportToDot(std::string const& filename) const; |
|||
|
|||
private: |
|||
/*! |
|||
* Adds all nodes below the current one to the given mapping. |
|||
* |
|||
* @param levelToOddNodesMap A mapping of the level to the ODD node. |
|||
* @param The level of the current node. |
|||
*/ |
|||
void addToLevelToOddNodesMap(std::map<uint_fast64_t, std::vector<std::reference_wrapper<storm::dd::Odd const>>>& levelToOddNodesMap, uint_fast64_t level = 0) const; |
|||
|
|||
/*! |
|||
* Adds the values of the old explicit values to the new explicit values where the positions in the old vector |
|||
* are given by the current old ODD and the positions in the new vector are given by the new ODD. |
|||
* |
|||
* @param oldOffset The offset in the old explicit values. |
|||
* @param oldOdd The ODD to use for the old explicit values. |
|||
* @param oldValues The vector of old values. |
|||
* @param newOffset The offset in the new explicit values. |
|||
* @param newOdd The ODD to use for the new explicit values. |
|||
* @param newValues The vector of new values. |
|||
*/ |
|||
static void expandValuesToVectorRec(uint_fast64_t oldOffset, storm::dd::Odd const& oldOdd, std::vector<double> const& oldValues, uint_fast64_t newOffset, storm::dd::Odd const& newOdd, std::vector<double>& newValues); |
|||
|
|||
// The then- and else-nodes. |
|||
std::shared_ptr<Odd> elseNode; |
|||
std::shared_ptr<Odd> thenNode; |
|||
|
|||
// The offsets that need to be added if the then- or else-successor is taken, respectively. |
|||
uint_fast64_t elseOffset; |
|||
uint_fast64_t thenOffset; |
|||
}; |
|||
} |
|||
} |
|||
|
@ -1,332 +0,0 @@ |
|||
#include "src/storage/dd/cudd/CuddOdd.h"
|
|||
|
|||
#include <fstream>
|
|||
#include <algorithm>
|
|||
#include <boost/functional/hash.hpp>
|
|||
#include <boost/algorithm/string/join.hpp>
|
|||
|
|||
#include "src/exceptions/InvalidArgumentException.h"
|
|||
#include "src/utility/macros.h"
|
|||
|
|||
#include "src/storage/dd/DdManager.h"
|
|||
#include "src/storage/dd/DdMetaVariable.h"
|
|||
|
|||
namespace storm { |
|||
namespace dd { |
|||
template<typename ValueType> |
|||
Odd<DdType::CUDD>::Odd(Add<DdType::CUDD, ValueType> const& add) { |
|||
std::shared_ptr<DdManager<DdType::CUDD> const> manager = add.getDdManager(); |
|||
|
|||
// First, we need to determine the involved DD variables indices.
|
|||
std::vector<uint_fast64_t> ddVariableIndices = add.getSortedVariableIndices(); |
|||
|
|||
// Prepare a unique table for each level that keeps the constructed ODD nodes unique.
|
|||
std::vector<std::unordered_map<DdNode*, std::shared_ptr<Odd<DdType::CUDD>>>> uniqueTableForLevels(ddVariableIndices.size() + 1); |
|||
|
|||
// Now construct the ODD structure from the ADD.
|
|||
std::shared_ptr<Odd<DdType::CUDD>> rootOdd = buildOddFromAddRec(add.internalAdd.getCuddDdNode(), manager->internalDdManager.getCuddManager(), 0, ddVariableIndices.size(), ddVariableIndices, uniqueTableForLevels); |
|||
|
|||
// Finally, move the children of the root ODD into this ODD.
|
|||
this->elseNode = std::move(rootOdd->elseNode); |
|||
this->thenNode = std::move(rootOdd->thenNode); |
|||
this->elseOffset = rootOdd->elseOffset; |
|||
this->thenOffset = rootOdd->thenOffset; |
|||
} |
|||
|
|||
Odd<DdType::CUDD>::Odd(Bdd<DdType::CUDD> const& bdd) { |
|||
std::shared_ptr<DdManager<DdType::CUDD> const> manager = bdd.getDdManager(); |
|||
|
|||
// First, we need to determine the involved DD variables indices.
|
|||
std::vector<uint_fast64_t> ddVariableIndices = bdd.getSortedVariableIndices(); |
|||
|
|||
// Prepare a unique table for each level that keeps the constructed ODD nodes unique.
|
|||
std::vector<std::unordered_map<std::pair<DdNode*, bool>, std::shared_ptr<Odd<DdType::CUDD>>, HashFunctor>> uniqueTableForLevels(ddVariableIndices.size() + 1); |
|||
|
|||
// Now construct the ODD structure from the BDD.
|
|||
std::shared_ptr<Odd<DdType::CUDD>> rootOdd = buildOddFromBddRec(Cudd_Regular(bdd.internalBdd.getCuddDdNode()), manager->internalDdManager.getCuddManager(), 0, Cudd_IsComplement(bdd.internalBdd.getCuddDdNode()), ddVariableIndices.size(), ddVariableIndices, uniqueTableForLevels); |
|||
|
|||
// Finally, move the children of the root ODD into this ODD.
|
|||
this->elseNode = std::move(rootOdd->elseNode); |
|||
this->thenNode = std::move(rootOdd->thenNode); |
|||
this->elseOffset = rootOdd->elseOffset; |
|||
this->thenOffset = rootOdd->thenOffset; |
|||
} |
|||
|
|||
Odd<DdType::CUDD>::Odd(std::shared_ptr<Odd<DdType::CUDD>> elseNode, uint_fast64_t elseOffset, std::shared_ptr<Odd<DdType::CUDD>> thenNode, uint_fast64_t thenOffset) : elseNode(elseNode), thenNode(thenNode), elseOffset(elseOffset), thenOffset(thenOffset) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
Odd<DdType::CUDD> const& Odd<DdType::CUDD>::getThenSuccessor() const { |
|||
return *this->thenNode; |
|||
} |
|||
|
|||
Odd<DdType::CUDD> const& Odd<DdType::CUDD>::getElseSuccessor() const { |
|||
return *this->elseNode; |
|||
} |
|||
|
|||
uint_fast64_t Odd<DdType::CUDD>::getElseOffset() const { |
|||
return this->elseOffset; |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::setElseOffset(uint_fast64_t newOffset) { |
|||
this->elseOffset = newOffset; |
|||
} |
|||
|
|||
uint_fast64_t Odd<DdType::CUDD>::getThenOffset() const { |
|||
return this->thenOffset; |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::setThenOffset(uint_fast64_t newOffset) { |
|||
this->thenOffset = newOffset; |
|||
} |
|||
|
|||
uint_fast64_t Odd<DdType::CUDD>::getTotalOffset() const { |
|||
return this->elseOffset + this->thenOffset; |
|||
} |
|||
|
|||
uint_fast64_t Odd<DdType::CUDD>::getNodeCount() const { |
|||
// If the ODD contains a constant (and thus has no children), the size is 1.
|
|||
if (this->elseNode == nullptr && this->thenNode == nullptr) { |
|||
return 1; |
|||
} |
|||
|
|||
// If the two successors are actually the same, we need to count the subnodes only once.
|
|||
if (this->elseNode == this->thenNode) { |
|||
return this->elseNode->getNodeCount(); |
|||
} else { |
|||
return this->elseNode->getNodeCount() + this->thenNode->getNodeCount(); |
|||
} |
|||
} |
|||
|
|||
uint_fast64_t Odd<DdType::CUDD>::getHeight() const { |
|||
if (this->isTerminalNode()) { |
|||
return 1; |
|||
} else { |
|||
return 1 + this->getElseSuccessor().getHeight(); |
|||
} |
|||
} |
|||
|
|||
bool Odd<DdType::CUDD>::isTerminalNode() const { |
|||
return this->elseNode == nullptr && this->thenNode == nullptr; |
|||
} |
|||
|
|||
std::vector<double> Odd<DdType::CUDD>::filterExplicitVector(storm::dd::Bdd<DdType::CUDD> const& selectedValues, std::vector<double> const& values) const { |
|||
std::vector<double> result(selectedValues.getNonZeroCount()); |
|||
|
|||
// First, we need to determine the involved DD variables indices.
|
|||
std::vector<uint_fast64_t> ddVariableIndices = selectedValues.getSortedVariableIndices(); |
|||
|
|||
uint_fast64_t currentIndex = 0; |
|||
addSelectedValuesToVectorRec(selectedValues.internalBdd.getCuddDdNode(), selectedValues.getDdManager()->internalDdManager.getCuddManager(), 0, Cudd_IsComplement(selectedValues.internalBdd.getCuddDdNode()), ddVariableIndices.size(), ddVariableIndices, 0, *this, result, currentIndex, values); |
|||
return result; |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::expandExplicitVector(storm::dd::Odd<DdType::CUDD> const& newOdd, std::vector<double> const& oldValues, std::vector<double>& newValues) const { |
|||
expandValuesToVectorRec(0, *this, oldValues, 0, newOdd, newValues); |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::expandValuesToVectorRec(uint_fast64_t oldOffset, storm::dd::Odd<DdType::CUDD> const& oldOdd, std::vector<double> const& oldValues, uint_fast64_t newOffset, storm::dd::Odd<DdType::CUDD> const& newOdd, std::vector<double>& newValues) { |
|||
if (oldOdd.isTerminalNode()) { |
|||
STORM_LOG_THROW(newOdd.isTerminalNode(), storm::exceptions::InvalidArgumentException, "The ODDs for the translation must have the same height."); |
|||
if (oldOdd.getThenOffset() != 0) { |
|||
newValues[newOffset] += oldValues[oldOffset]; |
|||
} |
|||
} else { |
|||
expandValuesToVectorRec(oldOffset, oldOdd.getElseSuccessor(), oldValues, newOffset, newOdd.getElseSuccessor(), newValues); |
|||
expandValuesToVectorRec(oldOffset + oldOdd.getElseOffset(), oldOdd.getThenSuccessor(), oldValues, newOffset + newOdd.getElseOffset(), newOdd.getThenSuccessor(), newValues); |
|||
} |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::addSelectedValuesToVectorRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, bool complement, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, uint_fast64_t currentOffset, storm::dd::Odd<DdType::CUDD> const& odd, std::vector<double>& result, uint_fast64_t& currentIndex, std::vector<double> const& values) { |
|||
// If there are no more values to select, we can directly return.
|
|||
if (dd == Cudd_ReadLogicZero(manager.getManager()) && !complement) { |
|||
return; |
|||
} else if (dd == Cudd_ReadOne(manager.getManager()) && complement) { |
|||
return; |
|||
} |
|||
|
|||
if (currentLevel == maxLevel) { |
|||
// If the DD is not the zero leaf, then the then-offset is 1.
|
|||
bool selected = false; |
|||
if (dd != Cudd_ReadLogicZero(manager.getManager())) { |
|||
selected = !complement; |
|||
} |
|||
|
|||
if (selected) { |
|||
result[currentIndex++] = values[currentOffset]; |
|||
} |
|||
} 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.
|
|||
addSelectedValuesToVectorRec(dd, manager, currentLevel + 1, complement, maxLevel, ddVariableIndices, currentOffset, odd.getElseSuccessor(), result, currentIndex, values); |
|||
addSelectedValuesToVectorRec(dd, manager, currentLevel + 1, complement, maxLevel, ddVariableIndices, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), result, currentIndex, values); |
|||
} else { |
|||
// Otherwise, we compute the ODDs for both the then- and else successors.
|
|||
DdNode* thenDdNode = Cudd_T(dd); |
|||
DdNode* elseDdNode = Cudd_E(dd); |
|||
|
|||
// Determine whether we have to evaluate the successors as if they were complemented.
|
|||
bool elseComplemented = Cudd_IsComplement(elseDdNode) ^ complement; |
|||
bool thenComplemented = Cudd_IsComplement(thenDdNode) ^ complement; |
|||
|
|||
addSelectedValuesToVectorRec(Cudd_Regular(elseDdNode), manager, currentLevel + 1, elseComplemented, maxLevel, ddVariableIndices, currentOffset, odd.getElseSuccessor(), result, currentIndex, values); |
|||
addSelectedValuesToVectorRec(Cudd_Regular(thenDdNode), manager, currentLevel + 1, thenComplemented, maxLevel, ddVariableIndices, currentOffset + odd.getElseOffset(), odd.getThenSuccessor(), result, currentIndex, values); |
|||
} |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::exportToDot(std::string const& filename) const { |
|||
std::ofstream dotFile; |
|||
dotFile.open (filename); |
|||
|
|||
// Print header.
|
|||
dotFile << "digraph \"ODD\" {" << std::endl << "center=true;" << std::endl << "edge [dir = none];" << std::endl; |
|||
|
|||
// Print levels as ranks.
|
|||
dotFile << "{ node [shape = plaintext];" << std::endl << "edge [style = invis];" << std::endl; |
|||
std::vector<std::string> levelNames; |
|||
for (uint_fast64_t level = 0; level < this->getHeight(); ++level) { |
|||
levelNames.push_back("\"" + std::to_string(level) + "\""); |
|||
} |
|||
dotFile << boost::join(levelNames, " -> ") << ";"; |
|||
dotFile << "}" << std::endl; |
|||
|
|||
std::map<uint_fast64_t, std::vector<std::reference_wrapper<storm::dd::Odd<DdType::CUDD> const>>> levelToOddNodesMap; |
|||
this->addToLevelToOddNodesMap(levelToOddNodesMap); |
|||
|
|||
for (auto const& levelNodes : levelToOddNodesMap) { |
|||
dotFile << "{ rank = same; \"" << levelNodes.first << "\"" << std::endl;; |
|||
for (auto const& node : levelNodes.second) { |
|||
dotFile << "\"" << &node.get() << "\";" << std::endl; |
|||
} |
|||
dotFile << "}" << std::endl; |
|||
} |
|||
|
|||
std::set<storm::dd::Odd<DdType::CUDD> const*> printedNodes; |
|||
for (auto const& levelNodes : levelToOddNodesMap) { |
|||
for (auto const& node : levelNodes.second) { |
|||
if (printedNodes.find(&node.get()) != printedNodes.end()) { |
|||
continue; |
|||
} else { |
|||
printedNodes.insert(&node.get()); |
|||
} |
|||
|
|||
dotFile << "\"" << &node.get() << "\" [label=\"" << levelNodes.first << "\"];" << std::endl; |
|||
if (!node.get().isTerminalNode()) { |
|||
dotFile << "\"" << &node.get() << "\" -> \"" << &node.get().getElseSuccessor() << "\" [style=dashed, label=\"0\"];" << std::endl; |
|||
dotFile << "\"" << &node.get() << "\" -> \"" << &node.get().getThenSuccessor() << "\" [style=solid, label=\"" << node.get().getElseOffset() << "\"];" << std::endl; |
|||
} |
|||
} |
|||
} |
|||
|
|||
dotFile << "}" << std::endl; |
|||
|
|||
dotFile.close(); |
|||
} |
|||
|
|||
void Odd<DdType::CUDD>::addToLevelToOddNodesMap(std::map<uint_fast64_t, std::vector<std::reference_wrapper<storm::dd::Odd<DdType::CUDD> const>>>& levelToOddNodesMap, uint_fast64_t level) const { |
|||
levelToOddNodesMap[level].push_back(*this); |
|||
if (!this->isTerminalNode()) { |
|||
this->getElseSuccessor().addToLevelToOddNodesMap(levelToOddNodesMap, level + 1); |
|||
this->getThenSuccessor().addToLevelToOddNodesMap(levelToOddNodesMap, level + 1); |
|||
} |
|||
} |
|||
|
|||
std::shared_ptr<Odd<DdType::CUDD>> Odd<DdType::CUDD>::buildOddFromAddRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<std::unordered_map<DdNode*, std::shared_ptr<Odd<DdType::CUDD>>>>& uniqueTableForLevels) { |
|||
// Check whether the ODD for this node has already been computed (for this level) and if so, return this instead.
|
|||
auto const& iterator = uniqueTableForLevels[currentLevel].find(dd); |
|||
if (iterator != uniqueTableForLevels[currentLevel].end()) { |
|||
return iterator->second; |
|||
} else { |
|||
// Otherwise, we need to recursively compute the ODD.
|
|||
|
|||
// If we are already past the maximal level that is to be considered, we can simply create an Odd without
|
|||
// successors
|
|||
if (currentLevel == maxLevel) { |
|||
uint_fast64_t elseOffset = 0; |
|||
uint_fast64_t thenOffset = 0; |
|||
|
|||
// If the DD is not the zero leaf, then the then-offset is 1.
|
|||
if (dd != Cudd_ReadZero(manager.getManager())) { |
|||
thenOffset = 1; |
|||
} |
|||
|
|||
return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(nullptr, elseOffset, nullptr, thenOffset)); |
|||
} else if (ddVariableIndices[currentLevel] < static_cast<uint_fast64_t>(dd->index)) { |
|||
// If we skipped the level in the DD, we compute the ODD just for the else-successor and use the same
|
|||
// node for the then-successor as well.
|
|||
std::shared_ptr<Odd<DdType::CUDD>> elseNode = buildOddFromAddRec(dd, manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); |
|||
std::shared_ptr<Odd<DdType::CUDD>> thenNode = elseNode; |
|||
return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(elseNode, elseNode->getElseOffset() + elseNode->getThenOffset(), thenNode, thenNode->getElseOffset() + thenNode->getThenOffset())); |
|||
} else { |
|||
// Otherwise, we compute the ODDs for both the then- and else successors.
|
|||
bool elseComplemented = Cudd_IsComplement(Cudd_E(dd)); |
|||
bool thenComplemented = Cudd_IsComplement(Cudd_T(dd)); |
|||
std::shared_ptr<Odd<DdType::CUDD>> elseNode = buildOddFromAddRec(Cudd_E(dd), manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); |
|||
std::shared_ptr<Odd<DdType::CUDD>> thenNode = buildOddFromAddRec(Cudd_T(dd), manager, currentLevel + 1, maxLevel, ddVariableIndices, uniqueTableForLevels); |
|||
uint_fast64_t totalElseOffset = elseNode->getElseOffset() + elseNode->getThenOffset(); |
|||
uint_fast64_t totalThenOffset = thenNode->getElseOffset() + thenNode->getThenOffset(); |
|||
return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(elseNode, elseComplemented ? (1ull << (maxLevel - currentLevel - 1)) - totalElseOffset : totalElseOffset, thenNode, thenComplemented ? (1ull << (maxLevel - currentLevel - 1)) - totalThenOffset : totalThenOffset)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
std::size_t Odd<DdType::CUDD>::HashFunctor::operator()(std::pair<DdNode*, bool> const& key) const { |
|||
std::size_t result = 0; |
|||
boost::hash_combine(result, key.first); |
|||
boost::hash_combine(result, key.second); |
|||
return result; |
|||
} |
|||
|
|||
std::shared_ptr<Odd<DdType::CUDD>> Odd<DdType::CUDD>::buildOddFromBddRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, bool complement, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<std::unordered_map<std::pair<DdNode*, bool>, std::shared_ptr<Odd<DdType::CUDD>>, HashFunctor>>& uniqueTableForLevels) { |
|||
// Check whether the ODD for this node has already been computed (for this level) and if so, return this instead.
|
|||
auto const& iterator = uniqueTableForLevels[currentLevel].find(std::make_pair(dd, complement)); |
|||
if (iterator != uniqueTableForLevels[currentLevel].end()) { |
|||
return iterator->second; |
|||
} else { |
|||
// Otherwise, we need to recursively compute the ODD.
|
|||
|
|||
// If we are already past the maximal level that is to be considered, we can simply create an Odd without
|
|||
// successors
|
|||
if (currentLevel == maxLevel) { |
|||
uint_fast64_t elseOffset = 0; |
|||
uint_fast64_t thenOffset = 0; |
|||
|
|||
// If the DD is not the zero leaf, then the then-offset is 1.
|
|||
if (dd != Cudd_ReadZero(manager.getManager())) { |
|||
thenOffset = 1; |
|||
} |
|||
|
|||
// If we need to complement the 'terminal' node, we need to negate its offset.
|
|||
if (complement) { |
|||
thenOffset = 1 - thenOffset; |
|||
} |
|||
|
|||
return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(nullptr, elseOffset, nullptr, thenOffset)); |
|||
} else if (ddVariableIndices[currentLevel] < static_cast<uint_fast64_t>(dd->index)) { |
|||
// If we skipped the level in the DD, we compute the ODD just for the else-successor and use the same
|
|||
// node for the then-successor as well.
|
|||
std::shared_ptr<Odd<DdType::CUDD>> elseNode = buildOddFromBddRec(dd, manager, currentLevel + 1, complement, maxLevel, ddVariableIndices, uniqueTableForLevels); |
|||
std::shared_ptr<Odd<DdType::CUDD>> thenNode = elseNode; |
|||
uint_fast64_t totalOffset = elseNode->getElseOffset() + elseNode->getThenOffset(); |
|||
return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(elseNode, totalOffset, thenNode, totalOffset)); |
|||
} else { |
|||
// Otherwise, we compute the ODDs for both the then- and else successors.
|
|||
DdNode* thenDdNode = Cudd_T(dd); |
|||
DdNode* elseDdNode = Cudd_E(dd); |
|||
|
|||
// Determine whether we have to evaluate the successors as if they were complemented.
|
|||
bool elseComplemented = Cudd_IsComplement(elseDdNode) ^ complement; |
|||
bool thenComplemented = Cudd_IsComplement(thenDdNode) ^ complement; |
|||
|
|||
std::shared_ptr<Odd<DdType::CUDD>> elseNode = buildOddFromBddRec(Cudd_Regular(elseDdNode), manager, currentLevel + 1, elseComplemented, maxLevel, ddVariableIndices, uniqueTableForLevels); |
|||
std::shared_ptr<Odd<DdType::CUDD>> thenNode = buildOddFromBddRec(Cudd_Regular(thenDdNode), manager, currentLevel + 1, thenComplemented, maxLevel, ddVariableIndices, uniqueTableForLevels); |
|||
|
|||
return std::shared_ptr<Odd<DdType::CUDD>>(new Odd<DdType::CUDD>(elseNode, elseNode->getElseOffset() + elseNode->getThenOffset(), thenNode, thenNode->getElseOffset() + thenNode->getThenOffset())); |
|||
} |
|||
} |
|||
} |
|||
|
|||
template Odd<DdType::CUDD>::Odd(Add<DdType::CUDD, double> const& add); |
|||
template Odd<DdType::CUDD>::Odd(Add<DdType::CUDD, uint_fast64_t> const& add); |
|||
} |
|||
} |
@ -1,235 +0,0 @@ |
|||
#ifndef STORM_STORAGE_DD_CUDDODD_H_ |
|||
#define STORM_STORAGE_DD_CUDDODD_H_ |
|||
|
|||
#include <memory> |
|||
#include <unordered_map> |
|||
|
|||
#include "src/storage/dd/Odd.h" |
|||
#include "src/storage/dd/Add.h" |
|||
#include "src/utility/OsDetection.h" |
|||
|
|||
// Include the C++-interface of CUDD. |
|||
#include "cuddObj.hh" |
|||
|
|||
namespace storm { |
|||
namespace dd { |
|||
template<> |
|||
class Odd<DdType::CUDD> { |
|||
public: |
|||
/*! |
|||
* Constructs an offset-labeled DD from the given ADD. |
|||
* |
|||
* @param add The ADD for which to build the offset-labeled ADD. |
|||
*/ |
|||
template<typename ValueType> |
|||
Odd(Add<DdType::CUDD, ValueType> const& add); |
|||
|
|||
/*! |
|||
* Constructs an offset-labeled DD from the given BDD. |
|||
* |
|||
* @param bdd The BDD for which to build the offset-labeled ADD. |
|||
*/ |
|||
Odd(Bdd<DdType::CUDD> const& bdd); |
|||
|
|||
// Instantiate all copy/move constructors/assignments with the default implementation. |
|||
Odd() = default; |
|||
Odd(Odd<DdType::CUDD> const& other) = default; |
|||
Odd& operator=(Odd<DdType::CUDD> const& other) = default; |
|||
#ifndef WINDOWS |
|||
Odd(Odd<DdType::CUDD>&& other) = default; |
|||
Odd& operator=(Odd<DdType::CUDD>&& other) = default; |
|||
#endif |
|||
|
|||
/*! |
|||
* Retrieves the then-successor of this ODD node. |
|||
* |
|||
* @return The then-successor of this ODD node. |
|||
*/ |
|||
Odd<DdType::CUDD> const& getThenSuccessor() const; |
|||
|
|||
/*! |
|||
* Retrieves the else-successor of this ODD node. |
|||
* |
|||
* @return The else-successor of this ODD node. |
|||
*/ |
|||
Odd<DdType::CUDD> const& getElseSuccessor() const; |
|||
|
|||
/*! |
|||
* Retrieves the else-offset of this ODD node. |
|||
* |
|||
* @return The else-offset of this ODD node. |
|||
*/ |
|||
uint_fast64_t getElseOffset() const; |
|||
|
|||
/*! |
|||
* Sets the else-offset of this ODD node. |
|||
* |
|||
* @param newOffset The new else-offset of this ODD node. |
|||
*/ |
|||
void setElseOffset(uint_fast64_t newOffset); |
|||
|
|||
/*! |
|||
* Retrieves the then-offset of this ODD node. |
|||
* |
|||
* @return The then-offset of this ODD node. |
|||
*/ |
|||
uint_fast64_t getThenOffset() const; |
|||
|
|||
/*! |
|||
* Sets the then-offset of this ODD node. |
|||
* |
|||
* @param newOffset The new then-offset of this ODD node. |
|||
*/ |
|||
void setThenOffset(uint_fast64_t newOffset); |
|||
|
|||
/*! |
|||
* Retrieves the total offset, i.e., the sum of the then- and else-offset. |
|||
* |
|||
* @return The total offset of this ODD. |
|||
*/ |
|||
uint_fast64_t getTotalOffset() const; |
|||
|
|||
/*! |
|||
* Retrieves the size of the ODD. Note: the size is computed by a traversal, so this may be costlier than |
|||
* expected. |
|||
* |
|||
* @return The size (in nodes) of this ODD. |
|||
*/ |
|||
uint_fast64_t getNodeCount() const; |
|||
|
|||
/*! |
|||
* Retrieves the height of the ODD. |
|||
* |
|||
* @return The height of the ODD. |
|||
*/ |
|||
uint_fast64_t getHeight() const; |
|||
|
|||
/*! |
|||
* Checks whether the given ODD node is a terminal node, i.e. has no successors. |
|||
* |
|||
* @return True iff the node is terminal. |
|||
*/ |
|||
bool isTerminalNode() const; |
|||
|
|||
/*! |
|||
* Filters the given explicit vector using the symbolic representation of which values to select. |
|||
* |
|||
* @param selectedValues A symbolic representation of which values to select. |
|||
* @param values The value vector from which to select the values. |
|||
* @return The resulting vector. |
|||
*/ |
|||
std::vector<double> filterExplicitVector(storm::dd::Bdd<DdType::CUDD> const& selectedValues, std::vector<double> const& values) const; |
|||
|
|||
/*! |
|||
* Adds the old values to the new values. It does so by writing the old values at their correct positions |
|||
* wrt. to the new ODD. |
|||
* |
|||
* @param newOdd The new ODD to use. |
|||
* @param oldValues The old vector of values (which is being read). |
|||
* @param newValues The new vector of values (which is being written). |
|||
*/ |
|||
void expandExplicitVector(storm::dd::Odd<DdType::CUDD> const& newOdd, std::vector<double> const& oldValues, std::vector<double>& newValues) const; |
|||
|
|||
/*! |
|||
* Exports the ODD in the dot format to the given file. |
|||
* |
|||
* @param filename The name of the file to which to write the dot output. |
|||
*/ |
|||
void exportToDot(std::string const& filename) const; |
|||
|
|||
private: |
|||
// Declare a hash functor that is used for the unique tables in the construction process. |
|||
class HashFunctor { |
|||
public: |
|||
std::size_t operator()(std::pair<DdNode*, bool> const& key) const; |
|||
}; |
|||
|
|||
/*! |
|||
* Adds all nodes below the current one to the given mapping. |
|||
* |
|||
* @param levelToOddNodesMap A mapping of the level to the ODD node. |
|||
* @param The level of the current node. |
|||
*/ |
|||
void addToLevelToOddNodesMap(std::map<uint_fast64_t, std::vector<std::reference_wrapper<storm::dd::Odd<DdType::CUDD> const>>>& levelToOddNodesMap, uint_fast64_t level = 0) const; |
|||
|
|||
/*! |
|||
* Constructs an offset-labeled DD with the given topmost DD node, else- and then-successor. |
|||
* |
|||
* @param dd The DD node associated with this ODD node. |
|||
* @param elseNode The else-successor of thie ODD node. |
|||
* @param elseOffset The offset of the else-successor. |
|||
* @param thenNode The then-successor of thie ODD node. |
|||
* @param thenOffset The offset of the then-successor. |
|||
*/ |
|||
Odd(std::shared_ptr<Odd<DdType::CUDD>> elseNode, uint_fast64_t elseOffset, std::shared_ptr<Odd<DdType::CUDD>> thenNode, uint_fast64_t thenOffset); |
|||
|
|||
/*! |
|||
* Recursively builds the ODD from an ADD (that has no complement edges). |
|||
* |
|||
* @param dd The DD for which to build the ODD. |
|||
* @param manager The manager responsible for the DD. |
|||
* @param currentLevel The currently considered level in the DD. |
|||
* @param maxLevel The number of levels that need to be considered. |
|||
* @param ddVariableIndices The (sorted) indices of all DD variables that need to be considered. |
|||
* @param uniqueTableForLevels A vector of unique tables, one for each level to be considered, that keeps |
|||
* ODD nodes for the same DD and level unique. |
|||
* @return A pointer to the constructed ODD for the given arguments. |
|||
*/ |
|||
static std::shared_ptr<Odd<DdType::CUDD>> buildOddFromAddRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<std::unordered_map<DdNode*, std::shared_ptr<Odd<DdType::CUDD>>>>& uniqueTableForLevels); |
|||
|
|||
/*! |
|||
* Recursively builds the ODD from a BDD (that potentially has complement edges). |
|||
* |
|||
* @param dd The DD for which to build the ODD. |
|||
* @param manager The manager responsible for the DD. |
|||
* @param currentLevel The currently considered level in the DD. |
|||
* @param complement A flag indicating whether or not the given node is to be considered as complemented. |
|||
* @param maxLevel The number of levels that need to be considered. |
|||
* @param ddVariableIndices The (sorted) indices of all DD variables that need to be considered. |
|||
* @param uniqueTableForLevels A vector of unique tables, one for each level to be considered, that keeps |
|||
* ODD nodes for the same DD and level unique. |
|||
* @return A pointer to the constructed ODD for the given arguments. |
|||
*/ |
|||
static std::shared_ptr<Odd<DdType::CUDD>> buildOddFromBddRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, bool complement, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, std::vector<std::unordered_map<std::pair<DdNode*, bool>, std::shared_ptr<Odd<DdType::CUDD>>, HashFunctor>>& uniqueTableForLevels); |
|||
|
|||
/*! |
|||
* Adds the selected values the target vector. |
|||
* |
|||
* @param dd The current node of the DD representing the selected values. |
|||
* @param manager The manager responsible for the DD. |
|||
* @param currentLevel The currently considered level in the DD. |
|||
* @param maxLevel The number of levels that need to be considered. |
|||
* @param ddVariableIndices The sorted list of variable indices to use. |
|||
* @param currentOffset The offset along the path taken in the DD representing the selected values. |
|||
* @param odd The current ODD node. |
|||
* @param result The target vector to which to write the values. |
|||
* @param currentIndex The index at which the next element is to be written. |
|||
* @param values The value vector from which to select the values. |
|||
*/ |
|||
static void addSelectedValuesToVectorRec(DdNode* dd, Cudd const& manager, uint_fast64_t currentLevel, bool complement, uint_fast64_t maxLevel, std::vector<uint_fast64_t> const& ddVariableIndices, uint_fast64_t currentOffset, storm::dd::Odd<DdType::CUDD> const& odd, std::vector<double>& result, uint_fast64_t& currentIndex, std::vector<double> const& values); |
|||
|
|||
/*! |
|||
* Adds the values of the old explicit values to the new explicit values where the positions in the old vector |
|||
* are given by the current old ODD and the positions in the new vector are given by the new ODD. |
|||
* |
|||
* @param oldOffset The offset in the old explicit values. |
|||
* @param oldOdd The ODD to use for the old explicit values. |
|||
* @param oldValues The vector of old values. |
|||
* @param newOffset The offset in the new explicit values. |
|||
* @param newOdd The ODD to use for the new explicit values. |
|||
* @param newValues The vector of new values. |
|||
*/ |
|||
static void expandValuesToVectorRec(uint_fast64_t oldOffset, storm::dd::Odd<DdType::CUDD> const& oldOdd, std::vector<double> const& oldValues, uint_fast64_t newOffset, storm::dd::Odd<DdType::CUDD> const& newOdd, std::vector<double>& newValues); |
|||
|
|||
// The then- and else-nodes. |
|||
std::shared_ptr<Odd<DdType::CUDD>> elseNode; |
|||
std::shared_ptr<Odd<DdType::CUDD>> thenNode; |
|||
|
|||
// The offsets that need to be added if the then- or else-successor is taken, respectively. |
|||
uint_fast64_t elseOffset; |
|||
uint_fast64_t thenOffset; |
|||
}; |
|||
} |
|||
} |
|||
|
|||
#endif /* STORM_STORAGE_DD_CUDDODD_H_ */ |
Reference in new issue
xxxxxxxxxx