You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
27 KiB
472 lines
27 KiB
#include "storm/storage/dd/bisimulation/SignatureRefiner.h"
|
|
|
|
#include <unordered_map>
|
|
#include <boost/container/flat_map.hpp>
|
|
|
|
#include "storm/storage/dd/DdManager.h"
|
|
|
|
#include "storm/storage/dd/cudd/InternalCuddDdManager.h"
|
|
|
|
#include "storm/utility/macros.h"
|
|
#include "storm/exceptions/InvalidSettingsException.h"
|
|
#include "storm/exceptions/NotImplementedException.h"
|
|
|
|
#include "storm/settings/SettingsManager.h"
|
|
#include "storm/settings/modules/BisimulationSettings.h"
|
|
|
|
#include <sparsepp/spp.h>
|
|
|
|
#include "sylvan_cache.h"
|
|
#include "sylvan_table.h"
|
|
#include "sylvan_int.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);
|
|
spp::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);
|
|
spp::hash_combine(seed, pair.second);
|
|
return seed;
|
|
}
|
|
};
|
|
|
|
struct SylvanMTBDDPairLess {
|
|
std::size_t operator()(std::pair<MTBDD, MTBDD> const& a, std::pair<MTBDD, MTBDD> const& b) const {
|
|
if (a.first < b.first) {
|
|
return true;
|
|
} else if (a.first == b.first && a.second < b.second) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template<storm::dd::DdType DdType, typename ValueType>
|
|
class InternalSignatureRefiner;
|
|
|
|
class ReuseWrapper {
|
|
public:
|
|
ReuseWrapper() : ReuseWrapper(false) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
ReuseWrapper(bool value) : value(value) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
bool isReused() const {
|
|
return value;
|
|
}
|
|
|
|
void setReused() {
|
|
value = true;
|
|
}
|
|
|
|
private:
|
|
bool value;
|
|
};
|
|
|
|
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), lastNumberOfVisitedNodes(10000), signatureCache(lastNumberOfVisitedNodes), reuseBlocksCache(lastNumberOfVisitedNodes) {
|
|
// 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.replacePartition(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) {
|
|
STORM_LOG_ASSERT(oldPartition.storedAsAdd(), "Expecting partition to be stored as ADD for CUDD.");
|
|
|
|
// Clear the caches.
|
|
signatureCache.clear();
|
|
reuseBlocksCache.clear();
|
|
nextFreeBlockIndex = oldPartition.getNextFreeBlockIndex();
|
|
|
|
// Perform the actual recursive refinement step.
|
|
DdNodePtr result = refine(oldPartition.asAdd().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.asAdd().getDdManager(), internalNewPartitionAdd, oldPartition.asAdd().getContainedMetaVariables());
|
|
|
|
return newPartitionAdd;
|
|
}
|
|
|
|
DdNodePtr refine(DdNode* partitionNode, DdNode* signatureNode) {
|
|
::DdManager* ddman = internalDdManager.getCuddManager().getManager();
|
|
|
|
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
|
|
// all states to be non-deadlock).
|
|
if (partitionNode == Cudd_ReadZero(ddman)) {
|
|
return partitionNode;
|
|
}
|
|
|
|
// Check the cache whether we have seen the same node before.
|
|
std::unique_ptr<DdNode*>& sigCacheEntrySmartPtr = signatureCache[std::make_pair(signatureNode, partitionNode)];
|
|
if (sigCacheEntrySmartPtr) {
|
|
// If so, we return the corresponding result.
|
|
return *sigCacheEntrySmartPtr;
|
|
}
|
|
|
|
DdNode** newEntryPtr = new DdNode*;
|
|
sigCacheEntrySmartPtr.reset(newEntryPtr);
|
|
|
|
// Determine the levels in the DDs.
|
|
uint64_t partitionVariable = Cudd_NodeReadIndex(partitionNode) - 1;
|
|
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);
|
|
}
|
|
|
|
DdNode* result;
|
|
if (thenResult == elseResult) {
|
|
Cudd_Deref(thenResult);
|
|
Cudd_Deref(elseResult);
|
|
result = thenResult;
|
|
} else {
|
|
// Get the node to connect the subresults.
|
|
DdNode* var = Cudd_addIthVar(ddman, topVariable + 1);
|
|
Cudd_Ref(var);
|
|
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.
|
|
*newEntryPtr = result;
|
|
Cudd_Deref(result);
|
|
|
|
return result;
|
|
} else {
|
|
|
|
// If we are not within the state encoding any more, we hit the signature itself.
|
|
|
|
// 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& reuseEntry = reuseBlocksCache[partitionNode];
|
|
if (!reuseEntry.isReused()) {
|
|
reuseEntry.setReused();
|
|
*newEntryPtr = partitionNode;
|
|
return partitionNode;
|
|
} else {
|
|
DdNode* result;
|
|
{
|
|
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> blockEncoding = manager.getEncoding(blockVariable, nextFreeBlockIndex, false).template toAdd<ValueType>();
|
|
++nextFreeBlockIndex;
|
|
result = blockEncoding.getInternalAdd().getCuddDdNode();
|
|
Cudd_Ref(result);
|
|
}
|
|
*newEntryPtr = 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 number of nodes visited in the last refinement operation.
|
|
uint64_t lastNumberOfVisitedNodes;
|
|
|
|
// The cache used to identify states with identical signature.
|
|
spp::sparse_hash_map<std::pair<DdNode const*, DdNode const*>, std::unique_ptr<DdNode*>, CuddPointerPairHash> signatureCache;
|
|
|
|
// The cache used to identify which old block numbers have already been reused.
|
|
spp::sparse_hash_map<DdNode const*, ReuseWrapper> 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), numberOfBlockVariables(manager.getMetaVariable(blockVariable).getNumberOfDdVariables()), blockCube(manager.getMetaVariable(blockVariable).getCube()), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0), signatureCache() {
|
|
// Perform garbage collection to clean up stuff not needed anymore.
|
|
LACE_ME;
|
|
sylvan_gc();
|
|
}
|
|
|
|
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::Bdd<storm::dd::DdType::Sylvan> newPartitionBdd = refine(oldPartition, signature.getSignatureAdd());
|
|
return oldPartition.replacePartition(newPartitionBdd, nextFreeBlockIndex);
|
|
}
|
|
|
|
private:
|
|
storm::dd::Bdd<storm::dd::DdType::Sylvan> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> const& signatureAdd) {
|
|
STORM_LOG_ASSERT(oldPartition.storedAsBdd(), "Expecting partition to be stored as BDD for Sylvan.");
|
|
|
|
LACE_ME;
|
|
|
|
// Set up next refinement.
|
|
++numberOfRefinements;
|
|
|
|
// Clear the caches.
|
|
std::size_t oldSize = signatureCache.size();
|
|
signatureCache.clear();
|
|
signatureCache.reserve(3 * oldSize);
|
|
reuseBlocksCache.clear();
|
|
reuseBlocksCache.reserve(3 * oldPartition.getNumberOfBlocks());
|
|
nextFreeBlockIndex = oldPartition.getNextFreeBlockIndex();
|
|
|
|
// Clear performance counters.
|
|
// signatureCacheLookups = 0;
|
|
// signatureCacheHits = 0;
|
|
// numberOfVisitedNodes = 0;
|
|
// totalSignatureCacheLookupTime = std::chrono::high_resolution_clock::duration(0);
|
|
// totalSignatureCacheStoreTime = std::chrono::high_resolution_clock::duration(0);
|
|
// totalReuseBlocksLookupTime = std::chrono::high_resolution_clock::duration(0);
|
|
// totalLevelLookupTime = std::chrono::high_resolution_clock::duration(0);
|
|
// totalBlockEncodingTime = std::chrono::high_resolution_clock::duration(0);
|
|
// totalMakeNodeTime = std::chrono::high_resolution_clock::duration(0);
|
|
|
|
// Perform the actual recursive refinement step.
|
|
BDD result = refine(oldPartition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD());
|
|
|
|
// Construct resulting BDD from the obtained node and the meta information.
|
|
storm::dd::InternalBdd<storm::dd::DdType::Sylvan> internalNewPartitionBdd(&internalDdManager, sylvan::Bdd(result));
|
|
storm::dd::Bdd<storm::dd::DdType::Sylvan> newPartitionBdd(oldPartition.asBdd().getDdManager(), internalNewPartitionBdd, oldPartition.asBdd().getContainedMetaVariables());
|
|
|
|
// // Display some statistics.
|
|
// STORM_LOG_TRACE("Refinement visited " << numberOfVisitedNodes << " nodes.");
|
|
// STORM_LOG_TRACE("Current #nodes in table: " << llmsset_count_marked(nodes) << " of " << llmsset_get_size(nodes) << ", cache: " << cache_getused() << " of " << cache_getsize() << ".");
|
|
// STORM_LOG_TRACE("Signature cache hits: " << signatureCacheHits << ", misses: " << (signatureCacheLookups - signatureCacheHits) << ".");
|
|
// STORM_LOG_TRACE("Signature cache lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheLookupTime).count() << "ms");
|
|
// STORM_LOG_TRACE("Signature cache store time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheStoreTime).count() << "ms");
|
|
// STORM_LOG_TRACE("Signature cache total time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheStoreTime + totalSignatureCacheLookupTime).count() << "ms");
|
|
// STORM_LOG_TRACE("Reuse blocks lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalReuseBlocksLookupTime).count() << "ms");
|
|
// STORM_LOG_TRACE("Level lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalLevelLookupTime).count() << "ms");
|
|
// STORM_LOG_TRACE("Block encoding time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalBlockEncodingTime).count() << "ms");
|
|
// STORM_LOG_TRACE("Make node time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalMakeNodeTime).count() << "ms");
|
|
|
|
return newPartitionBdd;
|
|
}
|
|
|
|
BDD encodeBlock(uint64_t blockIndex) {
|
|
std::vector<uint8_t> e(numberOfBlockVariables);
|
|
for (uint64_t i = 0; i < numberOfBlockVariables; ++i) {
|
|
e[i] = blockIndex & 1 ? 1 : 0;
|
|
blockIndex >>= 1;
|
|
}
|
|
return sylvan_cube(blockCube.getInternalBdd().getSylvanBdd().GetBDD(), e.data());
|
|
}
|
|
|
|
BDD refine(BDD partitionNode, MTBDD signatureNode) {
|
|
LACE_ME;
|
|
|
|
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
|
|
// all states to be non-deadlock).
|
|
if (partitionNode == sylvan_false) {
|
|
return partitionNode;
|
|
}
|
|
|
|
STORM_LOG_ASSERT(partitionNode != mtbdd_false, "Expected non-false node.");
|
|
|
|
// ++numberOfVisitedNodes;
|
|
|
|
// Check the cache whether we have seen the same node before.
|
|
// ++signatureCacheLookups;
|
|
// auto start = std::chrono::high_resolution_clock::now();
|
|
std::unique_ptr<MTBDD>& sigCacheEntrySmartPtr = signatureCache[std::make_pair(signatureNode, partitionNode)];
|
|
if (sigCacheEntrySmartPtr) {
|
|
// ++signatureCacheHits;
|
|
// If so, we return the corresponding result.
|
|
// auto end = std::chrono::high_resolution_clock::now();
|
|
// totalSignatureCacheLookupTime += end - start;
|
|
return *sigCacheEntrySmartPtr;
|
|
}
|
|
// auto end = std::chrono::high_resolution_clock::now();
|
|
// totalSignatureCacheLookupTime += end - start;
|
|
|
|
MTBDD* newEntryPtr = new MTBDD;
|
|
sigCacheEntrySmartPtr.reset(newEntryPtr);
|
|
|
|
sylvan_gc_test();
|
|
|
|
// Determine levels in the DDs.
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
BDDVAR signatureVariable = sylvan_isconst(signatureNode) ? 0xffffffff : sylvan_var(signatureNode);
|
|
BDDVAR partitionVariable = sylvan_var(partitionNode) - 1;
|
|
BDDVAR topVariable = std::min(signatureVariable, partitionVariable);
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalLevelLookupTime += end - start;
|
|
|
|
// Check whether the top variable is still within the state encoding.
|
|
if (topVariable <= lastStateLevel) {
|
|
// Determine subresults by recursive descent.
|
|
BDD thenResult;
|
|
BDD elseResult;
|
|
if (partitionVariable < signatureVariable) {
|
|
elseResult = refine(sylvan_low(partitionNode), signatureNode);
|
|
thenResult = refine(sylvan_high(partitionNode), signatureNode);
|
|
} else if (partitionVariable > signatureVariable) {
|
|
elseResult = refine(partitionNode, sylvan_low(signatureNode));
|
|
thenResult = refine(partitionNode, sylvan_high(signatureNode));
|
|
} else {
|
|
elseResult = refine(sylvan_low(partitionNode), sylvan_low(signatureNode));
|
|
thenResult = refine(sylvan_high(partitionNode), sylvan_high(signatureNode));
|
|
}
|
|
|
|
BDD result;
|
|
if (thenResult == elseResult) {
|
|
result = thenResult;
|
|
} else {
|
|
// Get the node to connect the subresults.
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
result = sylvan_makenode(topVariable + 1, elseResult, thenResult);
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalMakeNodeTime += end - start;
|
|
}
|
|
|
|
// Store the result in the cache.
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
*newEntryPtr = result;
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalSignatureCacheStoreTime += end - start;
|
|
|
|
return result;
|
|
} else {
|
|
|
|
// If we are not within the state encoding any more, we hit the signature itself.
|
|
|
|
// 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.
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
auto& reuseBlockEntry = reuseBlocksCache[partitionNode];
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalReuseBlocksLookupTime += end - start;
|
|
if (!reuseBlockEntry.isReused()) {
|
|
reuseBlockEntry.setReused();
|
|
reuseBlocksCache.emplace(partitionNode, true);
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
*newEntryPtr = partitionNode;
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalSignatureCacheStoreTime += end - start;
|
|
return partitionNode;
|
|
} else {
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
BDD result = encodeBlock(nextFreeBlockIndex++);
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalBlockEncodingTime += end - start;
|
|
|
|
// start = std::chrono::high_resolution_clock::now();
|
|
*newEntryPtr = result;
|
|
// end = std::chrono::high_resolution_clock::now();
|
|
// totalSignatureCacheStoreTime += end - start;
|
|
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;
|
|
|
|
uint64_t numberOfBlockVariables;
|
|
|
|
storm::dd::Bdd<storm::dd::DdType::Sylvan> blockCube;
|
|
|
|
// 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>, std::unique_ptr<MTBDD>, SylvanMTBDDPairHash> signatureCache;
|
|
|
|
// The cache used to identify which old block numbers have already been reused.
|
|
spp::sparse_hash_map<MTBDD, ReuseWrapper> reuseBlocksCache;
|
|
|
|
// Performance counters.
|
|
// uint64_t signatureCacheLookups;
|
|
// uint64_t signatureCacheHits;
|
|
// uint64_t numberOfVisitedNodes;
|
|
// std::chrono::high_resolution_clock::duration totalSignatureCacheLookupTime;
|
|
// std::chrono::high_resolution_clock::duration totalSignatureCacheStoreTime;
|
|
// std::chrono::high_resolution_clock::duration totalReuseBlocksLookupTime;
|
|
// std::chrono::high_resolution_clock::duration totalLevelLookupTime;
|
|
// std::chrono::high_resolution_clock::duration totalBlockEncodingTime;
|
|
// std::chrono::high_resolution_clock::duration totalMakeNodeTime;
|
|
|
|
};
|
|
|
|
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>;
|
|
|
|
}
|
|
}
|
|
}
|