Browse Source

first version of sparse quotient extraction for dd bisimulation

main
dehnert 8 years ago
parent
commit
f0f4cd7390
  1. 2
      src/storm/cli/cli.cpp
  2. 4
      src/storm/models/sparse/Model.cpp
  3. 4
      src/storm/models/sparse/Model.h
  4. 4
      src/storm/models/symbolic/Model.cpp
  5. 4
      src/storm/models/symbolic/Model.h
  6. 4
      src/storm/settings/modules/BisimulationSettings.cpp
  7. 6
      src/storm/storage/SparseMatrix.cpp
  8. 4
      src/storm/storage/dd/BisimulationDecomposition.cpp
  9. 2
      src/storm/storage/dd/BisimulationDecomposition.h
  10. 22
      src/storm/storage/dd/DdMetaVariable.cpp
  11. 10
      src/storm/storage/dd/DdMetaVariable.h
  12. 499
      src/storm/storage/dd/bisimulation/QuotientExtractor.cpp
  13. 5
      src/storm/storage/dd/bisimulation/QuotientExtractor.h
  14. 2
      src/storm/storage/dd/cudd/InternalCuddAdd.h
  15. 2
      src/storm/storage/dd/cudd/InternalCuddBdd.h
  16. 14
      src/storm/storage/dd/sylvan/InternalSylvanAdd.h
  17. 1
      src/storm/transformer/SymbolicToSparseTransformer.cpp
  18. 2
      src/storm/utility/storm.h

2
src/storm/cli/cli.cpp

@ -277,8 +277,6 @@ namespace storm {
model = model.preprocess(constantDefinitions);
if (ioSettings.isNoBuildModelSet()) {
return;
}

4
src/storm/models/sparse/Model.cpp

@ -20,7 +20,7 @@ namespace storm {
storm::models::sparse::StateLabeling const& stateLabeling,
std::unordered_map<std::string, RewardModelType> const& rewardModels,
boost::optional<std::vector<LabelSet>> const& optionalChoiceLabeling)
: ModelBase(modelType), transitionMatrix(transitionMatrix), stateLabeling(stateLabeling),
: storm::models::Model<ValueType>(modelType), transitionMatrix(transitionMatrix), stateLabeling(stateLabeling),
rewardModels(rewardModels), choiceLabeling(optionalChoiceLabeling) {
for (auto const& rewardModel : this->getRewardModels()) {
STORM_LOG_THROW(!rewardModel.second.hasTransitionRewards() || rewardModel.second.getTransitionRewardMatrix().isSubmatrixOf(this->getTransitionMatrix()), storm::exceptions::IllegalArgumentException, "The transition reward matrix is not a submatrix of the transition matrix, i.e. there are rewards for transitions that do not exist.");
@ -33,7 +33,7 @@ namespace storm {
storm::models::sparse::StateLabeling&& stateLabeling,
std::unordered_map<std::string, RewardModelType>&& rewardModels,
boost::optional<std::vector<LabelSet>>&& optionalChoiceLabeling)
: ModelBase(modelType), transitionMatrix(std::move(transitionMatrix)), stateLabeling(std::move(stateLabeling)),
: storm::models::Model<ValueType>(modelType), transitionMatrix(std::move(transitionMatrix)), stateLabeling(std::move(stateLabeling)),
rewardModels(std::move(rewardModels)), choiceLabeling(std::move(optionalChoiceLabeling)) {
for (auto const& rewardModel : this->getRewardModels()) {
STORM_LOG_THROW(!rewardModel.second.hasTransitionRewards() || rewardModel.second.getTransitionRewardMatrix().isSubmatrixOf(this->getTransitionMatrix()), storm::exceptions::IllegalArgumentException, "The transition reward matrix is not a submatrix of the transition matrix, i.e. there are rewards for transitions that do not exist.");

4
src/storm/models/sparse/Model.h

@ -6,7 +6,7 @@
#include <boost/container/flat_set.hpp>
#include <boost/optional.hpp>
#include "storm/models/ModelBase.h"
#include "storm/models/Model.h"
#include "storm/models/sparse/StateLabeling.h"
#include "storm/storage/sparse/StateType.h"
#include "storm/storage/SparseMatrix.h"
@ -35,7 +35,7 @@ namespace storm {
* Base class for all sparse models.
*/
template<class CValueType, class CRewardModelType = StandardRewardModel<CValueType>>
class Model : public storm::models::ModelBase {
class Model : public storm::models::Model<CValueType> {
template<typename ParametricModelType, typename ConstantModelType>
friend class storm::utility::ModelInstantiator;

4
src/storm/models/symbolic/Model.cpp

@ -35,7 +35,7 @@ namespace storm {
std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& rowColumnMetaVariablePairs,
std::map<std::string, storm::expressions::Expression> labelToExpressionMap,
std::unordered_map<std::string, RewardModelType> const& rewardModels)
: ModelBase(modelType), manager(manager), reachableStates(reachableStates), transitionMatrix(transitionMatrix), rowVariables(rowVariables), rowExpressionAdapter(rowExpressionAdapter), columnVariables(columnVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), labelToExpressionMap(labelToExpressionMap), rewardModels(rewardModels) {
: storm::models::Model<ValueType>(modelType), manager(manager), reachableStates(reachableStates), transitionMatrix(transitionMatrix), rowVariables(rowVariables), rowExpressionAdapter(rowExpressionAdapter), columnVariables(columnVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), labelToExpressionMap(labelToExpressionMap), rewardModels(rewardModels) {
this->labelToBddMap.emplace("init", initialStates);
this->labelToBddMap.emplace("deadlock", deadlockStates);
}
@ -52,7 +52,7 @@ namespace storm {
std::vector<std::pair<storm::expressions::Variable, storm::expressions::Variable>> const& rowColumnMetaVariablePairs,
std::map<std::string, storm::dd::Bdd<Type>> labelToBddMap,
std::unordered_map<std::string, RewardModelType> const& rewardModels)
: ModelBase(modelType), manager(manager), reachableStates(reachableStates), transitionMatrix(transitionMatrix), rowVariables(rowVariables), rowExpressionAdapter(nullptr), columnVariables(columnVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), labelToBddMap(labelToBddMap), rewardModels(rewardModels) {
: storm::models::Model<ValueType>(modelType), manager(manager), reachableStates(reachableStates), transitionMatrix(transitionMatrix), rowVariables(rowVariables), rowExpressionAdapter(nullptr), columnVariables(columnVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), labelToBddMap(labelToBddMap), rewardModels(rewardModels) {
STORM_LOG_THROW(this->labelToBddMap.find("init") == this->labelToBddMap.end(), storm::exceptions::WrongFormatException, "Illegal custom label 'init'.");
STORM_LOG_THROW(this->labelToBddMap.find("deadlock") == this->labelToBddMap.end(), storm::exceptions::WrongFormatException, "Illegal custom label 'deadlock'.");
this->labelToBddMap.emplace("init", initialStates);

4
src/storm/models/symbolic/Model.h

@ -10,7 +10,7 @@
#include "storm/storage/expressions/Variable.h"
#include "storm/storage/dd/DdType.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/models/ModelBase.h"
#include "storm/models/Model.h"
#include "storm/utility/OsDetection.h"
#include "storm-config.h"
@ -45,7 +45,7 @@ namespace storm {
* Base class for all symbolic models.
*/
template<storm::dd::DdType Type, typename CValueType = double>
class Model : public storm::models::ModelBase {
class Model : public storm::models::Model<CValueType> {
public:
typedef CValueType ValueType;

4
src/storm/settings/modules/BisimulationSettings.cpp

@ -22,7 +22,7 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, typeOptionName, true, "Sets the kind of bisimulation quotienting used.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the type to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(types)).setDefaultValueString("strong").build()).build());
std::vector<std::string> quotTypes = { "sparse", "dd" };
this->addOption(storm::settings::OptionBuilder(moduleName, typeOptionName, true, "Sets the format in which the quotient is extracted (only applies to DD-based bisimulation).").addArgument(storm::settings::ArgumentBuilder::createStringArgument("format", "The format of the quotient.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(quotTypes)).setDefaultValueString("dd").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, quotientFormatOptionName, true, "Sets the format in which the quotient is extracted (only applies to DD-based bisimulation).").addArgument(storm::settings::ArgumentBuilder::createStringArgument("format", "The format of the quotient.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(quotTypes)).setDefaultValueString("dd").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, representativeOptionName, false, "Sets whether to use representatives in the quotient rather than block numbers.").build());
}
@ -42,7 +42,7 @@ namespace storm {
}
BisimulationSettings::QuotientFormat BisimulationSettings::getQuotientFormat() const {
std::string quotientFormatAsString = this->getOption(typeOptionName).getArgumentByName("format").getValueAsString();
std::string quotientFormatAsString = this->getOption(quotientFormatOptionName).getArgumentByName("format").getValueAsString();
if (quotientFormatAsString == "sparse") {
return BisimulationSettings::QuotientFormat::Sparse;
}

6
src/storm/storage/SparseMatrix.cpp

@ -125,6 +125,11 @@ namespace storm {
// the insertion.
bool fixCurrentRow = row == lastRow && column < lastColumn;
// If the element is in the same row and column as the previous entry, we add them up.
if (row == lastRow && column == lastColumn) {
columnsAndValues.back().setColumn(column);
columnsAndValues.back().setValue(value);
} else {
// If we switched to another row, we have to adjust the missing entries in the row indices vector.
if (row != lastRow) {
// Otherwise, we need to push the correct values to the vectors, which might trigger reallocations.
@ -162,6 +167,7 @@ namespace storm {
columnsAndValues.resize(columnsAndValues.size() - elementsToRemove);
}
}
}
// In case we did not expect this value, we throw an exception.
if (forceInitialDimensions) {

4
src/storm/storage/dd/BisimulationDecomposition.cpp

@ -82,12 +82,12 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> BisimulationDecomposition<DdType, ValueType>::getQuotient() const {
std::shared_ptr<storm::models::Model<ValueType>> BisimulationDecomposition<DdType, ValueType>::getQuotient() const {
STORM_LOG_THROW(this->status == Status::FixedPoint, storm::exceptions::InvalidOperationException, "Cannot extract quotient, because bisimulation decomposition was not completed.");
STORM_LOG_TRACE("Starting quotient extraction.");
QuotientExtractor<DdType, ValueType> extractor;
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> quotient = extractor.extract(model, currentPartition);
std::shared_ptr<storm::models::Model<ValueType>> quotient = extractor.extract(model, currentPartition);
STORM_LOG_TRACE("Quotient extraction done.");
return quotient;

2
src/storm/storage/dd/BisimulationDecomposition.h

@ -33,7 +33,7 @@ namespace storm {
/*!
* Retrieves the quotient model after the bisimulation decomposition was computed.
*/
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> getQuotient() const;
std::shared_ptr<storm::models::Model<ValueType>> getQuotient() const;
private:
// The status of the computation.

22
src/storm/storage/dd/DdMetaVariable.cpp

@ -49,6 +49,28 @@ namespace storm {
return this->cube;
}
template<DdType LibraryType>
std::vector<uint64_t> DdMetaVariable<LibraryType>::getIndices() const {
std::vector<std::pair<uint64_t, uint64_t>> indicesAndLevels = this->getIndicesAndLevels();
std::sort(indicesAndLevels.begin(), indicesAndLevels.end(), [] (std::pair<uint64_t, uint64_t> const& a, std::pair<uint64_t, uint64_t> const& b) { return a.second < b.second; });
std::vector<uint64_t> indices;
for (auto const& e : indicesAndLevels) {
indices.emplace_back(e.first);
}
return indices;
}
template<DdType LibraryType>
std::vector<std::pair<uint64_t, uint64_t>> DdMetaVariable<LibraryType>::getIndicesAndLevels() const {
std::vector<std::pair<uint64_t, uint64_t>> indicesAndLevels;
for (auto const& v : ddVariables) {
indicesAndLevels.emplace_back(v.getIndex(), v.getLevel());
}
return indicesAndLevels;
}
template<DdType LibraryType>
uint64_t DdMetaVariable<LibraryType>::getHighestLevel() const {
uint64_t result = 0;

10
src/storm/storage/dd/DdMetaVariable.h

@ -78,6 +78,16 @@ namespace storm {
*/
uint64_t getHighestLevel() const;
/*!
* Retrieves the indices of the DD variables associated with this meta variable sorted by level.
*/
std::vector<uint64_t> getIndices() const;
/*!
* Retrieves the indices and levels of the DD variables associated with this meta variable.
*/
std::vector<std::pair<uint64_t, uint64_t>> getIndicesAndLevels() const;
private:
/*!
* Creates an integer meta variable with the given name and range bounds.

499
src/storm/storage/dd/bisimulation/QuotientExtractor.cpp

@ -6,6 +6,10 @@
#include "storm/models/symbolic/Ctmc.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/models/sparse/Dtmc.h"
#include "storm/models/sparse/Ctmc.h"
#include "storm/models/sparse/StandardRewardModel.h"
#include "storm/storage/dd/bisimulation/PreservationInformation.h"
#include "storm/settings/SettingsManager.h"
@ -13,10 +17,452 @@
#include "storm/utility/macros.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/storage/SparseMatrix.h"
#include "storm/storage/BitVector.h"
#include <sparsepp/spp.h>
namespace storm {
namespace dd {
namespace bisimulation {
template<storm::dd::DdType DdType, typename ValueType>
class InternalSparseQuotientExtractor;
template<storm::dd::DdType DdType, typename ValueType>
class InternalSparseQuotientExtractorBase {
public:
InternalSparseQuotientExtractorBase(storm::dd::DdManager<DdType> const& manager, std::set<storm::expressions::Variable> const& stateVariables) : manager(manager) {
for (auto const& variable : stateVariables) {
auto const& ddMetaVariable = manager.getMetaVariable(variable);
std::vector<std::pair<uint64_t, uint64_t>> indicesAndLevels = ddMetaVariable.getIndicesAndLevels();
stateVariablesIndicesAndLevels.insert(stateVariablesIndicesAndLevels.end(), indicesAndLevels.begin(), indicesAndLevels.end());
}
// Sort the indices by their levels.
std::sort(stateVariablesIndicesAndLevels.begin(), stateVariablesIndicesAndLevels.end(), [] (std::pair<uint64_t, uint64_t> const& a, std::pair<uint64_t, uint64_t> const& b) { return a.second < b.second; } );
}
protected:
storm::storage::SparseMatrix<ValueType> createMatrixFromEntries(Partition<DdType, ValueType> const& partition) {
for (auto& row : entries) {
std::sort(row.begin(), row.end(), [] (storm::storage::MatrixEntry<uint_fast64_t, ValueType> const& a, storm::storage::MatrixEntry<uint_fast64_t, ValueType> const& b) { return a.getColumn() < b.getColumn(); } );
}
storm::storage::SparseMatrixBuilder<ValueType> builder(partition.getNumberOfBlocks(), partition.getNumberOfBlocks());
uint64_t rowCounter = 0;
for (auto& row : entries) {
for (auto const& entry : row) {
builder.addNextValue(rowCounter, entry.getColumn(), entry.getValue());
}
// Free storage for row.
row.clear();
row.shrink_to_fit();
++rowCounter;
}
return builder.build();
}
storm::dd::DdManager<DdType> const& manager;
std::vector<std::pair<uint64_t, uint64_t>> stateVariablesIndicesAndLevels;
std::vector<std::vector<storm::storage::MatrixEntry<uint_fast64_t, ValueType>>> entries;
};
template<typename ValueType>
class InternalSparseQuotientExtractor<storm::dd::DdType::CUDD, ValueType> : public InternalSparseQuotientExtractorBase<storm::dd::DdType::CUDD, ValueType> {
public:
InternalSparseQuotientExtractor(storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager, std::set<storm::expressions::Variable> const& stateVariables) : InternalSparseQuotientExtractorBase<storm::dd::DdType::CUDD, ValueType>(manager, stateVariables), ddman(this->manager.getInternalDdManager().getCuddManager().getManager()) {
// Intentionally left empty.
}
storm::storage::SparseMatrix<ValueType> extractTransitionMatrix(storm::dd::Add<storm::dd::DdType::CUDD, ValueType> const& transitionMatrix, Partition<storm::dd::DdType::CUDD, ValueType> const& partition) {
STORM_LOG_ASSERT(partition.storedAsAdd(), "Expected partition stored as ADD.");
// Create the number of rows necessary for the matrix.
this->entries.resize(partition.getNumberOfBlocks());
storm::storage::BitVector encoding(this->stateVariablesIndicesAndLevels.size());
extractTransitionMatrixRec(transitionMatrix.getInternalAdd().getCuddDdNode(), partition.asAdd().getInternalAdd().getCuddDdNode(), partition.asAdd().getInternalAdd().getCuddDdNode(), 0, encoding);
return this->createMatrixFromEntries(partition);
}
storm::storage::BitVector extractStates(storm::dd::Bdd<storm::dd::DdType::CUDD> const& states, Partition<storm::dd::DdType::CUDD, ValueType> const& partition) {
STORM_LOG_ASSERT(partition.storedAsAdd(), "Expected partition stored as ADD.");
storm::storage::BitVector result(partition.getNumberOfBlocks());
extractStatesRec(states.getInternalBdd().getCuddDdNode(), partition.asAdd().getInternalAdd().getCuddDdNode(), 0, result);
return result;
}
private:
uint64_t decodeBlockIndex(DdNode* blockEncoding) {
std::unique_ptr<uint64_t>& blockCacheEntry = blockDecodeCache[blockEncoding];
if (blockCacheEntry) {
return *blockCacheEntry;
}
uint64_t result = 0;
uint64_t offset = 0;
while (blockEncoding != Cudd_ReadOne(ddman)) {
if (Cudd_T(blockEncoding) != Cudd_ReadZero(ddman)) {
blockEncoding = Cudd_T(blockEncoding);
result |= 1ull << offset;
} else {
blockEncoding = Cudd_E(blockEncoding);
}
++offset;
}
blockCacheEntry.reset(new uint64_t(result));
return result;
}
void extractStatesRec(DdNode* statesNode, DdNode* partitionNode, uint64_t offset, storm::storage::BitVector& result) {
if (statesNode == Cudd_ReadLogicZero(ddman)) {
return;
}
// Determine the levels in the DDs.
uint64_t statesVariable = Cudd_NodeReadIndex(statesNode);
uint64_t partitionVariable = Cudd_NodeReadIndex(partitionNode) - 1;
// See how many variables we skipped.
while (offset < this->stateVariablesIndicesAndLevels.size() && statesVariable != this->stateVariablesIndicesAndLevels[offset].first && partitionVariable != this->stateVariablesIndicesAndLevels[offset].first) {
++offset;
}
if (offset == this->stateVariablesIndicesAndLevels.size()) {
result.set(decodeBlockIndex(partitionNode));
return;
}
uint64_t topVariable;
if (statesVariable == this->stateVariablesIndicesAndLevels[offset].first) {
topVariable = statesVariable;
} else {
topVariable = partitionVariable;
}
DdNode* tStates = statesNode;
DdNode* eStates = statesNode;
bool negate = false;
if (topVariable == statesVariable) {
tStates = Cudd_T(statesNode);
eStates = Cudd_E(statesNode);
negate = Cudd_IsComplement(statesNode);
}
DdNode* tPartition = partitionNode;
DdNode* ePartition = partitionNode;
if (topVariable == partitionVariable) {
tPartition = Cudd_T(partitionNode);
ePartition = Cudd_E(partitionNode);
}
extractStatesRec(negate ? Cudd_Not(tStates) : tStates, tPartition, offset, result);
extractStatesRec(negate ? Cudd_Not(eStates) : eStates, ePartition, offset, result);
}
void extractTransitionMatrixRec(DdNode* transitionMatrixNode, DdNode* sourcePartitionNode, DdNode* targetPartitionNode, uint64_t currentIndex, storm::storage::BitVector& sourceState) {
// For the empty DD, we do not need to add any entries. Note that the partition nodes cannot be zero
// as all states of the model have to be contained.
if (transitionMatrixNode == Cudd_ReadZero(ddman)) {
return;
}
// If we have moved through all source variables, we must have arrived at a constant.
if (currentIndex == sourceState.size()) {
// Decode the source block.
uint64_t sourceBlockIndex = decodeBlockIndex(sourcePartitionNode);
std::unique_ptr<storm::storage::BitVector>& sourceRepresentative = uniqueSourceRepresentative[sourceBlockIndex];
if (sourceRepresentative && *sourceRepresentative != sourceState) {
// In this case, we have picked a different representative and must not record any entries now.
return;
}
// Otherwise, we record the new representative.
sourceRepresentative.reset(new storm::storage::BitVector(sourceState));
// Decode the target block.
uint64_t targetBlockIndex = decodeBlockIndex(targetPartitionNode);
this->entries[sourceBlockIndex].emplace_back(targetBlockIndex, Cudd_V(transitionMatrixNode));
} else {
// Determine the levels in the DDs.
uint64_t transitionMatrixVariable = Cudd_NodeReadIndex(transitionMatrixNode);
uint64_t sourcePartitionVariable = Cudd_NodeReadIndex(sourcePartitionNode) - 1;
uint64_t targetPartitionVariable = Cudd_NodeReadIndex(targetPartitionNode) - 1;
// Move through transition matrix.
DdNode* tt = transitionMatrixNode;
DdNode* te = transitionMatrixNode;
DdNode* et = transitionMatrixNode;
DdNode* ee = transitionMatrixNode;
if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) {
DdNode* t = Cudd_T(transitionMatrixNode);
DdNode* e = Cudd_E(transitionMatrixNode);
uint64_t tVariable = Cudd_NodeReadIndex(t);
if (tVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) {
tt = Cudd_T(t);
te = Cudd_E(t);
} else {
tt = te = t;
}
uint64_t eVariable = Cudd_NodeReadIndex(e);
if (eVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) {
et = Cudd_T(e);
ee = Cudd_E(e);
} else {
et = ee = e;
}
} else {
if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) {
tt = Cudd_T(transitionMatrixNode);
te = Cudd_E(transitionMatrixNode);
} else {
tt = te = transitionMatrixNode;
}
}
// Move through partition (for source state).
DdNode* sourceT;
DdNode* sourceE;
if (sourcePartitionVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) {
sourceT = Cudd_T(sourcePartitionNode);
sourceE = Cudd_E(sourcePartitionNode);
} else {
sourceT = sourceE = sourcePartitionNode;
}
// Move through partition (for target state).
DdNode* targetT;
DdNode* targetE;
if (targetPartitionVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) {
targetT = Cudd_T(targetPartitionNode);
targetE = Cudd_E(targetPartitionNode);
} else {
targetT = targetE = targetPartitionNode;
}
sourceState.set(currentIndex, true);
extractTransitionMatrixRec(tt, sourceT, targetT, currentIndex + 1, sourceState);
extractTransitionMatrixRec(te, sourceT, targetE, currentIndex + 1, sourceState);
sourceState.set(currentIndex, false);
extractTransitionMatrixRec(et, sourceE, targetT, currentIndex + 1, sourceState);
extractTransitionMatrixRec(ee, sourceE, targetE, currentIndex + 1, sourceState);
}
}
::DdManager* ddman;
spp::sparse_hash_map<uint64_t, std::unique_ptr<storm::storage::BitVector>> uniqueSourceRepresentative;
spp::sparse_hash_map<DdNode const*, std::unique_ptr<uint64_t>> blockDecodeCache;
};
template<typename ValueType>
class InternalSparseQuotientExtractor<storm::dd::DdType::Sylvan, ValueType> : public InternalSparseQuotientExtractorBase<storm::dd::DdType::Sylvan, ValueType> {
public:
InternalSparseQuotientExtractor(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, std::set<storm::expressions::Variable> const& stateVariables) : InternalSparseQuotientExtractorBase<storm::dd::DdType::Sylvan, ValueType>(manager, stateVariables) {
// Intentionally left empty.
}
storm::storage::SparseMatrix<ValueType> extractTransitionMatrix(storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> const& transitionMatrix, Partition<storm::dd::DdType::Sylvan, ValueType> const& partition) {
STORM_LOG_ASSERT(partition.storedAsBdd(), "Expected partition stored as BDD.");
// Create the number of rows necessary for the matrix.
this->entries.resize(partition.getNumberOfBlocks());
storm::storage::BitVector encoding(this->stateVariablesIndicesAndLevels.size());
extractTransitionMatrixRec(transitionMatrix.getInternalAdd().getSylvanMtbdd().GetMTBDD(), partition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), partition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), 0, encoding);
return this->createMatrixFromEntries(partition);
}
storm::storage::BitVector extractStates(storm::dd::Bdd<storm::dd::DdType::Sylvan> const& states, Partition<storm::dd::DdType::Sylvan, ValueType> const& partition) {
STORM_LOG_ASSERT(partition.storedAsBdd(), "Expected partition stored as BDD.");
storm::storage::BitVector result(partition.getNumberOfBlocks());
extractStatesRec(states.getInternalBdd().getSylvanBdd().GetBDD(), partition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), 0, result);
return result;
}
private:
uint64_t decodeBlockIndex(BDD blockEncoding) {
std::unique_ptr<uint64_t>& blockCacheEntry = blockDecodeCache[blockEncoding];
if (blockCacheEntry) {
return *blockCacheEntry;
}
uint64_t result = 0;
uint64_t offset = 0;
while (blockEncoding != sylvan_true) {
if (sylvan_high(blockEncoding) != sylvan_false) {
blockEncoding = sylvan_high(blockEncoding);
result |= 1ull << offset;
} else {
blockEncoding = sylvan_low(blockEncoding);
}
++offset;
}
blockCacheEntry.reset(new uint64_t(result));
return result;
}
void extractStatesRec(BDD statesNode, BDD partitionNode, uint64_t offset, storm::storage::BitVector& result) {
if (statesNode == sylvan_false) {
return;
}
// Determine the levels in the DDs.
uint64_t statesVariable = sylvan_isconst(statesNode) ? 0xffffffff : sylvan_var(statesNode);
uint64_t partitionVariable = sylvan_var(partitionNode) - 1;
// See how many variables we skipped.
while (offset < this->stateVariablesIndicesAndLevels.size() && statesVariable != this->stateVariablesIndicesAndLevels[offset].first && partitionVariable != this->stateVariablesIndicesAndLevels[offset].first) {
++offset;
}
if (offset == this->stateVariablesIndicesAndLevels.size()) {
result.set(decodeBlockIndex(partitionNode));
return;
}
uint64_t topVariable;
if (statesVariable == this->stateVariablesIndicesAndLevels[offset].first) {
topVariable = statesVariable;
} else {
topVariable = partitionVariable;
}
BDD tStates = statesNode;
BDD eStates = statesNode;
if (topVariable == statesVariable) {
tStates = sylvan_high(statesNode);
eStates = sylvan_low(statesNode);
}
BDD tPartition = partitionNode;
BDD ePartition = partitionNode;
if (topVariable == partitionVariable) {
tPartition = sylvan_high(partitionNode);
ePartition = sylvan_low(partitionNode);
}
extractStatesRec(tStates, tPartition, offset, result);
extractStatesRec(eStates, ePartition, offset, result);
}
void extractTransitionMatrixRec(MTBDD transitionMatrixNode, BDD sourcePartitionNode, BDD targetPartitionNode, uint64_t currentIndex, storm::storage::BitVector& sourceState) {
// For the empty DD, we do not need to add any entries. Note that the partition nodes cannot be zero
// as all states of the model have to be contained.
if (mtbdd_iszero(transitionMatrixNode)) {
return;
}
// If we have moved through all source variables, we must have arrived at a constant.
if (currentIndex == sourceState.size()) {
// Decode the source block.
uint64_t sourceBlockIndex = decodeBlockIndex(sourcePartitionNode);
std::unique_ptr<storm::storage::BitVector>& sourceRepresentative = uniqueSourceRepresentative[sourceBlockIndex];
if (sourceRepresentative && *sourceRepresentative != sourceState) {
// In this case, we have picked a different representative and must not record any entries now.
return;
}
// Otherwise, we record the new representative.
sourceRepresentative.reset(new storm::storage::BitVector(sourceState));
// Decode the target block.
uint64_t targetBlockIndex = decodeBlockIndex(targetPartitionNode);
this->entries[sourceBlockIndex].emplace_back(targetBlockIndex, storm::dd::InternalAdd<storm::dd::DdType::Sylvan, ValueType>::getValue(transitionMatrixNode));
} else {
// Determine the levels in the DDs.
uint64_t transitionMatrixVariable = sylvan_isconst(transitionMatrixNode) ? 0xffffffff : sylvan_var(transitionMatrixNode);
uint64_t sourcePartitionVariable = sylvan_var(sourcePartitionNode) - 1;
uint64_t targetPartitionVariable = sylvan_var(targetPartitionNode) - 1;
// Move through transition matrix.
MTBDD tt = transitionMatrixNode;
MTBDD te = transitionMatrixNode;
MTBDD et = transitionMatrixNode;
MTBDD ee = transitionMatrixNode;
if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) {
MTBDD t = sylvan_high(transitionMatrixNode);
MTBDD e = sylvan_low(transitionMatrixNode);
uint64_t tVariable = sylvan_isconst(t) ? 0xffffffff : sylvan_var(t);
if (tVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) {
tt = sylvan_high(t);
te = sylvan_low(t);
} else {
tt = te = t;
}
uint64_t eVariable = sylvan_isconst(e) ? 0xffffffff : sylvan_var(e);
if (eVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) {
et = sylvan_high(e);
ee = sylvan_low(e);
} else {
et = ee = e;
}
} else {
if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) {
tt = sylvan_high(transitionMatrixNode);
te = sylvan_low(transitionMatrixNode);
} else {
tt = te = transitionMatrixNode;
}
}
// Move through partition (for source state).
MTBDD sourceT;
MTBDD sourceE;
if (sourcePartitionVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) {
sourceT = sylvan_high(sourcePartitionNode);
sourceE = sylvan_low(sourcePartitionNode);
} else {
sourceT = sourceE = sourcePartitionNode;
}
// Move through partition (for target state).
MTBDD targetT;
MTBDD targetE;
if (targetPartitionVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) {
targetT = sylvan_high(targetPartitionNode);
targetE = sylvan_low(targetPartitionNode);
} else {
targetT = targetE = targetPartitionNode;
}
sourceState.set(currentIndex, true);
extractTransitionMatrixRec(tt, sourceT, targetT, currentIndex + 1, sourceState);
extractTransitionMatrixRec(te, sourceT, targetE, currentIndex + 1, sourceState);
sourceState.set(currentIndex, false);
extractTransitionMatrixRec(et, sourceE, targetT, currentIndex + 1, sourceState);
extractTransitionMatrixRec(ee, sourceE, targetE, currentIndex + 1, sourceState);
}
}
spp::sparse_hash_map<uint64_t, std::unique_ptr<storm::storage::BitVector>> uniqueSourceRepresentative;
spp::sparse_hash_map<BDD, std::unique_ptr<uint64_t>> blockDecodeCache;
};
template<storm::dd::DdType DdType, typename ValueType>
QuotientExtractor<DdType, ValueType>::QuotientExtractor() : useRepresentatives(false) {
auto const& settings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
@ -25,9 +471,9 @@ namespace storm {
}
template<storm::dd::DdType DdType, typename ValueType>
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> QuotientExtractor<DdType, ValueType>::extract(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition) {
std::shared_ptr<storm::models::Model<ValueType>> QuotientExtractor<DdType, ValueType>::extract(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition) {
auto start = std::chrono::high_resolution_clock::now();
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> result;
std::shared_ptr<storm::models::Model<ValueType>> result;
if (quotientFormat == storm::settings::modules::BisimulationSettings::QuotientFormat::Sparse) {
result = extractSparseQuotient(model, partition);
} else {
@ -35,12 +481,51 @@ namespace storm {
}
auto end = std::chrono::high_resolution_clock::now();
STORM_LOG_TRACE("Quotient extraction completed in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
STORM_LOG_THROW(result, storm::exceptions::NotSupportedException, "Quotient could not be extracted.");
return result;
}
template<storm::dd::DdType DdType, typename ValueType>
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> QuotientExtractor<DdType, ValueType>::extractSparseQuotient(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition) {
return nullptr;
std::shared_ptr<storm::models::sparse::Model<ValueType>> QuotientExtractor<DdType, ValueType>::extractSparseQuotient(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition) {
InternalSparseQuotientExtractor<DdType, ValueType> sparseExtractor(model.getManager(), model.getRowVariables());
storm::storage::SparseMatrix<ValueType> quotientTransitionMatrix = sparseExtractor.extractTransitionMatrix(model.getTransitionMatrix(), partition);
std::cout << "Matrix has " << quotientTransitionMatrix.getEntryCount() << " entries" << std::endl;
storm::models::sparse::StateLabeling quotientStateLabeling(partition.getNumberOfBlocks());
quotientStateLabeling.addLabel("init", sparseExtractor.extractStates(model.getInitialStates(), partition));
std::cout << "init: " << quotientStateLabeling.getStates("init").getNumberOfSetBits() << std::endl;
quotientStateLabeling.addLabel("deadlock", sparseExtractor.extractStates(model.getDeadlockStates(), partition));
std::cout << "deadlock: " << quotientStateLabeling.getStates("init").getNumberOfSetBits() << std::endl;
for (auto const& label : partition.getPreservationInformation().getLabels()) {
quotientStateLabeling.addLabel(label, sparseExtractor.extractStates(model.getStates(label), partition));
std::cout << label << ": " << quotientStateLabeling.getStates(label).getNumberOfSetBits() << std::endl;
}
for (auto const& expression : partition.getPreservationInformation().getExpressions()) {
std::stringstream stream;
stream << expression;
std::string expressionAsString = stream.str();
if (quotientStateLabeling.containsLabel(expressionAsString)) {
STORM_LOG_WARN("Duplicate label '" << expressionAsString << "', dropping second label definition.");
} else {
quotientStateLabeling.addLabel(stream.str(), sparseExtractor.extractStates(model.getStates(expression), partition));
std::cout << stream.str() << ": " << quotientStateLabeling.getStates(stream.str()).getNumberOfSetBits() << std::endl;
}
}
std::shared_ptr<storm::models::sparse::Model<ValueType>> result;
if (model.getType() == storm::models::ModelType::Dtmc) {
result = std::make_shared<storm::models::sparse::Dtmc<ValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling));
} else if (model.getType() == storm::models::ModelType::Ctmc) {
result = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(quotientTransitionMatrix), std::move(quotientStateLabeling));
}
return result;
}
template<storm::dd::DdType DdType, typename ValueType>
@ -65,10 +550,14 @@ namespace storm {
}
storm::dd::Add<DdType, ValueType> partitionAsAdd = partitionAsBdd.template toAdd<ValueType>();
auto start = std::chrono::high_resolution_clock::now();
storm::dd::Add<DdType, ValueType> quotientTransitionMatrix = model.getTransitionMatrix().multiplyMatrix(partitionAsAdd.renameVariables(blockVariableSet, blockPrimeVariableSet), model.getColumnVariables());
quotientTransitionMatrix = quotientTransitionMatrix.multiplyMatrix(partitionAsAdd.renameVariables(model.getColumnVariables(), model.getRowVariables()), model.getRowVariables());
auto end = std::chrono::high_resolution_clock::now();
STORM_LOG_TRACE("Quotient transition matrix extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
storm::dd::Bdd<DdType> quotientTransitionMatrixBdd = quotientTransitionMatrix.notZero();
start = std::chrono::high_resolution_clock::now();
storm::dd::Bdd<DdType> partitionAsBddOverRowVariables = partitionAsBdd.renameVariables(model.getColumnVariables(), model.getRowVariables());
storm::dd::Bdd<DdType> reachableStates = partitionAsBdd.existsAbstract(model.getColumnVariables());
storm::dd::Bdd<DdType> initialStates = (model.getInitialStates() && partitionAsBddOverRowVariables).existsAbstract(model.getRowVariables());
@ -90,6 +579,8 @@ namespace storm {
preservedLabelBdds.emplace(stream.str(), (model.getStates(expression) && partitionAsBddOverRowVariables).existsAbstract(model.getRowVariables()));
}
}
end = std::chrono::high_resolution_clock::now();
STORM_LOG_TRACE("Quotient labels extracted in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
if (modelType == storm::models::ModelType::Dtmc) {
return std::shared_ptr<storm::models::symbolic::Dtmc<DdType, ValueType>>(new storm::models::symbolic::Dtmc<DdType, ValueType>(model.getManager().asSharedPointer(), reachableStates, initialStates, deadlockStates, quotientTransitionMatrix, blockVariableSet, blockPrimeVariableSet, blockMetaVariablePairs, preservedLabelBdds, {}));

5
src/storm/storage/dd/bisimulation/QuotientExtractor.h

@ -5,6 +5,7 @@
#include "storm/storage/dd/DdType.h"
#include "storm/models/symbolic/Model.h"
#include "storm/models/sparse/Model.h"
#include "storm/storage/dd/bisimulation/Partition.h"
@ -19,10 +20,10 @@ namespace storm {
public:
QuotientExtractor();
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> extract(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition);
std::shared_ptr<storm::models::Model<ValueType>> extract(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition);
private:
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> extractSparseQuotient(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition);
std::shared_ptr<storm::models::sparse::Model<ValueType>> extractSparseQuotient(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition);
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> extractDdQuotient(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition);
std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> extractQuotientUsingBlockVariables(storm::models::symbolic::Model<DdType, ValueType> const& model, Partition<DdType, ValueType> const& partition);

2
src/storm/storage/dd/cudd/InternalCuddAdd.h

@ -595,7 +595,6 @@ namespace storm {
InternalDdManager<DdType::CUDD> const& getInternalDdManager() const;
private:
/*!
* Retrieves the CUDD ADD object associated with this ADD.
*
@ -610,6 +609,7 @@ namespace storm {
*/
DdNode* getCuddDdNode() const;
private:
/*!
* Performs a recursive step to perform the given function between the given DD-based vector and the given
* explicit vector.

2
src/storm/storage/dd/cudd/InternalCuddBdd.h

@ -387,7 +387,6 @@ namespace storm {
friend struct std::hash<storm::dd::InternalBdd<storm::dd::DdType::CUDD>>;
private:
/*!
* Retrieves the CUDD BDD object associated with this DD.
*
@ -402,6 +401,7 @@ namespace storm {
*/
DdNode* getCuddDdNode() const;
private:
/*!
* Builds a BDD representing the values that make the given filter function evaluate to true.
*

14
src/storm/storage/dd/sylvan/InternalSylvanAdd.h

@ -605,6 +605,13 @@ namespace storm {
*/
sylvan::Mtbdd getSylvanMtbdd() const;
/*!
* Retrieves the value of the given node (that must be a leaf).
*
* @return The value of the leaf.
*/
static ValueType getValue(MTBDD const& node);
private:
/*!
* Recursively builds the ODD from an ADD.
@ -733,13 +740,6 @@ namespace storm {
static MTBDD getLeaf(storm::RationalFunction const& value);
#endif
/*!
* Retrieves the value of the given node (that must be a leaf).
*
* @return The value of the leaf.
*/
static ValueType getValue(MTBDD const& node);
// The manager responsible for this MTBDD.
InternalDdManager<DdType::Sylvan> const* ddManager;

1
src/storm/transformer/SymbolicToSparseTransformer.cpp

@ -29,6 +29,7 @@ namespace storm {
template<storm::dd::DdType Type, typename ValueType>
std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> SymbolicDtmcToSparseDtmcTransformer<Type, ValueType>::translate(storm::models::symbolic::Dtmc<Type, ValueType> const& symbolicDtmc) {
this->odd = symbolicDtmc.getReachableStates().createOdd();
storm::storage::SparseMatrix<ValueType> transitionMatrix = symbolicDtmc.getTransitionMatrix().toMatrix(this->odd, this->odd);
std::unordered_map<std::string, storm::models::sparse::StandardRewardModel<ValueType>> rewardModels;

2
src/storm/utility/storm.h

@ -194,7 +194,7 @@ namespace storm {
if (storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet()) {
storm::dd::BisimulationDecomposition<LibraryType, ValueType> decomposition(*result, formulas);
decomposition.compute();
result = decomposition.getQuotient();
result = std::dynamic_pointer_cast<storm::models::symbolic::Model<LibraryType, ValueType>>(decomposition.getQuotient());
}
return result;

Loading…
Cancel
Save