Browse Source

started integrating parallelism implementation of sigrefmc by Tom van Dijk

main
dehnert 8 years ago
parent
commit
99f45fea3c
  1. 1
      src/storm/modelchecker/abstraction/AbstractAbstractionRefinementModelChecker.cpp
  2. 1
      src/storm/modelchecker/abstraction/BisimulationAbstractionRefinementModelChecker.cpp
  3. 1
      src/storm/modelchecker/abstraction/PartialBisimulationMdpModelChecker.cpp
  4. 367
      src/storm/storage/dd/bisimulation/InternalCuddSignatureRefiner.cpp
  5. 84
      src/storm/storage/dd/bisimulation/InternalCuddSignatureRefiner.h
  6. 45
      src/storm/storage/dd/bisimulation/InternalSignatureRefiner.cpp
  7. 36
      src/storm/storage/dd/bisimulation/InternalSignatureRefiner.h
  8. 632
      src/storm/storage/dd/bisimulation/InternalSylvanSignatureRefiner.cpp
  9. 85
      src/storm/storage/dd/bisimulation/InternalSylvanSignatureRefiner.h
  10. 6
      src/storm/storage/dd/bisimulation/Partition.cpp
  11. 1013
      src/storm/storage/dd/bisimulation/SignatureRefiner.cpp

1
src/storm/modelchecker/abstraction/AbstractAbstractionRefinementModelChecker.cpp

@ -10,6 +10,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/StochasticTwoPlayerGame.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/modelchecker/CheckTask.h"
#include "storm/modelchecker/results/CheckResult.h"

1
src/storm/modelchecker/abstraction/BisimulationAbstractionRefinementModelChecker.cpp

@ -3,6 +3,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/StochasticTwoPlayerGame.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/abstraction/SymbolicStateSet.h"

1
src/storm/modelchecker/abstraction/PartialBisimulationMdpModelChecker.cpp

@ -3,6 +3,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/StochasticTwoPlayerGame.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/modelchecker/results/CheckResult.h"
#include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h"

367
src/storm/storage/dd/bisimulation/InternalCuddSignatureRefiner.cpp

@ -0,0 +1,367 @@
#include "storm/storage/dd/bisimulation/InternalCuddSignatureRefiner.h"
#include "storm/storage/dd/DdManager.h"
#include "storm/storage/dd/bisimulation/Partition.h"
#include "storm/storage/dd/bisimulation/Signature.h"
namespace storm {
namespace dd {
namespace bisimulation {
template<typename ValueType>
InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::CUDD> const& nondeterminismVariables, storm::dd::Bdd<storm::dd::DdType::CUDD> const& nonBlockVariables, InternalSignatureRefinerOptions const& options) : manager(manager), ddman(manager.getInternalDdManager().getCuddManager().getManager()), blockVariable(blockVariable), stateVariables(stateVariables), nondeterminismVariables(nondeterminismVariables), nonBlockVariables(nonBlockVariables), options(options), nextFreeBlockIndex(0), numberOfRefinements(0), signatureCache(), reuseBlocksCache() {
// Initialize precomputed data.
auto const& ddMetaVariable = manager.getMetaVariable(blockVariable);
blockDdVariableIndices = ddMetaVariable.getIndices();
// Create initialized block encoding where all variables are "don't care".
blockEncoding = std::vector<int>(static_cast<uint64_t>(manager.getInternalDdManager().getCuddManager().ReadSize()), static_cast<int>(2));
}
template<typename ValueType>
Partition<storm::dd::DdType::CUDD, ValueType> InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::refine(Partition<storm::dd::DdType::CUDD, ValueType> const& oldPartition, Signature<storm::dd::DdType::CUDD, ValueType> const& signature) {
std::pair<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>, boost::optional<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>>> newPartitionDds = refine(oldPartition, signature.getSignatureAdd());;
++numberOfRefinements;
return oldPartition.replacePartition(newPartitionDds.first, nextFreeBlockIndex, nextFreeBlockIndex, newPartitionDds.second);
}
template<typename ValueType>
void InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::clearCaches() {
signatureCache.clear();
reuseBlocksCache.clear();
}
template<typename ValueType>
std::pair<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>, boost::optional<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>>> InternalSignatureRefiner<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.");
nextFreeBlockIndex = options.reuseBlockNumbers ? oldPartition.getNextFreeBlockIndex() : 0;
// Perform the actual recursive refinement step.
std::pair<DdNodePtr, DdNodePtr> result = refine(oldPartition.asAdd().getInternalAdd().getCuddDdNode(), signatureAdd.getInternalAdd().getCuddDdNode(), nondeterminismVariables.getInternalBdd().getCuddDdNode(), nonBlockVariables.getInternalBdd().getCuddDdNode());
// Construct resulting ADD from the obtained node and the meta information.
storm::dd::InternalAdd<storm::dd::DdType::CUDD, ValueType> internalNewPartitionAdd(&manager.getInternalDdManager(), cudd::ADD(manager.getInternalDdManager().getCuddManager(), result.first));
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd(oldPartition.asAdd().getDdManager(), internalNewPartitionAdd, oldPartition.asAdd().getContainedMetaVariables());
boost::optional<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>> optionalChangedAdd;
if (result.second) {
storm::dd::InternalAdd<storm::dd::DdType::CUDD, ValueType> internalChangedAdd(&manager.getInternalDdManager(), cudd::ADD(manager.getInternalDdManager().getCuddManager(), result.second));
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> changedAdd(oldPartition.asAdd().getDdManager(), internalChangedAdd, stateVariables);
optionalChangedAdd = changedAdd;
}
clearCaches();
return std::make_pair(newPartitionAdd, optionalChangedAdd);
}
template<typename ValueType>
DdNodePtr InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::encodeBlock(uint64_t blockIndex) {
for (auto const& blockDdVariableIndex : blockDdVariableIndices) {
blockEncoding[blockDdVariableIndex] = blockIndex & 1 ? 1 : 0;
blockIndex >>= 1;
}
DdNodePtr bddEncoding = Cudd_CubeArrayToBdd(ddman, blockEncoding.data());
Cudd_Ref(bddEncoding);
DdNodePtr result = Cudd_BddToAdd(ddman, bddEncoding);
Cudd_Ref(result);
Cudd_RecursiveDeref(ddman, bddEncoding);
Cudd_Deref(result);
return result;
}
template<typename ValueType>
std::pair<DdNodePtr, DdNodePtr> InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::reuseOrRelabel(DdNode* partitionNode, DdNode* nondeterminismVariablesNode, DdNode* nonBlockVariablesNode) {
// If we arrived at the constant zero node, then this was an illegal state encoding.
if (partitionNode == Cudd_ReadZero(ddman)) {
if (options.createChangedStates) {
return std::make_pair(partitionNode, Cudd_ReadZero(ddman));
} else {
return std::make_pair(partitionNode, nullptr);
}
}
// Check the cache whether we have seen the same node before.
auto nodePair = std::make_pair(nullptr, partitionNode);
auto it = signatureCache.find(nodePair);
if (it != signatureCache.end()) {
// If so, we return the corresponding result.
return it->second;
}
// If there are no more non-block variables, we hit the signature.
if (Cudd_IsConstant(nonBlockVariablesNode)) {
if (options.reuseBlockNumbers) {
// 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();
std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(partitionNode, options.createChangedStates ? Cudd_ReadZero(ddman) : nullptr);
signatureCache[nodePair] = result;
return result;
}
}
std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(encodeBlock(nextFreeBlockIndex++), options.createChangedStates ? Cudd_ReadOne(ddman) : nullptr);
signatureCache[nodePair] = result;
return result;
} else {
// If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
bool skipped = true;
DdNode* partitionThen;
DdNode* partitionElse;
short offset;
bool isNondeterminismVariable = false;
while (skipped && !Cudd_IsConstant(nonBlockVariablesNode)) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = options.shiftStateVariables ? 1 : 0;
if (!Cudd_IsConstant(nondeterminismVariablesNode) && Cudd_NodeReadIndex(nondeterminismVariablesNode) == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
offset = 0;
isNondeterminismVariable = true;
}
if (Cudd_NodeReadIndex(partitionNode) - offset == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
partitionThen = Cudd_T(partitionNode);
partitionElse = Cudd_E(partitionNode);
skipped = false;
} else {
partitionThen = partitionElse = partitionNode;
}
// If we skipped the next variable, we fast-forward.
if (skipped) {
// If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
nonBlockVariablesNode = Cudd_T(nonBlockVariablesNode);
if (isNondeterminismVariable) {
nondeterminismVariablesNode = Cudd_T(nondeterminismVariablesNode);
}
}
}
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if (Cudd_IsConstant(nonBlockVariablesNode)) {
return reuseOrRelabel(partitionNode, nondeterminismVariablesNode, nonBlockVariablesNode);
}
std::pair<DdNodePtr, DdNodePtr> combinedThenResult = reuseOrRelabel(partitionThen, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode, Cudd_T(nonBlockVariablesNode));
DdNodePtr thenResult = combinedThenResult.first;
DdNodePtr changedThenResult = combinedThenResult.second;
Cudd_Ref(thenResult);
if (options.createChangedStates) {
Cudd_Ref(changedThenResult);
} else {
STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
}
std::pair<DdNodePtr, DdNodePtr> combinedElseResult = reuseOrRelabel(partitionElse, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode, Cudd_T(nonBlockVariablesNode));
DdNodePtr elseResult = combinedElseResult.first;
DdNodePtr changedElseResult = combinedElseResult.second;
Cudd_Ref(elseResult);
if (options.createChangedStates) {
Cudd_Ref(changedElseResult);
} else {
STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
}
DdNodePtr result;
if (thenResult == elseResult) {
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
result = thenResult;
} else {
// Get the node to connect the subresults.
bool complemented = Cudd_IsComplement(thenResult);
result = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(thenResult), complemented ? Cudd_Not(elseResult) : elseResult);
if (complemented) {
result = Cudd_Not(result);
}
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
}
DdNodePtr changedResult = nullptr;
if (options.createChangedStates) {
if (changedThenResult == changedElseResult) {
Cudd_Deref(changedThenResult);
Cudd_Deref(changedElseResult);
changedResult = changedThenResult;
} else {
// Get the node to connect the subresults.
bool complemented = Cudd_IsComplement(changedThenResult);
changedResult = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(changedThenResult), complemented ? Cudd_Not(changedElseResult) : changedElseResult);
if (complemented) {
changedResult = Cudd_Not(changedResult);
}
Cudd_Deref(changedThenResult);
Cudd_Deref(changedElseResult);
}
}
// Store the result in the cache.
auto pairResult = std::make_pair(result, changedResult);
signatureCache[nodePair] = pairResult;
return pairResult;
}
}
template<typename ValueType>
std::pair<DdNodePtr, DdNodePtr> InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType>::refine(DdNode* partitionNode, DdNode* signatureNode, DdNode* nondeterminismVariablesNode, DdNode* nonBlockVariablesNode) {
// If we arrived at the constant zero node, then this was an illegal state encoding.
if (partitionNode == Cudd_ReadZero(ddman)) {
if (options.createChangedStates) {
return std::make_pair(partitionNode, Cudd_ReadZero(ddman));
} else {
return std::make_pair(partitionNode, nullptr);
}
} else if (signatureNode == Cudd_ReadZero(ddman)) {
std::pair<DdNodePtr, DdNodePtr> result = reuseOrRelabel(partitionNode, nondeterminismVariablesNode, nonBlockVariablesNode);
signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
return result;
}
// Check the cache whether we have seen the same node before.
auto nodePair = std::make_pair(signatureNode, partitionNode);
auto it = signatureCache.find(nodePair);
if (it != signatureCache.end()) {
// If so, we return the corresponding result.
return it->second;
}
// If there are no more non-block variables, we hit the signature.
if (Cudd_IsConstant(nonBlockVariablesNode)) {
if (options.reuseBlockNumbers) {
// 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();
std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(partitionNode, options.createChangedStates ? Cudd_ReadZero(ddman) : nullptr);
signatureCache[nodePair] = result;
return result;
}
}
std::pair<DdNodePtr, DdNodePtr> result = std::make_pair(encodeBlock(nextFreeBlockIndex++), options.createChangedStates ? Cudd_ReadOne(ddman) : nullptr);
signatureCache[nodePair] = result;
return result;
} else {
// If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
bool skippedBoth = true;
DdNode* partitionThen;
DdNode* partitionElse;
DdNode* signatureThen;
DdNode* signatureElse;
short offset;
bool isNondeterminismVariable = false;
while (skippedBoth && !Cudd_IsConstant(nonBlockVariablesNode)) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = options.shiftStateVariables ? 1 : 0;
if (!Cudd_IsConstant(nondeterminismVariablesNode) && Cudd_NodeReadIndex(nondeterminismVariablesNode) == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
offset = 0;
isNondeterminismVariable = true;
}
if (Cudd_NodeReadIndex(partitionNode) - offset == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
partitionThen = Cudd_T(partitionNode);
partitionElse = Cudd_E(partitionNode);
skippedBoth = false;
} else {
partitionThen = partitionElse = partitionNode;
}
if (Cudd_NodeReadIndex(signatureNode) == Cudd_NodeReadIndex(nonBlockVariablesNode)) {
signatureThen = Cudd_T(signatureNode);
signatureElse = Cudd_E(signatureNode);
skippedBoth = false;
} else {
signatureThen = signatureElse = signatureNode;
}
// If both (signature and partition) skipped the next variable, we fast-forward.
if (skippedBoth) {
// If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
nonBlockVariablesNode = Cudd_T(nonBlockVariablesNode);
if (isNondeterminismVariable) {
nondeterminismVariablesNode = Cudd_T(nondeterminismVariablesNode);
}
}
}
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if (Cudd_IsConstant(nonBlockVariablesNode)) {
return refine(partitionNode, signatureNode, nondeterminismVariablesNode, nonBlockVariablesNode);
}
std::pair<DdNodePtr, DdNodePtr> combinedThenResult = refine(partitionThen, signatureThen, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode, Cudd_T(nonBlockVariablesNode));
DdNodePtr thenResult = combinedThenResult.first;
DdNodePtr changedThenResult = combinedThenResult.second;
Cudd_Ref(thenResult);
if (options.createChangedStates) {
Cudd_Ref(changedThenResult);
} else {
STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
}
std::pair<DdNodePtr, DdNodePtr> combinedElseResult = refine(partitionElse, signatureElse, isNondeterminismVariable ? Cudd_T(nondeterminismVariablesNode) : nondeterminismVariablesNode, Cudd_T(nonBlockVariablesNode));
DdNodePtr elseResult = combinedElseResult.first;
DdNodePtr changedElseResult = combinedElseResult.second;
Cudd_Ref(elseResult);
if (options.createChangedStates) {
Cudd_Ref(changedElseResult);
} else {
STORM_LOG_ASSERT(!changedThenResult, "Expected not changed state DD.");
}
DdNodePtr result;
if (thenResult == elseResult) {
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
result = thenResult;
} else {
// Get the node to connect the subresults.
bool complemented = Cudd_IsComplement(thenResult);
result = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(thenResult), complemented ? Cudd_Not(elseResult) : elseResult);
if (complemented) {
result = Cudd_Not(result);
}
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
}
DdNodePtr changedResult = nullptr;
if (options.createChangedStates) {
if (changedThenResult == changedElseResult) {
Cudd_Deref(changedThenResult);
Cudd_Deref(changedElseResult);
changedResult = changedThenResult;
} else {
// Get the node to connect the subresults.
bool complemented = Cudd_IsComplement(changedThenResult);
changedResult = cuddUniqueInter(ddman, Cudd_NodeReadIndex(nonBlockVariablesNode) + offset, Cudd_Regular(changedThenResult), complemented ? Cudd_Not(changedElseResult) : changedElseResult);
if (complemented) {
changedResult = Cudd_Not(changedResult);
}
Cudd_Deref(changedThenResult);
Cudd_Deref(changedElseResult);
}
}
// Store the result in the cache.
auto pairResult = std::make_pair(result, changedResult);
signatureCache[nodePair] = pairResult;
return pairResult;
}
}
template class InternalSignatureRefiner<storm::dd::DdType::CUDD, double>;
}
}
}

84
src/storm/storage/dd/bisimulation/InternalCuddSignatureRefiner.h

@ -0,0 +1,84 @@
#pragma once
#include <set>
#include <boost/optional.hpp>
#include "storm/storage/dd/bisimulation/InternalSignatureRefiner.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/storage/expressions/Variable.h"
#include "storm/storage/dd/cudd/utility.h"
#include <sparsepp/spp.h>
namespace storm {
namespace dd {
template <storm::dd::DdType DdType>
class DdManager;
template <storm::dd::DdType DdType, typename ValueType>
class Add;
namespace bisimulation {
template <storm::dd::DdType DdType, typename ValueType>
class Partition;
template <storm::dd::DdType DdType, typename ValueType>
class Signature;
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, std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::CUDD> const& nondeterminismVariables, storm::dd::Bdd<storm::dd::DdType::CUDD> const& nonBlockVariables, InternalSignatureRefinerOptions const& options = InternalSignatureRefinerOptions());
Partition<storm::dd::DdType::CUDD, ValueType> refine(Partition<storm::dd::DdType::CUDD, ValueType> const& oldPartition, Signature<storm::dd::DdType::CUDD, ValueType> const& signature);
private:
void clearCaches();
std::pair<storm::dd::Add<storm::dd::DdType::CUDD, ValueType>, boost::optional<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);
DdNodePtr encodeBlock(uint64_t blockIndex);
std::pair<DdNodePtr, DdNodePtr> reuseOrRelabel(DdNode* partitionNode, DdNode* nondeterminismVariablesNode, DdNode* nonBlockVariablesNode);
std::pair<DdNodePtr, DdNodePtr> refine(DdNode* partitionNode, DdNode* signatureNode, DdNode* nondeterminismVariablesNode, DdNode* nonBlockVariablesNode);
storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager;
::DdManager* ddman;
storm::expressions::Variable blockVariable;
std::set<storm::expressions::Variable> stateVariables;
// The cubes representing all non-block and all nondeterminism variables, respectively.
storm::dd::Bdd<storm::dd::DdType::CUDD> nondeterminismVariables;
storm::dd::Bdd<storm::dd::DdType::CUDD> nonBlockVariables;
// The provided options.
InternalSignatureRefinerOptions options;
// The indices of the DD variables associated with the block variable.
std::vector<uint64_t> blockDdVariableIndices;
// A vector used for encoding block indices.
std::vector<int> blockEncoding;
// 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*>, std::pair<DdNodePtr, DdNodePtr>, CuddPointerPairHash> signatureCache;
// The cache used to identify which old block numbers have already been reused.
spp::sparse_hash_map<DdNode const*, ReuseWrapper> reuseBlocksCache;
};
}
}
}

45
src/storm/storage/dd/bisimulation/InternalSignatureRefiner.cpp

@ -0,0 +1,45 @@
#include "storm/storage/dd/bisimulation/InternalSignatureRefiner.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/BisimulationSettings.h"
namespace storm {
namespace dd {
namespace bisimulation {
InternalSignatureRefinerOptions::InternalSignatureRefinerOptions() : InternalSignatureRefinerOptions(true) {
// Intentionally left empty.
}
InternalSignatureRefinerOptions::InternalSignatureRefinerOptions(bool shiftStateVariables) : shiftStateVariables(shiftStateVariables), createChangedStates(true), parallel(false) {
auto const& bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
storm::settings::modules::BisimulationSettings::ReuseMode reuseMode = bisimulationSettings.getReuseMode();
this->reuseBlockNumbers = reuseMode == storm::settings::modules::BisimulationSettings::ReuseMode::BlockNumbers;
storm::settings::modules::BisimulationSettings::RefinementMode refinementMode = bisimulationSettings.getRefinementMode();
this->createChangedStates = refinementMode == storm::settings::modules::BisimulationSettings::RefinementMode::ChangedStates;
storm::settings::modules::BisimulationSettings::ParallelismMode parallelismMode = bisimulationSettings.getParallelismMode();
this->parallel = parallelismMode == storm::settings::modules::BisimulationSettings::ParallelismMode::Parallel;
}
ReuseWrapper::ReuseWrapper() : ReuseWrapper(false) {
// Intentionally left empty.
}
ReuseWrapper::ReuseWrapper(bool value) : value(value) {
// Intentionally left empty.
}
bool ReuseWrapper::isReused() const {
return value;
}
void ReuseWrapper::setReused() {
value = true;
}
}
}
}

36
src/storm/storage/dd/bisimulation/InternalSignatureRefiner.h

@ -0,0 +1,36 @@
#pragma once
#include "storm/storage/dd/DdType.h"
namespace storm {
namespace dd {
namespace bisimulation {
template <storm::dd::DdType DdType, typename ValueType>
class InternalSignatureRefiner;
struct InternalSignatureRefinerOptions {
InternalSignatureRefinerOptions();
InternalSignatureRefinerOptions(bool shiftStateVariables);
bool shiftStateVariables;
bool reuseBlockNumbers;
bool createChangedStates;
bool parallel;
};
class ReuseWrapper {
public:
ReuseWrapper();
ReuseWrapper(bool value);
bool isReused() const;
void setReused();
private:
bool value;
};
}
}
}

632
src/storm/storage/dd/bisimulation/InternalSylvanSignatureRefiner.cpp

