25 changed files with 769 additions and 49 deletions
-
1resources/3rdparty/CMakeLists.txt
-
2resources/3rdparty/sylvan/src/storm_wrapper.cpp
-
8resources/3rdparty/sylvan/src/sylvan_storm_rational_function.c
-
8resources/3rdparty/sylvan/src/sylvan_storm_rational_number.c
-
6resources/examples/testfiles/dtmc/die.pm
-
13src/storm/models/symbolic/Model.cpp
-
23src/storm/models/symbolic/Model.h
-
10src/storm/storage/dd/Add.cpp
-
17src/storm/storage/dd/Add.h
-
15src/storm/storage/dd/DdMetaVariable.cpp
-
5src/storm/storage/dd/DdMetaVariable.h
-
123src/storm/storage/dd/bisimulation/Partition.cpp
-
73src/storm/storage/dd/bisimulation/Partition.h
-
25src/storm/storage/dd/bisimulation/Signature.cpp
-
24src/storm/storage/dd/bisimulation/Signature.h
-
329src/storm/storage/dd/bisimulation/SignatureRefiner.cpp
-
37src/storm/storage/dd/bisimulation/SignatureRefiner.h
-
7src/storm/storage/dd/cudd/InternalCuddAdd.cpp
-
9src/storm/storage/dd/cudd/InternalCuddAdd.h
-
2src/storm/storage/dd/cudd/InternalCuddDdManager.h
-
5src/storm/storage/dd/sylvan/InternalSylvanAdd.cpp
-
16src/storm/storage/dd/sylvan/InternalSylvanAdd.h
-
7src/storm/storage/dd/sylvan/InternalSylvanDdManager.cpp
-
16src/storm/utility/storm.h
-
27src/test/storage/SymbolicBisimulationDecompositionTest.cpp
@ -0,0 +1,123 @@ |
|||||
|
#include "storm/storage/dd/bisimulation/Partition.h"
|
||||
|
|
||||
|
#include "storm/storage/dd/DdManager.h"
|
||||
|
|
||||
|
namespace storm { |
||||
|
namespace dd { |
||||
|
namespace bisimulation { |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Partition<DdType, ValueType>::Partition(storm::dd::Add<DdType, ValueType> const& partitionAdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex) : partitionAdd(partitionAdd), blockVariable(blockVariable), nextFreeBlockIndex(nextFreeBlockIndex) { |
||||
|
// Intentionally left empty.
|
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
bool Partition<DdType, ValueType>::operator==(Partition<DdType, ValueType> const& other) { |
||||
|
return this->partitionAdd == other.partitionAdd && this->blockVariable == other.blockVariable && this->nextFreeBlockIndex == other.nextFreeBlockIndex; |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Partition<DdType, ValueType> Partition<DdType, ValueType>::replacePartitionAdd(storm::dd::Add<DdType, ValueType> const& newPartitionAdd, uint64_t nextFreeBlockIndex) const { |
||||
|
return Partition<DdType, ValueType>(newPartitionAdd, blockVariable, nextFreeBlockIndex); |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model) { |
||||
|
return create(model, model.getLabels()); |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::string> const& labels) { |
||||
|
std::vector<storm::expressions::Expression> expressions; |
||||
|
for (auto const& label : labels) { |
||||
|
expressions.push_back(model.getExpression(label)); |
||||
|
} |
||||
|
return create(model, expressions); |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions) { |
||||
|
storm::dd::DdManager<DdType>& manager = model.getManager(); |
||||
|
|
||||
|
std::vector<storm::dd::Bdd<DdType>> stateSets; |
||||
|
for (auto const& expression : expressions) { |
||||
|
stateSets.emplace_back(model.getStates(expression)); |
||||
|
} |
||||
|
|
||||
|
storm::expressions::Variable blockVariable = createBlockVariable(manager, model.getReachableStates().getNonZeroCount()); |
||||
|
std::pair<storm::dd::Add<DdType, ValueType>, uint64_t> partitionAddAndBlockCount = createPartitionAdd(manager, model, stateSets, blockVariable); |
||||
|
return Partition<DdType, ValueType>(partitionAddAndBlockCount.first, blockVariable, partitionAddAndBlockCount.second); |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
uint64_t Partition<DdType, ValueType>::getNumberOfBlocks() const { |
||||
|
return nextFreeBlockIndex; |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
storm::dd::Add<DdType, ValueType> const& Partition<DdType, ValueType>::getPartitionAdd() const { |
||||
|
return partitionAdd; |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
storm::expressions::Variable const& Partition<DdType, ValueType>::getBlockVariable() const { |
||||
|
return blockVariable; |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
uint64_t Partition<DdType, ValueType>::getNextFreeBlockIndex() const { |
||||
|
return nextFreeBlockIndex; |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType> |
||||
|
void enumerateBlocksRec(std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::dd::Bdd<DdType> const& currentStateSet, uint64_t offset, storm::expressions::Variable const& blockVariable, std::function<void (storm::dd::Bdd<DdType> const&)> const& callback) { |
||||
|
if (currentStateSet.isZero()) { |
||||
|
return; |
||||
|
} |
||||
|
if (offset == stateSets.size()) { |
||||
|
callback(currentStateSet); |
||||
|
} else { |
||||
|
enumerateBlocksRec(stateSets, currentStateSet && stateSets[offset], offset + 1, blockVariable, callback); |
||||
|
enumerateBlocksRec(stateSets, currentStateSet && !stateSets[offset], offset + 1, blockVariable, callback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
std::pair<storm::dd::Add<DdType, ValueType>, uint64_t> Partition<DdType, ValueType>::createPartitionAdd(storm::dd::DdManager<DdType> const& manager, storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::expressions::Variable const& blockVariable) { |
||||
|
uint64_t blockCount = 0; |
||||
|
storm::dd::Add<DdType, ValueType> partitionAdd = manager.template getAddZero<ValueType>(); |
||||
|
|
||||
|
// Enumerate all realizable blocks.
|
||||
|
enumerateBlocksRec<DdType>(stateSets, model.getReachableStates(), 0, blockVariable, [&manager, &partitionAdd, &blockVariable, &blockCount](storm::dd::Bdd<DdType> const& stateSet) { |
||||
|
stateSet.template toAdd<ValueType>().exportToDot("states_" + std::to_string(blockCount) + ".dot"); |
||||
|
partitionAdd += (stateSet && manager.getEncoding(blockVariable, blockCount)).template toAdd<ValueType>(); |
||||
|
blockCount++; |
||||
|
} ); |
||||
|
|
||||
|
return std::make_pair(partitionAdd, blockCount); |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
storm::expressions::Variable Partition<DdType, ValueType>::createBlockVariable(storm::dd::DdManager<DdType>& manager, uint64_t numberOfStates) { |
||||
|
storm::expressions::Variable blockVariable; |
||||
|
if (manager.hasMetaVariable("blocks")) { |
||||
|
int64_t counter = 0; |
||||
|
while (manager.hasMetaVariable("block" + std::to_string(counter))) { |
||||
|
++counter; |
||||
|
} |
||||
|
blockVariable = manager.addMetaVariable("blocks" + std::to_string(counter), 0, numberOfStates, 1).front(); |
||||
|
} else { |
||||
|
blockVariable = manager.addMetaVariable("blocks", 0, numberOfStates, 1).front(); |
||||
|
} |
||||
|
return blockVariable; |
||||
|
} |
||||
|
|
||||
|
template class Partition<storm::dd::DdType::CUDD, double>; |
||||
|
|
||||
|
template class Partition<storm::dd::DdType::Sylvan, double>; |
||||
|
template class Partition<storm::dd::DdType::Sylvan, storm::RationalNumber>; |
||||
|
template class Partition<storm::dd::DdType::Sylvan, storm::RationalFunction>; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "storm/storage/dd/DdType.h" |
||||
|
#include "storm/storage/dd/Add.h" |
||||
|
|
||||
|
#include "storm/models/symbolic/Model.h" |
||||
|
|
||||
|
namespace storm { |
||||
|
namespace dd { |
||||
|
namespace bisimulation { |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
class Partition { |
||||
|
public: |
||||
|
Partition() = default; |
||||
|
|
||||
|
/*! |
||||
|
* Creates a new partition from the given data. |
||||
|
* |
||||
|
* @param partitionAdd An ADD that maps encoding over the state/row variables and the block variable to |
||||
|
* one iff the state is in the block. |
||||
|
* @param blockVariable The variable to use for the block encoding. Its range must be [0, x] where x is |
||||
|
* the number of states in the partition. |
||||
|
* @param nextFreeBlockIndex The next free block index. The existing blocks must be encoded with indices |
||||
|
* between 0 and this number. |
||||
|
*/ |
||||
|
Partition(storm::dd::Add<DdType, ValueType> const& partitionAdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex); |
||||
|
|
||||
|
bool operator==(Partition<DdType, ValueType> const& other); |
||||
|
|
||||
|
Partition<DdType, ValueType> replacePartitionAdd(storm::dd::Add<DdType, ValueType> const& newPartitionAdd, uint64_t nextFreeBlockIndex) const; |
||||
|
|
||||
|
/*! |
||||
|
* Creates a partition from the given model that respects all labels. |
||||
|
*/ |
||||
|
static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model); |
||||
|
|
||||
|
/*! |
||||
|
* Creates a partition from the given model that respects the given labels. |
||||
|
*/ |
||||
|
static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::string> const& labels); |
||||
|
|
||||
|
/*! |
||||
|
* Creates a partition from the given model that respects the given expressions. |
||||
|
*/ |
||||
|
static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions); |
||||
|
|
||||
|
uint64_t getNumberOfBlocks() const; |
||||
|
|
||||
|
storm::dd::Add<DdType, ValueType> const& getPartitionAdd() const; |
||||
|
|
||||
|
storm::expressions::Variable const& getBlockVariable() const; |
||||
|
|
||||
|
uint64_t getNextFreeBlockIndex() const; |
||||
|
|
||||
|
private: |
||||
|
static std::pair<storm::dd::Add<DdType, ValueType>, uint64_t> createPartitionAdd(storm::dd::DdManager<DdType> const& manager, storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::expressions::Variable const& blockVariable); |
||||
|
|
||||
|
static storm::expressions::Variable createBlockVariable(storm::dd::DdManager<DdType>& manager, uint64_t numberOfStates); |
||||
|
|
||||
|
// The ADD representing the partition. The ADD is over the row variables of the model and the block variable. |
||||
|
storm::dd::Add<DdType, ValueType> partitionAdd; |
||||
|
|
||||
|
// The meta variable used to encode the block of each state in this partition. |
||||
|
storm::expressions::Variable blockVariable; |
||||
|
|
||||
|
// The next free block index. |
||||
|
uint64_t nextFreeBlockIndex; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
#include "storm/storage/dd/bisimulation/Signature.h"
|
||||
|
|
||||
|
namespace storm { |
||||
|
namespace dd { |
||||
|
namespace bisimulation { |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Signature<DdType, ValueType>::Signature(storm::dd::Add<DdType, ValueType> const& signatureAdd) : signatureAdd(signatureAdd) { |
||||
|
// Intentionally left empty.
|
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
storm::dd::Add<DdType, ValueType> const& Signature<DdType, ValueType>::getSignatureAdd() const { |
||||
|
return signatureAdd; |
||||
|
} |
||||
|
|
||||
|
template class Signature<storm::dd::DdType::CUDD, double>; |
||||
|
|
||||
|
template class Signature<storm::dd::DdType::Sylvan, double>; |
||||
|
template class Signature<storm::dd::DdType::Sylvan, storm::RationalNumber>; |
||||
|
template class Signature<storm::dd::DdType::Sylvan, storm::RationalFunction>; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "storm/storage/dd/DdType.h" |
||||
|
|
||||
|
#include "storm/storage/dd/bisimulation/Partition.h" |
||||
|
|
||||
|
namespace storm { |
||||
|
namespace dd { |
||||
|
namespace bisimulation { |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
class Signature { |
||||
|
public: |
||||
|
Signature(storm::dd::Add<DdType, ValueType> const& signatureAdd); |
||||
|
|
||||
|
storm::dd::Add<DdType, ValueType> const& getSignatureAdd() const; |
||||
|
|
||||
|
private: |
||||
|
storm::dd::Add<DdType, ValueType> signatureAdd; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,329 @@ |
|||||
|
#include "storm/storage/dd/bisimulation/SignatureRefiner.h"
|
||||
|
|
||||
|
#include <unordered_map>
|
||||
|
|
||||
|
#include "storm/storage/dd/DdManager.h"
|
||||
|
|
||||
|
#include "storm/storage/dd/cudd/InternalCuddDdManager.h"
|
||||
|
|
||||
|
#include "storm/utility/macros.h"
|
||||
|
#include "storm/exceptions/NotImplementedException.h"
|
||||
|
|
||||
|
#include "resources/3rdparty/sparsepp/sparsepp.h"
|
||||
|
|
||||
|
namespace storm { |
||||
|
namespace dd { |
||||
|
namespace bisimulation { |
||||
|
|
||||
|
struct CuddPointerPairHash { |
||||
|
std::size_t operator()(std::pair<DdNode const*, DdNode const*> const& pair) const { |
||||
|
std::size_t seed = std::hash<DdNode const*>()(pair.first); |
||||
|
boost::hash_combine(seed, pair.second); |
||||
|
return seed; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
struct SylvanMTBDDPairHash { |
||||
|
std::size_t operator()(std::pair<MTBDD, MTBDD> const& pair) const { |
||||
|
std::size_t seed = std::hash<MTBDD>()(pair.first); |
||||
|
boost::hash_combine(seed, pair.second); |
||||
|
return seed; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
class InternalSignatureRefiner; |
||||
|
|
||||
|
template<typename ValueType> |
||||
|
class InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType> { |
||||
|
public: |
||||
|
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager, storm::expressions::Variable const& blockVariable, uint64_t lastStateLevel) : manager(manager), internalDdManager(manager.getInternalDdManager()), blockVariable(blockVariable), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0) { |
||||
|
// Intentionally left empty.
|
||||
|
} |
||||
|
|
||||
|
Partition<storm::dd::DdType::CUDD, ValueType> refine(Partition<storm::dd::DdType::CUDD, ValueType> const& oldPartition, Signature<storm::dd::DdType::CUDD, ValueType> const& signature) { |
||||
|
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd = refine(oldPartition, signature.getSignatureAdd()); |
||||
|
++numberOfRefinements; |
||||
|
return oldPartition.replacePartitionAdd(newPartitionAdd, nextFreeBlockIndex); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> refine(Partition<storm::dd::DdType::CUDD, ValueType> const& oldPartition, storm::dd::Add<storm::dd::DdType::CUDD, ValueType> const& signatureAdd) { |
||||
|
// Clear the caches.
|
||||
|
signatureCache.clear(); |
||||
|
reuseBlocksCache.clear(); |
||||
|
nextFreeBlockIndex = oldPartition.getNextFreeBlockIndex(); |
||||
|
|
||||
|
// Perform the actual recursive refinement step.
|
||||
|
DdNodePtr result = refine(oldPartition.getPartitionAdd().getInternalAdd().getCuddDdNode(), signatureAdd.getInternalAdd().getCuddDdNode()); |
||||
|
|
||||
|
// Construct resulting ADD from the obtained node and the meta information.
|
||||
|
storm::dd::InternalAdd<storm::dd::DdType::CUDD, ValueType> internalNewPartitionAdd(&internalDdManager, cudd::ADD(internalDdManager.getCuddManager(), result)); |
||||
|
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd(oldPartition.getPartitionAdd().getDdManager(), internalNewPartitionAdd, oldPartition.getPartitionAdd().getContainedMetaVariables()); |
||||
|
|
||||
|
return newPartitionAdd; |
||||
|
} |
||||
|
|
||||
|
DdNodePtr refine(DdNode* partitionNode, DdNode* signatureNode) { |
||||
|
::DdManager* ddman = internalDdManager.getCuddManager().getManager(); |
||||
|
|
||||
|
// Check the cache whether we have seen the same node before.
|
||||
|
auto sigCacheIt = signatureCache.find(std::make_pair(signatureNode, partitionNode)); |
||||
|
if (sigCacheIt != signatureCache.end()) { |
||||
|
// If so, we return the corresponding result.
|
||||
|
return sigCacheIt->second; |
||||
|
} |
||||
|
|
||||
|
// Determine the levels in the DDs.
|
||||
|
uint64_t partitionVariable = Cudd_NodeReadIndex(partitionNode); |
||||
|
uint64_t signatureVariable = Cudd_NodeReadIndex(signatureNode); |
||||
|
uint64_t partitionLevel = Cudd_ReadPerm(ddman, partitionVariable); |
||||
|
uint64_t signatureLevel = Cudd_ReadPerm(ddman, signatureVariable); |
||||
|
uint64_t topLevel = std::min(partitionLevel, signatureLevel); |
||||
|
uint64_t topVariable = topLevel == partitionLevel ? partitionVariable : signatureVariable; |
||||
|
|
||||
|
// Check whether the top variable is still within the state encoding.
|
||||
|
if (topLevel <= lastStateLevel) { |
||||
|
// Determine subresults by recursive descent.
|
||||
|
DdNodePtr thenResult; |
||||
|
DdNodePtr elseResult; |
||||
|
if (partitionLevel < signatureLevel) { |
||||
|
thenResult = refine(Cudd_T(partitionNode), signatureNode); |
||||
|
Cudd_Ref(thenResult); |
||||
|
elseResult = refine(Cudd_E(partitionNode), signatureNode); |
||||
|
Cudd_Ref(elseResult); |
||||
|
} else if (partitionLevel > signatureLevel) { |
||||
|
thenResult = refine(partitionNode, Cudd_T(signatureNode)); |
||||
|
Cudd_Ref(thenResult); |
||||
|
elseResult = refine(partitionNode, Cudd_E(signatureNode)); |
||||
|
Cudd_Ref(elseResult); |
||||
|
} else { |
||||
|
thenResult = refine(Cudd_T(partitionNode), Cudd_T(signatureNode)); |
||||
|
Cudd_Ref(thenResult); |
||||
|
elseResult = refine(Cudd_E(partitionNode), Cudd_E(signatureNode)); |
||||
|
Cudd_Ref(elseResult); |
||||
|
} |
||||
|
|
||||
|
if (thenResult == elseResult) { |
||||
|
Cudd_Deref(thenResult); |
||||
|
Cudd_Deref(elseResult); |
||||
|
return thenResult; |
||||
|
} |
||||
|
|
||||
|
// Get the node to connect the subresults.
|
||||
|
DdNode* var = Cudd_addIthVar(ddman, topVariable); |
||||
|
Cudd_Ref(var); |
||||
|
DdNode* result = Cudd_addIte(ddman, var, thenResult, elseResult); |
||||
|
Cudd_Ref(result); |
||||
|
Cudd_RecursiveDeref(ddman, var); |
||||
|
Cudd_Deref(thenResult); |
||||
|
Cudd_Deref(elseResult); |
||||
|
|
||||
|
// Store the result in the cache.
|
||||
|
signatureCache[std::make_pair(signatureNode, partitionNode)] = result; |
||||
|
|
||||
|
Cudd_Deref(result); |
||||
|
return result; |
||||
|
} else { |
||||
|
|
||||
|
// If we are not within the state encoding any more, we hit the signature itself.
|
||||
|
|
||||
|
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
|
||||
|
// all states to be non-deadlock).
|
||||
|
if (signatureNode == Cudd_ReadZero(ddman)) { |
||||
|
return signatureNode; |
||||
|
} |
||||
|
|
||||
|
// If this is the first time (in this traversal) that we encounter this signature, we check
|
||||
|
// whether we can assign the old block number to it.
|
||||
|
auto reuseCacheIt = reuseBlocksCache.find(partitionNode); |
||||
|
if (reuseCacheIt == reuseBlocksCache.end()) { |
||||
|
reuseBlocksCache.emplace(partitionNode, true); |
||||
|
signatureCache[std::make_pair(signatureNode, partitionNode)] = partitionNode; |
||||
|
return partitionNode; |
||||
|
} else { |
||||
|
DdNode* result; |
||||
|
{ |
||||
|
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> blockEncoding = manager.getEncoding(blockVariable, nextFreeBlockIndex).template toAdd<ValueType>(); |
||||
|
++nextFreeBlockIndex; |
||||
|
result = blockEncoding.getInternalAdd().getCuddDdNode(); |
||||
|
Cudd_Ref(result); |
||||
|
} |
||||
|
signatureCache[std::make_pair(signatureNode, partitionNode)] = result; |
||||
|
Cudd_Deref(result); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager; |
||||
|
storm::dd::InternalDdManager<storm::dd::DdType::CUDD> const& internalDdManager; |
||||
|
storm::expressions::Variable const& blockVariable; |
||||
|
|
||||
|
// The last level that belongs to the state encoding in the DDs.
|
||||
|
uint64_t lastStateLevel; |
||||
|
|
||||
|
// The current number of blocks of the new partition.
|
||||
|
uint64_t nextFreeBlockIndex; |
||||
|
|
||||
|
// The number of completed refinements.
|
||||
|
uint64_t numberOfRefinements; |
||||
|
|
||||
|
// The cache used to identify states with identical signature.
|
||||
|
spp::sparse_hash_map<std::pair<DdNode const*, DdNode const*>, DdNode*, CuddPointerPairHash> signatureCache; |
||||
|
|
||||
|
// The cache used to identify which old block numbers have already been reused.
|
||||
|
spp::sparse_hash_map<DdNode const*, bool> reuseBlocksCache; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
template<typename ValueType> |
||||
|
class InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType> { |
||||
|
public: |
||||
|
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, uint64_t lastStateLevel) : manager(manager), internalDdManager(manager.getInternalDdManager()), blockVariable(blockVariable), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0) { |
||||
|
// Intentionally left empty.
|
||||
|
} |
||||
|
|
||||
|
Partition<storm::dd::DdType::Sylvan, ValueType> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, Signature<storm::dd::DdType::Sylvan, ValueType> const& signature) { |
||||
|
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> newPartitionAdd = refine(oldPartition, signature.getSignatureAdd()); |
||||
|
++numberOfRefinements; |
||||
|
return oldPartition.replacePartitionAdd(newPartitionAdd, nextFreeBlockIndex); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> const& signatureAdd) { |
||||
|
// Clear the caches.
|
||||
|
signatureCache.clear(); |
||||
|
reuseBlocksCache.clear(); |
||||
|
nextFreeBlockIndex = oldPartition.getNextFreeBlockIndex(); |
||||
|
|
||||
|
// Perform the actual recursive refinement step.
|
||||
|
MTBDD result = refine(oldPartition.getPartitionAdd().getInternalAdd().getSylvanMtbdd().GetMTBDD(), signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD()); |
||||
|
|
||||
|
// Construct resulting ADD from the obtained node and the meta information.
|
||||
|
storm::dd::InternalAdd<storm::dd::DdType::Sylvan, ValueType> internalNewPartitionAdd(&internalDdManager, sylvan::Mtbdd(result)); |
||||
|
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> newPartitionAdd(oldPartition.getPartitionAdd().getDdManager(), internalNewPartitionAdd, oldPartition.getPartitionAdd().getContainedMetaVariables()); |
||||
|
|
||||
|
return newPartitionAdd; |
||||
|
} |
||||
|
|
||||
|
MTBDD refine(MTBDD partitionNode, MTBDD signatureNode) { |
||||
|
LACE_ME; |
||||
|
|
||||
|
// Check the cache whether we have seen the same node before.
|
||||
|
auto sigCacheIt = signatureCache.find(std::make_pair(signatureNode, partitionNode)); |
||||
|
if (sigCacheIt != signatureCache.end()) { |
||||
|
// If so, we return the corresponding result.
|
||||
|
return sigCacheIt->second; |
||||
|
} |
||||
|
|
||||
|
// Determine levels in the DDs.
|
||||
|
BDDVAR signatureVariable = mtbdd_isleaf(signatureNode) ? 0xffffffff : sylvan_var(signatureNode); |
||||
|
BDDVAR partitionVariable = sylvan_var(partitionNode); |
||||
|
BDDVAR topVariable = std::min(signatureVariable, partitionVariable); |
||||
|
|
||||
|
// Check whether the top variable is still within the state encoding.
|
||||
|
if (topVariable <= lastStateLevel) { |
||||
|
// Determine subresults by recursive descent.
|
||||
|
MTBDD thenResult; |
||||
|
MTBDD elseResult; |
||||
|
if (partitionVariable < signatureVariable) { |
||||
|
thenResult = refine(sylvan_high(partitionNode), signatureNode); |
||||
|
elseResult = refine(sylvan_low(partitionNode), signatureNode); |
||||
|
} else if (partitionVariable > signatureVariable) { |
||||
|
thenResult = refine(partitionNode, sylvan_high(signatureNode)); |
||||
|
elseResult = refine(partitionNode, sylvan_low(signatureNode)); |
||||
|
} else { |
||||
|
thenResult = refine(sylvan_high(partitionNode), sylvan_high(signatureNode)); |
||||
|
elseResult = refine(sylvan_low(partitionNode), sylvan_low(signatureNode)); |
||||
|
} |
||||
|
|
||||
|
if (thenResult == elseResult) { |
||||
|
return thenResult; |
||||
|
} |
||||
|
|
||||
|
// Get the node to connect the subresults.
|
||||
|
MTBDD result = mtbdd_ite(sylvan_ithvar(topVariable), thenResult, elseResult); |
||||
|
|
||||
|
// Store the result in the cache.
|
||||
|
signatureCache[std::make_pair(signatureNode, partitionNode)] = result; |
||||
|
|
||||
|
return result; |
||||
|
} else { |
||||
|
|
||||
|
// If we are not within the state encoding any more, we hit the signature itself.
|
||||
|
|
||||
|
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
|
||||
|
// all states to be non-deadlock).
|
||||
|
if (mtbdd_iszero(signatureNode)) { |
||||
|
return signatureNode; |
||||
|
} |
||||
|
|
||||
|
// If this is the first time (in this traversal) that we encounter this signature, we check
|
||||
|
// whether we can assign the old block number to it.
|
||||
|
auto reuseCacheIt = reuseBlocksCache.find(partitionNode); |
||||
|
if (reuseCacheIt == reuseBlocksCache.end()) { |
||||
|
reuseBlocksCache.emplace(partitionNode, true); |
||||
|
signatureCache[std::make_pair(signatureNode, partitionNode)] = partitionNode; |
||||
|
return partitionNode; |
||||
|
} else { |
||||
|
MTBDD result; |
||||
|
{ |
||||
|
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> blockEncoding = manager.getEncoding(blockVariable, nextFreeBlockIndex).template toAdd<ValueType>(); |
||||
|
++nextFreeBlockIndex; |
||||
|
result = blockEncoding.getInternalAdd().getSylvanMtbdd().GetMTBDD(); |
||||
|
} |
||||
|
signatureCache[std::make_pair(signatureNode, partitionNode)] = result; |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager; |
||||
|
storm::dd::InternalDdManager<storm::dd::DdType::Sylvan> const& internalDdManager; |
||||
|
storm::expressions::Variable const& blockVariable; |
||||
|
|
||||
|
// The last level that belongs to the state encoding in the DDs.
|
||||
|
uint64_t lastStateLevel; |
||||
|
|
||||
|
// The current number of blocks of the new partition.
|
||||
|
uint64_t nextFreeBlockIndex; |
||||
|
|
||||
|
// The number of completed refinements.
|
||||
|
uint64_t numberOfRefinements; |
||||
|
|
||||
|
// The cache used to identify states with identical signature.
|
||||
|
spp::sparse_hash_map<std::pair<MTBDD, MTBDD>, MTBDD, SylvanMTBDDPairHash> signatureCache; |
||||
|
|
||||
|
// The cache used to identify which old block numbers have already been reused.
|
||||
|
spp::sparse_hash_map<MTBDD, bool> reuseBlocksCache; |
||||
|
}; |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
SignatureRefiner<DdType, ValueType>::SignatureRefiner(storm::dd::DdManager<DdType> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables) : manager(manager), stateVariables(stateVariables) { |
||||
|
uint64_t lastStateLevel = 0; |
||||
|
for (auto const& stateVariable : stateVariables) { |
||||
|
lastStateLevel = std::max(lastStateLevel, manager.getMetaVariable(stateVariable).getHighestLevel()); |
||||
|
} |
||||
|
|
||||
|
internalRefiner = std::make_unique<InternalSignatureRefiner<DdType, ValueType>>(manager, blockVariable, lastStateLevel); |
||||
|
} |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
SignatureRefiner<DdType, ValueType>::~SignatureRefiner() = default; |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
Partition<DdType, ValueType> SignatureRefiner<DdType, ValueType>::refine(Partition<DdType, ValueType> const& oldPartition, Signature<DdType, ValueType> const& signature) { |
||||
|
return internalRefiner->refine(oldPartition, signature); |
||||
|
} |
||||
|
|
||||
|
template class SignatureRefiner<storm::dd::DdType::CUDD, double>; |
||||
|
|
||||
|
template class SignatureRefiner<storm::dd::DdType::Sylvan, double>; |
||||
|
template class SignatureRefiner<storm::dd::DdType::Sylvan, storm::RationalNumber>; |
||||
|
template class SignatureRefiner<storm::dd::DdType::Sylvan, storm::RationalFunction>; |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "storm/storage/dd/DdType.h" |
||||
|
|
||||
|
#include "storm/storage/dd/bisimulation/Partition.h" |
||||
|
#include "storm/storage/dd/bisimulation/Signature.h" |
||||
|
|
||||
|
namespace storm { |
||||
|
namespace dd { |
||||
|
namespace bisimulation { |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
class InternalSignatureRefiner; |
||||
|
|
||||
|
template<storm::dd::DdType DdType, typename ValueType> |
||||
|
class SignatureRefiner { |
||||
|
public: |
||||
|
SignatureRefiner(storm::dd::DdManager<DdType> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables); |
||||
|
|
||||
|
~SignatureRefiner(); |
||||
|
|
||||
|
Partition<DdType, ValueType> refine(Partition<DdType, ValueType> const& oldPartition, Signature<DdType, ValueType> const& signature); |
||||
|
|
||||
|
private: |
||||
|
// The manager responsible for the DDs. |
||||
|
storm::dd::DdManager<DdType> const& manager; |
||||
|
|
||||
|
// The variables encodin the states. |
||||
|
std::set<storm::expressions::Variable> const& stateVariables; |
||||
|
|
||||
|
// The internal refiner. |
||||
|
std::unique_ptr<InternalSignatureRefiner<DdType, ValueType>> internalRefiner; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
#include "gtest/gtest.h"
|
||||
|
#include "storm-config.h"
|
||||
|
#include "storm/parser/PrismParser.h"
|
||||
|
#include "storm/storage/SymbolicModelDescription.h"
|
||||
|
#include "storm/builder/DdPrismModelBuilder.h"
|
||||
|
#include "storm/models/symbolic/Dtmc.h"
|
||||
|
#include "storm/storage/dd/BisimulationDecomposition.h"
|
||||
|
|
||||
|
TEST(SymbolicBisimulationDecompositionTest_Cudd, Die) { |
||||
|
storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/die.pm"); |
||||
|
storm::prism::Program program = modelDescription.preprocess().asPrismProgram(); |
||||
|
|
||||
|
std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD, double>> model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD, double>().build(program); |
||||
|
|
||||
|
storm::dd::BisimulationDecomposition<storm::dd::DdType::CUDD, double> decomposition(*model, storm::dd::bisimulation::Partition<storm::dd::DdType::CUDD, double>::create(*model, {"one"})); |
||||
|
decomposition.compute(); |
||||
|
} |
||||
|
|
||||
|
TEST(SymbolicBisimulationDecompositionTest_Cudd, Crowds) { |
||||
|
storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/crowds-5-5.pm"); |
||||
|
storm::prism::Program program = modelDescription.preprocess().asPrismProgram(); |
||||
|
|
||||
|
std::shared_ptr<storm::models::symbolic::Model<storm::dd::DdType::CUDD, double>> model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD, double>().build(program); |
||||
|
|
||||
|
storm::dd::BisimulationDecomposition<storm::dd::DdType::CUDD, double> decomposition(*model, storm::dd::bisimulation::Partition<storm::dd::DdType::CUDD, double>::create(*model, {"observe0Greater1"})); |
||||
|
decomposition.compute(); |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue