From b25ef3f09c7d98116926e39a9b76830530db6334 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 31 Jul 2017 10:19:06 +0200 Subject: [PATCH] introduced symbolic bisimulation modes lazy and eager, fixed bug in sparse quotient extraction --- src/storm/api/bisimulation.h | 10 ++-- src/storm/cli/cli.cpp | 2 +- src/storm/models/symbolic/Model.cpp | 2 +- src/storm/models/symbolic/Model.h | 3 +- .../models/symbolic/NondeterministicModel.cpp | 9 ++++ .../models/symbolic/NondeterministicModel.h | 9 ++++ .../settings/modules/BisimulationSettings.cpp | 14 ++++++ .../settings/modules/BisimulationSettings.h | 8 +++ .../bisimulation/BisimulationDecomposition.h | 18 +++---- .../storage/bisimulation/BisimulationType.h | 10 ++++ .../storage/dd/BisimulationDecomposition.cpp | 50 +++++++++++-------- .../storage/dd/BisimulationDecomposition.h | 9 ++-- src/storm/storage/dd/DdMetaVariable.cpp | 1 + .../storage/dd/bisimulation/Partition.cpp | 41 +++++++++++---- src/storm/storage/dd/bisimulation/Partition.h | 14 ++++-- .../dd/bisimulation/QuotientExtractor.cpp | 27 +++++++--- .../dd/bisimulation/SignatureComputer.cpp | 34 ++++++++++++- .../dd/bisimulation/SignatureComputer.h | 18 ++++++- .../storage/dd/bisimulation/SignatureMode.h | 11 ++++ 19 files changed, 222 insertions(+), 68 deletions(-) create mode 100644 src/storm/storage/bisimulation/BisimulationType.h create mode 100644 src/storm/storage/dd/bisimulation/SignatureMode.h diff --git a/src/storm/api/bisimulation.h b/src/storm/api/bisimulation.h index ac8797fc8..c59c62b09 100644 --- a/src/storm/api/bisimulation.h +++ b/src/storm/api/bisimulation.h @@ -55,16 +55,18 @@ namespace storm { } template <storm::dd::DdType DdType, typename ValueType> - typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { + typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager) { + STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Dtmc) || model->isOfType(storm::models::ModelType::Ctmc), storm::exceptions::NotSupportedException, "Symbolic bisimulation minimization is currently only available for DTMCs and CTMCs."); + STORM_LOG_THROW(bisimulationType == storm::storage::BisimulationType::Strong, storm::exceptions::NotSupportedException, "Currently only strong bisimulation is supported."); - storm::dd::BisimulationDecomposition<DdType, ValueType> decomposition(*model, formulas); - decomposition.compute(); + storm::dd::BisimulationDecomposition<DdType, ValueType> decomposition(*model, formulas, bisimulationType); + decomposition.compute(mode); return decomposition.getQuotient(); } template <storm::dd::DdType DdType, typename ValueType> - typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { + typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Symbolic bisimulation minimization is not supported for this combination of DD library and value type."); return nullptr; } diff --git a/src/storm/cli/cli.cpp b/src/storm/cli/cli.cpp index 7a1ef8066..5bab54a87 100644 --- a/src/storm/cli/cli.cpp +++ b/src/storm/cli/cli.cpp @@ -485,7 +485,7 @@ namespace storm { STORM_LOG_WARN_COND(!bisimulationSettings.isWeakBisimulationSet(), "Weak bisimulation is currently not supported on DDs. Falling back to strong bisimulation."); STORM_LOG_INFO("Performing bisimulation minimization..."); - return storm::api::performBisimulationMinimization<DdType, ValueType>(model, createFormulasToRespect(input.properties)); + return storm::api::performBisimulationMinimization<DdType, ValueType>(model, createFormulasToRespect(input.properties), storm::storage::BisimulationType::Strong, bisimulationSettings.getSignatureMode()); } template <storm::dd::DdType DdType, typename ValueType> diff --git a/src/storm/models/symbolic/Model.cpp b/src/storm/models/symbolic/Model.cpp index 2a6804618..27a37534f 100644 --- a/src/storm/models/symbolic/Model.cpp +++ b/src/storm/models/symbolic/Model.cpp @@ -162,7 +162,7 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - storm::dd::Bdd<Type> Model<Type, ValueType>::getQualitativeTransitionMatrix() const { + storm::dd::Bdd<Type> Model<Type, ValueType>::getQualitativeTransitionMatrix(bool) const { return this->getTransitionMatrix().notZero(); } diff --git a/src/storm/models/symbolic/Model.h b/src/storm/models/symbolic/Model.h index c9a628ff4..dbd74634c 100644 --- a/src/storm/models/symbolic/Model.h +++ b/src/storm/models/symbolic/Model.h @@ -204,9 +204,10 @@ namespace storm { * Retrieves the matrix qualitatively (i.e. without probabilities) representing the transitions of the * model. * + * @param keepNondeterminism If false, the matrix will abstract from the nondeterminism variables. * @return A matrix representing the qualitative transitions of the model. */ - storm::dd::Bdd<Type> getQualitativeTransitionMatrix() const; + virtual storm::dd::Bdd<Type> getQualitativeTransitionMatrix(bool keepNondeterminism = true) const; /*! * Retrieves the meta variables used to encode the rows of the transition matrix and the vector indices. diff --git a/src/storm/models/symbolic/NondeterministicModel.cpp b/src/storm/models/symbolic/NondeterministicModel.cpp index 71a334a1a..6545e27c0 100644 --- a/src/storm/models/symbolic/NondeterministicModel.cpp +++ b/src/storm/models/symbolic/NondeterministicModel.cpp @@ -96,6 +96,15 @@ namespace storm { illegalMask = !(this->getTransitionMatrix().notZero().existsAbstract(this->getColumnVariables())) && this->getReachableStates(); } + template<storm::dd::DdType Type, typename ValueType> + storm::dd::Bdd<Type> NondeterministicModel<Type, ValueType>::getQualitativeTransitionMatrix(bool keepNondeterminism) const { + if (!keepNondeterminism) { + return this->getTransitionMatrix().notZero().existsAbstract(this->getNondeterminismVariables()); + } else { + return Model<Type, ValueType>::getQualitativeTransitionMatrix(keepNondeterminism); + } + } + // Explicitly instantiate the template class. template class NondeterministicModel<storm::dd::DdType::CUDD, double>; template class NondeterministicModel<storm::dd::DdType::Sylvan, double>; diff --git a/src/storm/models/symbolic/NondeterministicModel.h b/src/storm/models/symbolic/NondeterministicModel.h index e97347c44..45dc27b0a 100644 --- a/src/storm/models/symbolic/NondeterministicModel.h +++ b/src/storm/models/symbolic/NondeterministicModel.h @@ -113,6 +113,15 @@ namespace storm { */ storm::dd::Bdd<Type> getIllegalSuccessorMask() const; + /*! + * Retrieves the matrix qualitatively (i.e. without probabilities) representing the transitions of the + * model. + * + * @param keepNondeterminism If false, the matrix will abstract from the nondeterminism variables. + * @return A matrix representing the qualitative transitions of the model. + */ + virtual storm::dd::Bdd<Type> getQualitativeTransitionMatrix(bool keepNondeterminism = true) const override; + virtual void printModelInformationToStream(std::ostream& out) const override; protected: diff --git a/src/storm/settings/modules/BisimulationSettings.cpp b/src/storm/settings/modules/BisimulationSettings.cpp index 2c1db8a39..679daa54d 100644 --- a/src/storm/settings/modules/BisimulationSettings.cpp +++ b/src/storm/settings/modules/BisimulationSettings.cpp @@ -16,6 +16,7 @@ namespace storm { const std::string BisimulationSettings::typeOptionName = "type"; const std::string BisimulationSettings::representativeOptionName = "repr"; const std::string BisimulationSettings::quotientFormatOptionName = "quot"; + const std::string BisimulationSettings::signatureModeOptionName = "sigmode"; BisimulationSettings::BisimulationSettings() : ModuleSettings(moduleName) { std::vector<std::string> types = { "strong", "weak" }; @@ -25,6 +26,9 @@ namespace storm { 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()); + + std::vector<std::string> signatureModes = { "eager", "lazy" }; + this->addOption(storm::settings::OptionBuilder(moduleName, signatureModeOptionName, false, "Sets the signature computation mode.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("mode", "The mode to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(signatureModes)).setDefaultValueString("eager").build()).build()); } bool BisimulationSettings::isStrongBisimulationSet() const { @@ -53,6 +57,16 @@ namespace storm { return this->getOption(representativeOptionName).getHasOptionBeenSet(); } + storm::dd::bisimulation::SignatureMode BisimulationSettings::getSignatureMode() const { + std::string modeAsString = this->getOption(signatureModeOptionName).getArgumentByName("mode").getValueAsString(); + if (modeAsString == "eager") { + return storm::dd::bisimulation::SignatureMode::Eager; + } else if (modeAsString == "lazy") { + return storm::dd::bisimulation::SignatureMode::Lazy; + } + STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unknown signature mode '" << modeAsString << "."); + } + bool BisimulationSettings::check() const { bool optionsSet = this->getOption(typeOptionName).getHasOptionBeenSet(); STORM_LOG_WARN_COND(storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet() || !optionsSet, "Bisimulation minimization is not selected, so setting options for bisimulation has no effect."); diff --git a/src/storm/settings/modules/BisimulationSettings.h b/src/storm/settings/modules/BisimulationSettings.h index ccdea9e5d..0d71baa86 100644 --- a/src/storm/settings/modules/BisimulationSettings.h +++ b/src/storm/settings/modules/BisimulationSettings.h @@ -3,6 +3,8 @@ #include "storm/settings/modules/ModuleSettings.h" +#include "storm/storage/dd/bisimulation/SignatureMode.h" + namespace storm { namespace settings { namespace modules { @@ -48,6 +50,11 @@ namespace storm { */ bool isUseRepresentativesSet() const; + /*! + * Retrieves the mode to compute signatures. + */ + storm::dd::bisimulation::SignatureMode getSignatureMode() const; + virtual bool check() const override; // The name of the module. @@ -58,6 +65,7 @@ namespace storm { static const std::string typeOptionName; static const std::string representativeOptionName; static const std::string quotientFormatOptionName; + static const std::string signatureModeOptionName; }; } // namespace modules } // namespace settings diff --git a/src/storm/storage/bisimulation/BisimulationDecomposition.h b/src/storm/storage/bisimulation/BisimulationDecomposition.h index 49089063a..6aa56345c 100644 --- a/src/storm/storage/bisimulation/BisimulationDecomposition.h +++ b/src/storm/storage/bisimulation/BisimulationDecomposition.h @@ -7,6 +7,7 @@ #include "storm/storage/Decomposition.h" #include "storm/storage/StateBlock.h" #include "storm/storage/bisimulation/Partition.h" +#include "storm/storage/bisimulation/BisimulationType.h" #include "storm/solver/OptimizationDirection.h" #include "storm/logic/Formulas.h" @@ -18,15 +19,12 @@ namespace storm { namespace utility { template <typename ValueType> class ConstantsComparator; } - + namespace logic { class Formula; } namespace storage { - - enum class BisimulationType { Strong, Weak }; - enum class BisimulationTypeChoice { Strong, Weak, FromSettings }; inline BisimulationType resolveBisimulationTypeChoice(BisimulationTypeChoice c) { switch(c) { @@ -40,8 +38,8 @@ namespace storm { } else { return BisimulationType::Strong; } - } + return BisimulationType::Strong; } /*! @@ -89,7 +87,7 @@ namespace storm { /** * Sets the bisimulation type. If the bisimulation type is set to weak, - * we also change the bounded flag (as bounded properties are not preserved under + * we also change the bounded flag (as bounded properties are not preserved under * weak bisimulation). */ void setType(BisimulationType t) { @@ -136,7 +134,7 @@ namespace storm { private: boost::optional<OptimizationDirection> optimalityType; - + /// A flag that indicates whether or not the state-rewards of the model are to be respected (and should /// be kept in the quotient model, if one is built). bool keepRewards; @@ -155,7 +153,7 @@ namespace storm { * @param formula The only formula to check. */ void preserveSingleFormula(ModelType const& model, storm::logic::Formula const& formula); - + /*! * Adds the given expressions and labels to the set of respected atomic propositions. * @@ -189,7 +187,7 @@ namespace storm { * @return The quotient model. */ std::shared_ptr<ModelType> getQuotient() const; - + /*! * Computes the decomposition of the model into bisimulation equivalence classes. If requested, a quotient * model is built. @@ -263,7 +261,7 @@ namespace storm { // The model to decompose. ModelType const& model; - + // The backward transitions of the model. storm::storage::SparseMatrix<ValueType> backwardTransitions; diff --git a/src/storm/storage/bisimulation/BisimulationType.h b/src/storm/storage/bisimulation/BisimulationType.h new file mode 100644 index 000000000..fc9dba9b2 --- /dev/null +++ b/src/storm/storage/bisimulation/BisimulationType.h @@ -0,0 +1,10 @@ +#pragma once + +namespace storm { + namespace storage { + + enum class BisimulationType { Strong, Weak }; + enum class BisimulationTypeChoice { Strong, Weak, FromSettings }; + + } +} diff --git a/src/storm/storage/dd/BisimulationDecomposition.cpp b/src/storm/storage/dd/BisimulationDecomposition.cpp index 81ad026af..89746b5e9 100644 --- a/src/storm/storage/dd/BisimulationDecomposition.cpp +++ b/src/storm/storage/dd/BisimulationDecomposition.cpp @@ -8,22 +8,18 @@ #include "storm/exceptions/InvalidOperationException.h" #include "storm/exceptions/NotSupportedException.h" -#include <sylvan_table.h> - -extern llmsset_t nodes; - namespace storm { namespace dd { using namespace bisimulation; template <storm::dd::DdType DdType, typename ValueType> - BisimulationDecomposition<DdType, ValueType>::BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model) : BisimulationDecomposition(model, bisimulation::Partition<DdType, ValueType>::create(model)) { + BisimulationDecomposition<DdType, ValueType>::BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, storm::storage::BisimulationType const& bisimulationType) : BisimulationDecomposition(model, bisimulation::Partition<DdType, ValueType>::create(model, bisimulationType)) { // Intentionally left empty. } template <storm::dd::DdType DdType, typename ValueType> - BisimulationDecomposition<DdType, ValueType>::BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) : BisimulationDecomposition(model, bisimulation::Partition<DdType, ValueType>::create(model, formulas)) { + BisimulationDecomposition<DdType, ValueType>::BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType) : BisimulationDecomposition(model, bisimulation::Partition<DdType, ValueType>::create(model, formulas, bisimulationType)) { // Intentionally left empty. } @@ -33,7 +29,7 @@ namespace storm { } template <storm::dd::DdType DdType, typename ValueType> - void BisimulationDecomposition<DdType, ValueType>::compute() { + void BisimulationDecomposition<DdType, ValueType>::compute(bisimulation::SignatureMode const& mode) { this->status = Status::InComputation; auto start = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::duration totalSignatureTime(0); @@ -45,27 +41,41 @@ namespace storm { #endif SignatureRefiner<DdType, ValueType> refiner(model.getManager(), currentPartition.getBlockVariable(), model.getRowVariables()); - SignatureComputer<DdType, ValueType> signatureComputer(model); + SignatureComputer<DdType, ValueType> signatureComputer(model, mode); bool done = false; uint64_t iterations = 0; while (!done) { ++iterations; auto iterationStart = std::chrono::high_resolution_clock::now(); - auto signatureStart = std::chrono::high_resolution_clock::now(); - Signature<DdType, ValueType> signature = signatureComputer.compute(currentPartition); - auto signatureEnd = std::chrono::high_resolution_clock::now(); - totalSignatureTime += (signatureEnd - signatureStart); + std::chrono::milliseconds::rep signatureTime = 0; + std::chrono::milliseconds::rep refinementTime = 0; - auto refinementStart = std::chrono::high_resolution_clock::now(); - Partition<DdType, ValueType> newPartition = refiner.refine(currentPartition, signature); - auto refinementEnd = std::chrono::high_resolution_clock::now(); - totalRefinementTime += (refinementEnd - refinementStart); - - auto signatureTime = std::chrono::duration_cast<std::chrono::milliseconds>(signatureEnd - signatureStart).count(); - auto refinementTime = std::chrono::duration_cast<std::chrono::milliseconds>(refinementEnd - refinementStart).count(); + Partition<DdType, ValueType> newPartition; + for (uint64_t index = 0, end = signatureComputer.getNumberOfSignatures(); index < end; ++index) { + auto signatureStart = std::chrono::high_resolution_clock::now(); + Signature<DdType, ValueType> signature = signatureComputer.compute(currentPartition, index); + auto signatureEnd = std::chrono::high_resolution_clock::now(); + totalSignatureTime += (signatureEnd - signatureStart); + STORM_LOG_DEBUG("Signature " << iterations << "[" << index << "] DD has " << signature.getSignatureAdd().getNodeCount() << " nodes and partition DD has " << currentPartition.getNodeCount() << " nodes."); + + auto refinementStart = std::chrono::high_resolution_clock::now(); + newPartition = refiner.refine(currentPartition, signature); + auto refinementEnd = std::chrono::high_resolution_clock::now(); + totalRefinementTime += (refinementEnd - refinementStart); + + signatureTime += std::chrono::duration_cast<std::chrono::milliseconds>(signatureEnd - signatureStart).count(); + refinementTime = std::chrono::duration_cast<std::chrono::milliseconds>(refinementEnd - refinementStart).count(); + + // Potentially exit early in case we have refined the partition already. + if (newPartition.getNumberOfBlocks() > currentPartition.getNumberOfBlocks()) { + break; + } + } + auto iterationTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - iterationStart).count(); - STORM_LOG_DEBUG("Iteration " << iterations << " produced " << newPartition.getNumberOfBlocks() << " blocks and was completed in " << iterationTime << "ms (signature: " << signatureTime << "ms, refinement: " << refinementTime << "ms). Signature DD has " << signature.getSignatureAdd().getNodeCount() << " nodes and partition DD has " << currentPartition.getNodeCount() << " nodes."); + + STORM_LOG_DEBUG("Iteration " << iterations << " produced " << newPartition.getNumberOfBlocks() << " blocks and was completed in " << iterationTime << "ms (signature: " << signatureTime << "ms, refinement: " << refinementTime << "ms)."); if (currentPartition == newPartition) { done = true; diff --git a/src/storm/storage/dd/BisimulationDecomposition.h b/src/storm/storage/dd/BisimulationDecomposition.h index d13d6a114..3650c58ed 100644 --- a/src/storm/storage/dd/BisimulationDecomposition.h +++ b/src/storm/storage/dd/BisimulationDecomposition.h @@ -4,8 +4,9 @@ #include <vector> #include "storm/storage/dd/DdType.h" - +#include "storm/storage/bisimulation/BisimulationType.h" #include "storm/storage/dd/bisimulation/Partition.h" +#include "storm/storage/dd/bisimulation/SignatureMode.h" #include "storm/models/symbolic/Model.h" @@ -21,14 +22,14 @@ namespace storm { Initialized, InComputation, FixedPoint }; - BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model); - BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas); + BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, storm::storage::BisimulationType const& bisimulationType); + BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType); BisimulationDecomposition(storm::models::symbolic::Model<DdType, ValueType> const& model, bisimulation::Partition<DdType, ValueType> const& initialPartition); /*! * Computes the decomposition. */ - void compute(); + void compute(bisimulation::SignatureMode const& mode = bisimulation::SignatureMode::Eager); /*! * Retrieves the quotient model after the bisimulation decomposition was computed. diff --git a/src/storm/storage/dd/DdMetaVariable.cpp b/src/storm/storage/dd/DdMetaVariable.cpp index fda284391..9cebd15cb 100644 --- a/src/storm/storage/dd/DdMetaVariable.cpp +++ b/src/storm/storage/dd/DdMetaVariable.cpp @@ -68,6 +68,7 @@ namespace storm { for (auto const& v : ddVariables) { indicesAndLevels.emplace_back(v.getIndex(), v.getLevel()); } + return indicesAndLevels; } diff --git a/src/storm/storage/dd/bisimulation/Partition.cpp b/src/storm/storage/dd/bisimulation/Partition.cpp index 6e71074eb..7c434f653 100644 --- a/src/storm/storage/dd/bisimulation/Partition.cpp +++ b/src/storm/storage/dd/bisimulation/Partition.cpp @@ -8,7 +8,11 @@ #include "storm/logic/AtomicExpressionFormula.h" #include "storm/logic/AtomicLabelFormula.h" +#include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/BisimulationSettings.h" + #include "storm/utility/macros.h" +#include "storm/exceptions/NotSupportedException.h" #include "storm/exceptions/InvalidPropertyException.h" namespace storm { @@ -41,34 +45,34 @@ namespace storm { } template<storm::dd::DdType DdType, typename ValueType> - Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model) { - return create(model, model.getLabels()); + Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, storm::storage::BisimulationType const& bisimulationType) { + return create(model, model.getLabels(), bisimulationType); } template<storm::dd::DdType DdType, typename ValueType> - Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::string> const& labels) { + Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::string> const& labels, storm::storage::BisimulationType const& bisimulationType) { std::shared_ptr<PreservationInformation> preservationInformation = std::make_shared<PreservationInformation>(); std::vector<storm::expressions::Expression> expressions; for (auto const& label : labels) { preservationInformation->addLabel(label); expressions.push_back(model.getExpression(label)); } - return create(model, expressions, preservationInformation); + return create(model, expressions, preservationInformation, bisimulationType); } template<storm::dd::DdType DdType, typename ValueType> - Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions) { + Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions, storm::storage::BisimulationType const& bisimulationType) { std::shared_ptr<PreservationInformation> preservationInformation = std::make_shared<PreservationInformation>(); for (auto const& expression : expressions) { preservationInformation->addExpression(expression); } - return create(model, expressions, preservationInformation); + return create(model, expressions, preservationInformation, bisimulationType); } template<storm::dd::DdType DdType, typename ValueType> - Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) { + Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType) { if (formulas.empty()) { - return create(model); + return create(model, bisimulationType); } std::shared_ptr<PreservationInformation> preservationInformation = std::make_shared<PreservationInformation>(); @@ -93,11 +97,14 @@ namespace storm { expressionVector.emplace_back(expression); } - return create(model, expressionVector, preservationInformation); + return create(model, expressionVector, preservationInformation, bisimulationType); } template<storm::dd::DdType DdType, typename ValueType> - Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions, std::shared_ptr<PreservationInformation> const& preservationInformation) { + Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions, std::shared_ptr<PreservationInformation> const& preservationInformation, storm::storage::BisimulationType const& bisimulationType) { + + STORM_LOG_THROW(bisimulationType == storm::storage::BisimulationType::Strong, storm::exceptions::NotSupportedException, "Currently only strong bisimulation is supported."); + storm::dd::DdManager<DdType>& manager = model.getManager(); std::vector<storm::dd::Bdd<DdType>> stateSets; @@ -122,6 +129,20 @@ namespace storm { } } + template<storm::dd::DdType DdType, typename ValueType> + uint64_t Partition<DdType, ValueType>::getNumberOfStates() const { + return this->getStates().getNonZeroCount(); + } + + template<storm::dd::DdType DdType, typename ValueType> + storm::dd::Bdd<DdType> Partition<DdType, ValueType>::getStates() const { + if (this->storedAsAdd()) { + return this->asAdd().notZero().existsAbstract({this->getBlockVariable()}); + } else { + return this->asBdd().existsAbstract({this->getBlockVariable()}); + } + } + template<storm::dd::DdType DdType, typename ValueType> uint64_t Partition<DdType, ValueType>::getNumberOfBlocks() const { return nextFreeBlockIndex; diff --git a/src/storm/storage/dd/bisimulation/Partition.h b/src/storm/storage/dd/bisimulation/Partition.h index f52c827cb..80d882645 100644 --- a/src/storm/storage/dd/bisimulation/Partition.h +++ b/src/storm/storage/dd/bisimulation/Partition.h @@ -8,6 +8,7 @@ #include "storm/storage/dd/DdType.h" #include "storm/storage/dd/Add.h" #include "storm/storage/dd/Bdd.h" +#include "storm/storage/bisimulation/BisimulationType.h" #include "storm/models/symbolic/Model.h" @@ -35,23 +36,24 @@ namespace storm { /*! * Creates a partition from the given model that respects all labels. */ - static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model); + static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, storm::storage::BisimulationType const& bisimulationType); /*! * Creates a partition from the given model that respects the given labels. */ - static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::string> const& labels); + static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::string> const& labels, storm::storage::BisimulationType const& bisimulationType); /*! * Creates a partition from the given model that respects the given expressions. */ - static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions); + static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions, storm::storage::BisimulationType const& bisimulationType); /*! * Creates a partition from the given model that preserves the given formulas. */ - static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas); + static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType); + uint64_t getNumberOfStates() const; uint64_t getNumberOfBlocks() const; bool storedAsAdd() const; @@ -66,6 +68,8 @@ namespace storm { uint64_t getNextFreeBlockIndex() const; uint64_t getNodeCount() const; + storm::dd::Bdd<DdType> getStates() const; + PreservationInformation const& getPreservationInformation() const; private: @@ -98,7 +102,7 @@ namespace storm { /*! * Creates a partition from the given model that respects the given expressions. */ - static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions, std::shared_ptr<PreservationInformation> const& preservationInformation); + static Partition create(storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::expressions::Expression> const& expressions, std::shared_ptr<PreservationInformation> const& preservationInformation, storm::storage::BisimulationType const& bisimulationType); static std::pair<storm::dd::Bdd<DdType>, uint64_t> 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); diff --git a/src/storm/storage/dd/bisimulation/QuotientExtractor.cpp b/src/storm/storage/dd/bisimulation/QuotientExtractor.cpp index aee570395..2fb31b2d1 100644 --- a/src/storm/storage/dd/bisimulation/QuotientExtractor.cpp +++ b/src/storm/storage/dd/bisimulation/QuotientExtractor.cpp @@ -83,6 +83,7 @@ namespace storm { // Create the number of rows necessary for the matrix. this->entries.resize(partition.getNumberOfBlocks()); + STORM_LOG_TRACE("Partition has " << partition.getNumberOfStates() << " states in " << partition.getNumberOfBlocks() << " blocks."); storm::storage::BitVector encoding(this->stateVariablesIndicesAndLevels.size()); extractTransitionMatrixRec(transitionMatrix.getInternalAdd().getCuddDdNode(), partition.asAdd().getInternalAdd().getCuddDdNode(), partition.asAdd().getInternalAdd().getCuddDdNode(), 0, encoding); @@ -106,11 +107,16 @@ namespace storm { return *blockCacheEntry; } +// FILE* fp = fopen("block.dot" , "w"); +// Cudd_DumpDot(ddman, 1, &blockEncoding, nullptr, nullptr, fp); +// fclose(fp); + uint64_t result = 0; uint64_t offset = 0; while (blockEncoding != Cudd_ReadOne(ddman)) { - if (Cudd_T(blockEncoding) != Cudd_ReadZero(ddman)) { - blockEncoding = Cudd_T(blockEncoding); + DdNode* then = Cudd_T(blockEncoding); + if (then != Cudd_ReadZero(ddman)) { + blockEncoding = then; result |= 1ull << offset; } else { blockEncoding = Cudd_E(blockEncoding); @@ -205,6 +211,7 @@ namespace storm { DdNode* te = transitionMatrixNode; DdNode* et = transitionMatrixNode; DdNode* ee = transitionMatrixNode; + STORM_LOG_ASSERT(transitionMatrixVariable >= this->stateVariablesIndicesAndLevels[currentIndex].first, "Illegal top variable of transition matrix."); if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) { DdNode* t = Cudd_T(transitionMatrixNode); DdNode* e = Cudd_E(transitionMatrixNode); @@ -226,16 +233,17 @@ namespace storm { } } else { if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) { - tt = Cudd_T(transitionMatrixNode); - te = Cudd_E(transitionMatrixNode); + tt = et = Cudd_T(transitionMatrixNode); + te = ee = Cudd_E(transitionMatrixNode); } else { - tt = te = transitionMatrixNode; + tt = te = et = ee = transitionMatrixNode; } } // Move through partition (for source state). DdNode* sourceT; DdNode* sourceE; + STORM_LOG_ASSERT(sourcePartitionVariable >= this->stateVariablesIndicesAndLevels[currentIndex].first, "Illegal top variable of source partition."); if (sourcePartitionVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) { sourceT = Cudd_T(sourcePartitionNode); sourceE = Cudd_E(sourcePartitionNode); @@ -246,6 +254,7 @@ namespace storm { // Move through partition (for target state). DdNode* targetT; DdNode* targetE; + STORM_LOG_ASSERT(targetPartitionVariable >= this->stateVariablesIndicesAndLevels[currentIndex].first, "Illegal top variable of source partition."); if (targetPartitionVariable == this->stateVariablesIndicesAndLevels[currentIndex].first) { targetT = Cudd_T(targetPartitionNode); targetE = Cudd_E(targetPartitionNode); @@ -422,10 +431,10 @@ namespace storm { } } else { if (transitionMatrixVariable == this->stateVariablesIndicesAndLevels[currentIndex].first + 1) { - tt = sylvan_high(transitionMatrixNode); - te = sylvan_low(transitionMatrixNode); + tt = et = sylvan_high(transitionMatrixNode); + te = ee = sylvan_low(transitionMatrixNode); } else { - tt = te = transitionMatrixNode; + tt = te = et = ee = transitionMatrixNode; } } @@ -490,6 +499,8 @@ namespace storm { template<storm::dd::DdType DdType, typename ValueType> 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()); + auto states = partition.getStates().swapVariables(model.getRowColumnMetaVariablePairs()); + storm::storage::SparseMatrix<ValueType> quotientTransitionMatrix = sparseExtractor.extractTransitionMatrix(model.getTransitionMatrix(), partition); storm::models::sparse::StateLabeling quotientStateLabeling(partition.getNumberOfBlocks()); diff --git a/src/storm/storage/dd/bisimulation/SignatureComputer.cpp b/src/storm/storage/dd/bisimulation/SignatureComputer.cpp index 873d81608..f7606115f 100644 --- a/src/storm/storage/dd/bisimulation/SignatureComputer.cpp +++ b/src/storm/storage/dd/bisimulation/SignatureComputer.cpp @@ -7,14 +7,35 @@ namespace storm { namespace bisimulation { template<storm::dd::DdType DdType, typename ValueType> - SignatureComputer<DdType, ValueType>::SignatureComputer(storm::models::symbolic::Model<DdType, ValueType> const& model) : model(model), transitionMatrix(model.getTransitionMatrix()) { + SignatureComputer<DdType, ValueType>::SignatureComputer(storm::models::symbolic::Model<DdType, ValueType> const& model, SignatureMode const& mode) : model(model), transitionMatrix(model.getTransitionMatrix()), mode(mode) { if (DdType == storm::dd::DdType::Sylvan) { this->transitionMatrix = this->transitionMatrix.notZero().ite(this->transitionMatrix, this->transitionMatrix.getDdManager().template getAddUndefined<ValueType>()); } + if (mode == SignatureMode::Lazy) { + if (DdType == storm::dd::DdType::Sylvan) { + this->transitionMatrix01 = model.getQualitativeTransitionMatrix().ite(this->transitionMatrix.getDdManager().template getAddOne<ValueType>(), this->transitionMatrix.getDdManager().template getAddUndefined<ValueType>()); + } else { + this->transitionMatrix01 = model.getQualitativeTransitionMatrix().template toAdd<ValueType>(); + } + } } template<storm::dd::DdType DdType, typename ValueType> - Signature<DdType, ValueType> SignatureComputer<DdType, ValueType>::compute(Partition<DdType, ValueType> const& partition) { + uint64_t SignatureComputer<DdType, ValueType>::getNumberOfSignatures() const { + return this->mode == SignatureMode::Lazy ? 2 : 1; + } + + template<storm::dd::DdType DdType, typename ValueType> + Signature<DdType, ValueType> SignatureComputer<DdType, ValueType>::compute(Partition<DdType, ValueType> const& partition, uint64_t index) { + if (mode == SignatureMode::Lazy && index == 1) { + return getQualitativeSignature(partition); + } else { + return getFullSignature(partition); + } + } + + template<storm::dd::DdType DdType, typename ValueType> + Signature<DdType, ValueType> SignatureComputer<DdType, ValueType>::getFullSignature(Partition<DdType, ValueType> const& partition) const { if (partition.storedAsBdd()) { return Signature<DdType, ValueType>(this->transitionMatrix.multiplyMatrix(partition.asBdd(), model.getColumnVariables())); } else { @@ -22,6 +43,15 @@ namespace storm { } } + template<storm::dd::DdType DdType, typename ValueType> + Signature<DdType, ValueType> SignatureComputer<DdType, ValueType>::getQualitativeSignature(Partition<DdType, ValueType> const& partition) const { + if (partition.storedAsBdd()) { + return Signature<DdType, ValueType>(this->transitionMatrix01.multiplyMatrix(partition.asBdd(), model.getColumnVariables())); + } else { + return Signature<DdType, ValueType>(this->transitionMatrix01.multiplyMatrix(partition.asAdd(), model.getColumnVariables())); + } + } + template class SignatureComputer<storm::dd::DdType::CUDD, double>; template class SignatureComputer<storm::dd::DdType::Sylvan, double>; diff --git a/src/storm/storage/dd/bisimulation/SignatureComputer.h b/src/storm/storage/dd/bisimulation/SignatureComputer.h index f2de03f49..9b6177c1e 100644 --- a/src/storm/storage/dd/bisimulation/SignatureComputer.h +++ b/src/storm/storage/dd/bisimulation/SignatureComputer.h @@ -4,6 +4,7 @@ #include "storm/storage/dd/bisimulation/Signature.h" #include "storm/storage/dd/bisimulation/Partition.h" +#include "storm/storage/dd/bisimulation/SignatureMode.h" #include "storm/models/symbolic/Model.h" @@ -14,14 +15,27 @@ namespace storm { template<storm::dd::DdType DdType, typename ValueType> class SignatureComputer { public: - SignatureComputer(storm::models::symbolic::Model<DdType, ValueType> const& model); + SignatureComputer(storm::models::symbolic::Model<DdType, ValueType> const& model, SignatureMode const& mode = SignatureMode::Eager); - Signature<DdType, ValueType> compute(Partition<DdType, ValueType> const& partition); + Signature<DdType, ValueType> compute(Partition<DdType, ValueType> const& partition, uint64_t index = 0); + + uint64_t getNumberOfSignatures() const; private: + Signature<DdType, ValueType> getFullSignature(Partition<DdType, ValueType> const& partition) const; + + Signature<DdType, ValueType> getQualitativeSignature(Partition<DdType, ValueType> const& partition) const; + storm::models::symbolic::Model<DdType, ValueType> const& model; + // The transition matrix to use for the signature computation. storm::dd::Add<DdType, ValueType> transitionMatrix; + + // The mode to use for signature computation. + SignatureMode mode; + + // Only used when using lazy signatures is enabled. + storm::dd::Add<DdType, ValueType> transitionMatrix01; }; } diff --git a/src/storm/storage/dd/bisimulation/SignatureMode.h b/src/storm/storage/dd/bisimulation/SignatureMode.h new file mode 100644 index 000000000..bcec54b6f --- /dev/null +++ b/src/storm/storage/dd/bisimulation/SignatureMode.h @@ -0,0 +1,11 @@ +#pragma once + +namespace storm { + namespace dd { + namespace bisimulation { + + enum class SignatureMode { Eager, Lazy }; + + } + } +}