@ -0,0 +1,632 @@
#include "storm/storage/dd/bisimulation/InternalSylvanSignatureRefiner.h"
#include "storm/storage/dd/DdManager.h"
#include "storm/storage/dd/bisimulation/Partition.h"
#include "storm/storage/dd/bisimulation/Signature.h"
#include "sylvan_cache.h"
namespace storm {
namespace dd {
namespace bisimulation {
InternalSylvanSignatureRefinerBase::InternalSylvanSignatureRefinerBase(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables, InternalSignatureRefinerOptions const& options) : manager(manager), blockVariable(blockVariable), stateVariables(stateVariables), nondeterminismVariables(nondeterminismVariables), nonBlockVariables(nonBlockVariables), options(options), numberOfBlockVariables(manager.getMetaVariable(blockVariable).getNumberOfDdVariables()), blockCube(manager.getMetaVariable(blockVariable).getCube()), nextFreeBlockIndex(0), numberOfRefinements(0), signatureCache(), resize(0) {
// Perform garbage collection to clean up stuff not needed anymore.
LACE_ME;
sylvan_gc();
table.resize(3 * 8 * (1ull << 14));
}
BDD InternalSylvanSignatureRefinerBase::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());
}
template<typename ValueType>
InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables, InternalSignatureRefinerOptions const& options) : storm::dd::bisimulation::InternalSylvanSignatureRefinerBase(manager, blockVariable, stateVariables, nondeterminismVariables, nonBlockVariables, options) {
// Intentionally left empty.
}
template<typename ValueType>
Partition<storm::dd::DdType::Sylvan, ValueType> InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, Signature<storm::dd::DdType::Sylvan, ValueType> const& signature) {
std::pair<storm::dd::Bdd<storm::dd::DdType::Sylvan>, boost::optional<storm::dd::Bdd<storm::dd::DdType::Sylvan>>> newPartitionDds = refine(oldPartition, signature.getSignatureAdd());
++numberOfRefinements;
return oldPartition.replacePartition(newPartitionDds.first, nextFreeBlockIndex, nextFreeBlockIndex, newPartitionDds.second);
}
template<typename ValueType>
void InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::clearCaches() {
signatureCache.clear();
reuseBlocksCache.clear();
this->table = std::vector<uint64_t>(table.size());
this->signatures = std::vector<uint64_t>(signatures.size());
}
template<typename ValueType>
std::pair<storm::dd::Bdd<storm::dd::DdType::Sylvan>, boost::optional<storm::dd::Bdd<storm::dd::DdType::Sylvan>>> InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::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.");
nextFreeBlockIndex = options.reuseBlockNumbers ? oldPartition.getNextFreeBlockIndex() : 0;
signatures.resize(nextFreeBlockIndex);
// Perform the actual recursive refinement step.
std::pair<BDD, BDD> result;
if (options.parallel) {
STORM_LOG_TRACE("Using parallel refine.");
result = refineParallel(oldPartition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD(), nondeterminismVariables.getInternalBdd().getSylvanBdd().GetBDD(), nonBlockVariables.getInternalBdd().getSylvanBdd().GetBDD());
} else {
STORM_LOG_TRACE("Using sequential refine.");
result = refineSequential(oldPartition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD(), nondeterminismVariables.getInternalBdd().getSylvanBdd().GetBDD(), nonBlockVariables.getInternalBdd().getSylvanBdd().GetBDD());
}
// Construct resulting BDD from the obtained node and the meta information.
storm::dd::InternalBdd<storm::dd::DdType::Sylvan> internalNewPartitionBdd(&manager.getInternalDdManager(), sylvan::Bdd(result.first));
storm::dd::Bdd<storm::dd::DdType::Sylvan> newPartitionBdd(oldPartition.asBdd().getDdManager(), internalNewPartitionBdd, oldPartition.asBdd().getContainedMetaVariables());
boost::optional<storm::dd::Bdd<storm::dd::DdType::Sylvan>> optionalChangedBdd;
if (options.createChangedStates && result.second != 0) {
storm::dd::InternalBdd<storm::dd::DdType::Sylvan> internalChangedBdd(&manager.getInternalDdManager(), sylvan::Bdd(result.second));
storm::dd::Bdd<storm::dd::DdType::Sylvan> changedBdd(oldPartition.asBdd().getDdManager(), internalChangedBdd, stateVariables);
optionalChangedBdd = changedBdd;
}
clearCaches();
return std::make_pair(newPartitionBdd, optionalChangedBdd);
}
template<typename ValueType>
std::pair<BDD, BDD> InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::reuseOrRelabel(BDD partitionNode, BDD nondeterminismVariablesNode, BDD nonBlockVariablesNode) {
LACE_ME;
if (partitionNode == sylvan_false) {
if (options.createChangedStates) {
return std::make_pair(sylvan_false, sylvan_false);
} else {
return std::make_pair(sylvan_false, 0);
}
}
// Check the cache whether we have seen the same node before.
auto nodePair = std::make_pair(0, partitionNode);
auto it = signatureCache.find(nodePair);
if (it != signatureCache.end()) {
// If so, we return the corresponding result.
return it->second;
}
sylvan_gc_test();
// If there are no more non-block variables, we hit the signature.
if (sylvan_isconst(nonBlockVariablesNode)) {
if (options.reuseBlockNumbers) {
// 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& reuseBlockEntry = reuseBlocksCache[partitionNode];
if (!reuseBlockEntry.isReused()) {
reuseBlockEntry.setReused();
std::pair<BDD, BDD> result;
if (options.createChangedStates) {
result = std::make_pair(partitionNode, sylvan_false);
} else {
result = std::make_pair(partitionNode, 0);
}
signatureCache[nodePair] = result;
return result;
}
}
std::pair<BDD, BDD> result;
if (options.createChangedStates) {
result = std::make_pair(encodeBlock(nextFreeBlockIndex++), sylvan_true);
} else {
result = std::make_pair(encodeBlock(nextFreeBlockIndex++), 0);
}
signatureCache[nodePair] = result;
return result;
} else {
// If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
bool skipped = true;
BDD partitionThen;
BDD partitionElse;
short offset;
bool isNondeterminismVariable = false;
while (skipped && !sylvan_isconst(nonBlockVariablesNode)) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = options.shiftStateVariables ? 1 : 0;
if (!sylvan_isconst(nondeterminismVariablesNode) && sylvan_var(nondeterminismVariablesNode) == sylvan_var(nonBlockVariablesNode)) {
offset = 0;
isNondeterminismVariable = true;
}
if (sylvan_mtbdd_matches_variable_index(partitionNode, sylvan_var(nonBlockVariablesNode), -offset)) {
partitionThen = sylvan_high(partitionNode);
partitionElse = sylvan_low(partitionNode);
skipped = false;
} else {
partitionThen = partitionElse = partitionNode;
}
// If we skipped the next variable, we fast-forward.
if (skipped) {
// If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
nonBlockVariablesNode = sylvan_high(nonBlockVariablesNode);
if (isNondeterminismVariable) {
nondeterminismVariablesNode = sylvan_high(nondeterminismVariablesNode);
}
}
}
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if (sylvan_isconst(nonBlockVariablesNode)) {
return reuseOrRelabel(partitionNode, nondeterminismVariablesNode, nonBlockVariablesNode);
}
std::pair<BDD, BDD> combinedThenResult = reuseOrRelabel(partitionThen, isNondeterminismVariable ? sylvan_high(nondeterminismVariablesNode) : nondeterminismVariablesNode, sylvan_high(nonBlockVariablesNode));
BDD thenResult = combinedThenResult.first;
bdd_refs_push(thenResult);
BDD changedThenResult = combinedThenResult.second;
if (options.createChangedStates) {
bdd_refs_push(changedThenResult);
}
std::pair<BDD, BDD> combinedElseResult = reuseOrRelabel(partitionElse, isNondeterminismVariable ? sylvan_high(nondeterminismVariablesNode) : nondeterminismVariablesNode, sylvan_high(nonBlockVariablesNode));
BDD elseResult = combinedElseResult.first;
bdd_refs_push(elseResult);
BDD changedElseResult = combinedElseResult.second;
if (options.createChangedStates) {
bdd_refs_push(changedElseResult);
}
std::pair<BDD, BDD> result;
if (thenResult == elseResult) {
result.first = thenResult;
} else {
// Get the node to connect the subresults.
result.first = sylvan_makenode(sylvan_var(nonBlockVariablesNode) + offset, elseResult, thenResult);
}
result.second = 0;
if (options.createChangedStates) {
if (changedThenResult == changedElseResult) {
result.second = changedThenResult;
} else {
// Get the node to connect the subresults.
result.second = sylvan_makenode(sylvan_var(nonBlockVariablesNode) + offset, changedElseResult, changedThenResult);
}
}
// Dispose of the intermediate results.
if (options.createChangedStates) {
bdd_refs_pop(4);
} else {
bdd_refs_pop(2);
}
// Store the result in the cache.
signatureCache[nodePair] = result;
return result;
}
}
template<typename ValueType>
std::pair<BDD, BDD> InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::refineParallel(BDD partitionNode, MTBDD signatureNode, BDD nondeterminismVariablesNode, BDD nonBlockVariablesNode) {
LACE_ME;
// return std::make_pair(CALL(refine_parallel, partitionNode, signatureNode, nondeterminismVariablesNode, nonBlockVariablesNode, this), 0);
return std::make_pair(CALL(sylvan_refine_partition, signatureNode, nonBlockVariablesNode, partitionNode, this), 0);
}
template<typename ValueType>
std::pair<BDD, BDD> InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType>::refineSequential(BDD partitionNode, MTBDD signatureNode, BDD nondeterminismVariablesNode, BDD nonBlockVariablesNode) {
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) {
if (options.createChangedStates) {
return std::make_pair(sylvan_false, sylvan_false);
} else {
return std::make_pair(sylvan_false, 0);
}
} else if (mtbdd_iszero(signatureNode)) {
std::pair<BDD, BDD> result = reuseOrRelabel(partitionNode, nondeterminismVariablesNode, nonBlockVariablesNode);
signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
return result;
}
STORM_LOG_ASSERT(partitionNode != mtbdd_false, "Expected non-false node.");
// Check the cache whether we have seen the same node before.
auto nodePair = std::make_pair(signatureNode, partitionNode);
auto it = signatureCache.find(nodePair);
if (it != signatureCache.end()) {
// If so, we return the corresponding result.
return it->second;
}
sylvan_gc_test();
// If there are no more non-block variables, we hit the signature.
if (sylvan_isconst(nonBlockVariablesNode)) {
if (options.reuseBlockNumbers) {
// 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& reuseBlockEntry = reuseBlocksCache[partitionNode];
if (!reuseBlockEntry.isReused()) {
reuseBlockEntry.setReused();
std::pair<BDD, BDD> result;
if (options.createChangedStates) {
result = std::make_pair(partitionNode, sylvan_false);
} else {
result = std::make_pair(partitionNode, 0);
}
signatureCache[nodePair] = result;
return result;
}
}
std::pair<BDD, BDD> result;
if (options.createChangedStates) {
result = std::make_pair(encodeBlock(nextFreeBlockIndex++), sylvan_true);
} else {
result = std::make_pair(encodeBlock(nextFreeBlockIndex++), 0);
}
signatureCache[nodePair] = result;
return result;
} else {
// If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
bool skippedBoth = true;
BDD partitionThen;
BDD partitionElse;
MTBDD signatureThen;
MTBDD signatureElse;
short offset;
bool isNondeterminismVariable = false;
while (skippedBoth && !sylvan_isconst(nonBlockVariablesNode)) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = options.shiftStateVariables ? 1 : 0;
if (!sylvan_isconst(nondeterminismVariablesNode) && sylvan_var(nondeterminismVariablesNode) == sylvan_var(nonBlockVariablesNode)) {
offset = 0;
isNondeterminismVariable = true;
}
if (sylvan_mtbdd_matches_variable_index(partitionNode, sylvan_var(nonBlockVariablesNode), -offset)) {
partitionThen = sylvan_high(partitionNode);
partitionElse = sylvan_low(partitionNode);
skippedBoth = false;
} else {
partitionThen = partitionElse = partitionNode;
}
if (sylvan_mtbdd_matches_variable_index(signatureNode, sylvan_var(nonBlockVariablesNode))) {
signatureThen = sylvan_high(signatureNode);
signatureElse = sylvan_low(signatureNode);
skippedBoth = false;
} else {
signatureThen = signatureElse = signatureNode;
}
// If both (signature and partition) skipped the next variable, we fast-forward.
if (skippedBoth) {
// If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
nonBlockVariablesNode = sylvan_high(nonBlockVariablesNode);
if (isNondeterminismVariable) {
nondeterminismVariablesNode = sylvan_high(nondeterminismVariablesNode);
}
}
}
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if (sylvan_isconst(nonBlockVariablesNode)) {
return refineSequential(partitionNode, signatureNode, nondeterminismVariablesNode, nonBlockVariablesNode);
}
std::pair<BDD, BDD> combinedThenResult = refineSequential(partitionThen, signatureThen, isNondeterminismVariable ? sylvan_high(nondeterminismVariablesNode) : nondeterminismVariablesNode, sylvan_high(nonBlockVariablesNode));
BDD thenResult = combinedThenResult.first;
bdd_refs_push(thenResult);
BDD changedThenResult = combinedThenResult.second;
if (options.createChangedStates) {
bdd_refs_push(changedThenResult);
}
std::pair<BDD, BDD> combinedElseResult = refineSequential(partitionElse, signatureElse, isNondeterminismVariable ? sylvan_high(nondeterminismVariablesNode) : nondeterminismVariablesNode, sylvan_high(nonBlockVariablesNode));
BDD elseResult = combinedElseResult.first;
bdd_refs_push(elseResult);
BDD changedElseResult = combinedElseResult.second;
if (options.createChangedStates) {
bdd_refs_push(changedElseResult);
}
std::pair<BDD, BDD> result;
if (thenResult == elseResult) {
result.first = thenResult;
} else {
// Get the node to connect the subresults.
result.first = sylvan_makenode(sylvan_var(nonBlockVariablesNode) + offset, elseResult, thenResult);
}
result.second = 0;
if (options.createChangedStates) {
if (changedThenResult == changedElseResult) {
result.second = changedThenResult;
} else {
// Get the node to connect the subresults.
result.second = sylvan_makenode(sylvan_var(nonBlockVariablesNode) + offset, changedElseResult, changedThenResult);
}
}
// Dispose of the intermediate results.
if (options.createChangedStates) {
bdd_refs_pop(4);
} else {
bdd_refs_pop(2);
}
// Store the result in the cache.
signatureCache[nodePair] = result;
return result;
}
}
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new)))
#define ATOMIC_READ(x) (*(volatile decltype(x) *)&(x))
/* Rotating 64-bit FNV-1a hash */
static uint64_t
sylvan_hash(uint64_t a, uint64_t b)
{
const uint64_t prime = 1099511628211;
uint64_t hash = 14695981039346656037LLU;
hash = (hash ^ (a>>32));
hash = (hash ^ a) * prime;
hash = (hash ^ b) * prime;
return hash ^ (hash>>32);
}
VOID_TASK_3(sylvan_rehash, size_t, first, size_t, count, void*, refinerPtr)
{
auto& refiner = *static_cast<InternalSylvanSignatureRefinerBase*>(refinerPtr);
if (count > 128) {
SPAWN(sylvan_rehash, first, count/2, refinerPtr);
CALL(sylvan_rehash, first+count/2, count-count/2, refinerPtr);
SYNC(sylvan_rehash);
return;
}
while (count--) {
uint64_t *old_ptr = refiner.oldTable.data() + first*3;
uint64_t a = old_ptr[0];
uint64_t b = old_ptr[1];
uint64_t c = old_ptr[2];
uint64_t hash = sylvan_hash(a, b);
uint64_t pos = hash % (refiner.table.size() / 3);
volatile uint64_t *ptr = 0;
for (;;) {
ptr = refiner.table.data() + pos*3;
if (*ptr == 0) {
if (cas(ptr, 0, a)) {
ptr[1] = b;
ptr[2] = c;
break;
}
}
pos++;
if (pos >= (refiner.table.size() / 3)) pos = 0;
}
first++;
}
}
VOID_TASK_1(sylvan_grow_it, void*, refinerPtr)
{
auto& refiner = *static_cast<InternalSylvanSignatureRefinerBase*>(refinerPtr);
refiner.oldTable = std::move(refiner.table);
refiner.table = std::vector<uint64_t>(refiner.oldTable.size() << 1);
CALL(sylvan_rehash, 0, refiner.oldTable.size() / 3, refinerPtr);
refiner.oldTable.clear();
}
VOID_TASK_1(sylvan_grow, void*, refinerPtr)
{
auto& refiner = *static_cast<InternalSylvanSignatureRefinerBase*>(refinerPtr);
if (cas(&refiner.resize, 0, 1)) {
NEWFRAME(sylvan_grow_it, refinerPtr);
refiner.resize = 0;
} else {
/* wait for new frame to appear */
while (ATOMIC_READ(lace_newframe.t) == 0) {}
lace_yield(__lace_worker, __lace_dq_head);
}
}
static uint64_t
sylvan_search_or_insert(uint64_t sig, uint64_t previous_block, void* refinerPtr)
{
auto& refiner = *static_cast<InternalSylvanSignatureRefinerBase*>(refinerPtr);
uint64_t hash = sylvan_hash(sig, previous_block);
uint64_t pos = hash % (refiner.table.size() / 3);
volatile uint64_t *ptr = 0;
uint64_t a, b, c;
int count = 0;
for (;;) {
ptr = refiner.table.data() + pos*3;
a = *ptr;
if (a == sig) {
while ((b=ptr[1]) == 0) continue;
if (b == previous_block) {
while ((c=ptr[2]) == 0) continue;
return c;
}
} else if (a == 0) {
if (cas(ptr, 0, sig)) {
c = ptr[2] = __sync_fetch_and_add(&refiner.nextFreeBlockIndex, 1);
ptr[1] = previous_block;
return c;
} else {
continue;
}
}
pos++;
if (pos >= (refiner.table.size() / 3)) pos = 0;
if (++count >= 128) return 0;
}
}
TASK_1(uint64_t, sylvan_decode_block, BDD, block)
{
uint64_t result = 0;
uint64_t mask = 1;
while (block != sylvan_true) {
BDD b_low = sylvan_low(block);
if (b_low == sylvan_false) {
result |= mask;
block = sylvan_high(block);
} else {
block = b_low;
}
mask <<= 1;
}
return result;
}
TASK_3(BDD, sylvan_assign_block, BDD, sig, BDD, previous_block, void*, refinerPtr)
{
assert(previous_block != mtbdd_false); // if so, incorrect call!
auto& refiner = *static_cast<InternalSylvanSignatureRefinerBase*>(refinerPtr);
// maybe do garbage collection
sylvan_gc_test();
if (sig == sylvan_false) {
// slightly different handling because sylvan_false == 0
sig = (uint64_t)-1;
}
// try to claim previous block number
const uint64_t p_b = CALL(sylvan_decode_block, previous_block);
assert(p_b != 0);
for (;;) {
BDD cur = *(volatile BDD*)&refiner.signatures[p_b];
if (cur == sig) return previous_block;
if (cur != 0) break;
if (cas(&refiner.signatures[p_b], 0, sig)) return previous_block;
}
// no previous block number, search or insert
uint64_t c;
while ((c = sylvan_search_or_insert(sig, previous_block, refinerPtr)) == 0) CALL(sylvan_grow, refinerPtr);
// if (c >= refiner.signatures.size()) {
// fprintf(stderr, "Out of cheese exception, no more blocks available\n");
// exit(1);
// }
// return CALL(refiner.encodeBlock(c));
return refiner.encodeBlock(c);
}
TASK_4(BDD, sylvan_refine_partition, BDD, dd, BDD, vars, BDD, previous_partition, void*, refinerPtr)
{
auto& refiner = *static_cast<InternalSylvanSignatureRefinerBase*>(refinerPtr);
/* expecting dd as in s,a,B */
/* expecting vars to be conjunction of variables in s */
/* expecting previous_partition as in t,B */
if (previous_partition == sylvan_false) {
/* it had no block in the previous iteration, therefore also not now */
return sylvan_false;
}
if (sylvan_set_isempty(vars)) {
BDD result;
if (cache_get(dd|(256LL<<42), vars, previous_partition|(refiner.numberOfRefinements<<40), &result)) return result;
result = CALL(sylvan_assign_block, dd, previous_partition, refinerPtr);
cache_put(dd|(256LL<<42), vars, previous_partition|(refiner.numberOfRefinements<<40), result);
return result;
}
sylvan_gc_test();
/* vars != sylvan_false */
/* dd cannot be sylvan_true - if vars != sylvan_true, then dd is in a,B */
BDDVAR dd_var = sylvan_isconst(dd) ? 0xffffffff : sylvan_var(dd);
BDDVAR pp_var = sylvan_var(previous_partition);
BDDVAR vars_var = sylvan_var(vars);
while (vars_var < dd_var && vars_var+1 < pp_var) {
vars = sylvan_set_next(vars);
if (sylvan_set_isempty(vars)) return CALL(sylvan_refine_partition, dd, vars, previous_partition, refinerPtr);
vars_var = sylvan_var(vars);
}
/* Consult cache */
BDD result;
if (cache_get(dd|(256LL<<42), vars, previous_partition|(refiner.numberOfRefinements<<40), &result)) {
return result;
}
/* Compute cofactors */
BDD dd_low, dd_high;
if (vars_var == dd_var) {
dd_low = sylvan_low(dd);
dd_high = sylvan_high(dd);
} else {
dd_low = dd_high = dd;
}
BDD pp_low, pp_high;
if (vars_var+1 == pp_var) {
pp_low = sylvan_low(previous_partition);
pp_high = sylvan_high(previous_partition);
} else {
pp_low = pp_high = previous_partition;
}
/* Recursive steps */
BDD next_vars = sylvan_set_next(vars);
bdd_refs_spawn(SPAWN(sylvan_refine_partition, dd_low, next_vars, pp_low, refinerPtr));
BDD high = bdd_refs_push(CALL(sylvan_refine_partition, dd_high, next_vars, pp_high, refinerPtr));
BDD low = bdd_refs_sync(SYNC(sylvan_refine_partition));
bdd_refs_pop(1);
/* rename from s to t */
result = sylvan_makenode(vars_var+1, low, high);
/* Write to cache */
cache_put(dd|(256LL<<42), vars, previous_partition|(refiner.numberOfRefinements<<40), result);
return result;
}
template class InternalSignatureRefiner<storm::dd::DdType::Sylvan, double>;
template class InternalSignatureRefiner<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class InternalSignatureRefiner<storm::dd::DdType::Sylvan, storm::RationalFunction>;
}
}
}

85
src/storm/storage/dd/bisimulation/InternalSylvanSignatureRefiner.h

@ -0,0 +1,85 @@
#pragma once
#include "storm/storage/dd/bisimulation/InternalSignatureRefiner.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/storage/expressions/Variable.h"
#include "storm/storage/dd/sylvan/utility.h"
#include <sparsepp/spp.h>
namespace storm {
namespace dd {
template <storm::dd::DdType DdType>
class DdManager;
namespace bisimulation {
template <storm::dd::DdType DdType, typename ValueType>
class Partition;
template <storm::dd::DdType DdType, typename ValueType>
class Signature;
class InternalSylvanSignatureRefinerBase {
public:
InternalSylvanSignatureRefinerBase(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables, InternalSignatureRefinerOptions const& options);
BDD encodeBlock(uint64_t blockIndex);
storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager;
storm::expressions::Variable blockVariable;
std::set<storm::expressions::Variable> stateVariables;
storm::dd::Bdd<storm::dd::DdType::Sylvan> nondeterminismVariables;
storm::dd::Bdd<storm::dd::DdType::Sylvan> nonBlockVariables;
// The provided options.
InternalSignatureRefinerOptions options;
uint64_t numberOfBlockVariables;
storm::dd::Bdd<storm::dd::DdType::Sylvan> blockCube;
// 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::pair<BDD, BDD>, SylvanMTBDDPairHash> signatureCache;
// The cache used to identify which old block numbers have already been reused.
spp::sparse_hash_map<MTBDD, ReuseWrapper> reuseBlocksCache;
// Data used by sylvan implementation.
std::vector<BDD> signatures;
std::vector<uint64_t> table;
std::vector<uint64_t> oldTable;
uint64_t resize;
};
template<typename ValueType>
class InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType> : public InternalSylvanSignatureRefinerBase {
public:
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, std::set<storm::expressions::Variable> const& stateVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nondeterminismVariables, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& nonBlockVariables, InternalSignatureRefinerOptions const& options);
Partition<storm::dd::DdType::Sylvan, ValueType> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, Signature<storm::dd::DdType::Sylvan, ValueType> const& signature);
private:
void clearCaches();
std::pair<storm::dd::Bdd<storm::dd::DdType::Sylvan>, boost::optional<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);
std::pair<BDD, BDD> reuseOrRelabel(BDD partitionNode, BDD nondeterminismVariablesNode, BDD nonBlockVariablesNode);
std::pair<BDD, BDD> refineParallel(BDD partitionNode, MTBDD signatureNode, BDD nondeterminismVariablesNode, BDD nonBlockVariablesNode);
std::pair<BDD, BDD> refineSequential(BDD partitionNode, MTBDD signatureNode, BDD nondeterminismVariablesNode, BDD nonBlockVariablesNode);
};
}
}
}

6
src/storm/storage/dd/bisimulation/Partition.cpp

@ -135,7 +135,7 @@ namespace storm {
storm::dd::Bdd<DdType> partitionBdd = manager.getBddZero();
storm::dd::Bdd<DdType> transitionMatrixBdd = model.getTransitionMatrix().notZero().existsAbstract(model.getNondeterminismVariables());
storm::dd::Bdd<DdType> coveredStates = manager.getBddZero();
uint64_t blockCount = 0;
uint64_t blockCount = 1;
// Backward BFS.
storm::dd::Bdd<DdType> backwardFrontier = targetStates;
@ -244,7 +244,7 @@ namespace storm {
template<storm::dd::DdType DdType, typename ValueType>
uint64_t Partition<DdType, ValueType>::getNumberOfBlocks() const {
return numberOfBlocks;
return numberOfBlocks - 1;
}
template<storm::dd::DdType DdType, typename ValueType>
@ -311,7 +311,7 @@ namespace storm {
template<storm::dd::DdType DdType, typename ValueType>
std::pair<storm::dd::Bdd<DdType>, uint64_t> Partition<DdType, ValueType>::createPartitionBdd(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;
uint64_t blockCount = 1;
storm::dd::Bdd<DdType> partitionBdd = manager.getBddZero();
// Enumerate all realizable blocks.

1013
src/storm/storage/dd/bisimulation/SignatureRefiner.cpp
File diff suppressed because it is too large
View File

Loading…
Cancel
Save