From d4ed882795ecc824c04f0357d10a8c6990ecddba Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 10 Sep 2015 16:36:18 +0200 Subject: [PATCH 001/400] more work on menu-game abstraction PRISM programs Former-commit-id: acc54b7f159e1c15fd801eec05c7a254e83b5b80 --- src/CMakeLists.txt | 4 +- src/adapters/Z3ExpressionAdapter.h | 2 + .../prism/menu_games/AbstractCommand.cpp | 17 +++++ .../prism/menu_games/AbstractCommand.h | 48 +++++++++++++ .../prism/menu_games/AbstractModule.cpp | 21 ++++++ src/storage/prism/menu_games/AbstractModule.h | 51 ++++++++++++++ .../prism/menu_games/AbstractProgram.cpp | 64 +++++++++++++++++ .../prism/menu_games/AbstractProgram.h | 69 +++++++++++++++++++ .../prism/menu_games/VariablePartition.cpp | 0 .../prism/menu_games/VariablePartition.h | 0 10 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 src/storage/prism/menu_games/AbstractCommand.cpp create mode 100644 src/storage/prism/menu_games/AbstractCommand.h create mode 100644 src/storage/prism/menu_games/AbstractModule.cpp create mode 100644 src/storage/prism/menu_games/AbstractModule.h create mode 100644 src/storage/prism/menu_games/AbstractProgram.cpp create mode 100644 src/storage/prism/menu_games/AbstractProgram.h create mode 100644 src/storage/prism/menu_games/VariablePartition.cpp create mode 100644 src/storage/prism/menu_games/VariablePartition.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 454fe8262..5671ce4c6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,7 +35,8 @@ file(GLOB_RECURSE STORM_SOLVER_FILES ${PROJECT_SOURCE_DIR}/src/solver/*.h ${PROJ file(GLOB STORM_STORAGE_FILES ${PROJECT_SOURCE_DIR}/src/storage/*.h ${PROJECT_SOURCE_DIR}/src/storage/*.cpp) file(GLOB_RECURSE STORM_STORAGE_DD_FILES ${PROJECT_SOURCE_DIR}/src/storage/dd/*.h ${PROJECT_SOURCE_DIR}/src/storage/dd/*.cpp) file(GLOB_RECURSE STORM_STORAGE_EXPRESSIONS_FILES ${PROJECT_SOURCE_DIR}/src/storage/expressions/*.h ${PROJECT_SOURCE_DIR}/src/storage/expressions/*.cpp) -file(GLOB_RECURSE STORM_STORAGE_PRISM_FILES ${PROJECT_SOURCE_DIR}/src/storage/prism/*.h ${PROJECT_SOURCE_DIR}/src/storage/prism/*.cpp) +file(GLOB STORM_STORAGE_PRISM_FILES ${PROJECT_SOURCE_DIR}/src/storage/prism/*.h ${PROJECT_SOURCE_DIR}/src/storage/prism/*.cpp) +file(GLOB STORM_STORAGE_PRISM_MENU_GAME_FILES ${PROJECT_SOURCE_DIR}/src/storage/prism/menu_games/*.h ${PROJECT_SOURCE_DIR}/src/storage/prism/menu_games/*.cpp) file(GLOB_RECURSE STORM_STORAGE_SPARSE_FILES ${PROJECT_SOURCE_DIR}/src/storage/sparse/*.h ${PROJECT_SOURCE_DIR}/src/storage/sparse/*.cpp) file(GLOB_RECURSE STORM_UTILITY_FILES ${PROJECT_SOURCE_DIR}/src/utility/*.h ${PROJECT_SOURCE_DIR}/src/utility/*.cpp) @@ -80,6 +81,7 @@ source_group(storage FILES ${STORM_STORAGE_FILES}) source_group(storage\\dd FILES ${STORM_STORAGE_DD_FILES}) source_group(storage\\expressions FILES ${STORM_STORAGE_EXPRESSIONS_FILES}) source_group(storage\\prism FILES ${STORM_STORAGE_PRISM_FILES}) +source_group(storage\\prism\\menu_games FILES ${STORM_STORAGE_PRISM_MENU_GAME_FILES}) source_group(storage\\sparse FILES ${STORM_STORAGE_SPARSE_FILES}) source_group(utility FILES ${STORM_UTILITY_FILES}) diff --git a/src/adapters/Z3ExpressionAdapter.h b/src/adapters/Z3ExpressionAdapter.h index 4fc60c8a5..7ab8de742 100644 --- a/src/adapters/Z3ExpressionAdapter.h +++ b/src/adapters/Z3ExpressionAdapter.h @@ -47,6 +47,7 @@ namespace storm { z3::expr translateExpression(storm::expressions::Variable const& variable); storm::expressions::Expression translateExpression(z3::expr const& expr); + /*! * Finds the counterpart to the given z3 variable declaration. * @@ -82,6 +83,7 @@ namespace storm { * @param variable The variable for which to create a Z3 counterpart. */ z3::expr createVariable(storm::expressions::Variable const& variable); + // The manager that can be used to build expressions. storm::expressions::ExpressionManager& manager; diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp new file mode 100644 index 000000000..558d1e77c --- /dev/null +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -0,0 +1,17 @@ +#include "src/storage/prism/menu_games/AbstractCommand.h" + +#include "src/storage/prism/Command.h" +#include "src/storage/prism/Update.h" + +namespace storm { + namespace prism { + namespace menu_games { + template + AbstractCommand::AbstractCommand(storm::expressions::ExpressionManager& expressionManager, storm::prism::Command const& command, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : expressionManager(expressionManager), smtSolver(smtSolverFactory.create(expressionManager)), predicates(initialPredicates), command(command) { + // Intentionally left empty. + } + + template class AbstractCommand; + } + } +} \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h new file mode 100644 index 000000000..a380bc4f6 --- /dev/null +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -0,0 +1,48 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTCOMMAND_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTCOMMAND_H_ + +#include + +#include "src/storage/dd/DdType.h" +#include "src/storage/expressions/Expression.h" + +#include "src/solver/SmtSolver.h" +#include "src/utility/solver.h" + +namespace storm { + namespace prism { + // Forward-declare concrete command and update classes. + class Command; + + namespace menu_games { + template + class AbstractCommand { + public: + /*! + * Constructs an abstract command from the given command and the initial predicates. + * + * @param expressionManager The manager responsible for the expressions of the command. + * @param command The concrete command for which to build the abstraction. + * @param initialPredicates The initial set of predicates. + * @param smtSolverFactory A factory that is to be used for creating new SMT solvers. + */ + AbstractCommand(storm::expressions::ExpressionManager& expressionManager, storm::prism::Command const& command, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + + private: + // The manager responsible for the expressions of the command and the SMT solvers. + storm::expressions::ExpressionManager& expressionManager; + + // An SMT responsible for this abstract command. + std::unique_ptr smtSolver; + + // The current set of predicates used in the abstraction. + std::vector predicates; + + // The concrete command this abstract command refers to. + std::reference_wrapper command; + }; + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTCOMMAND_H_ */ \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp new file mode 100644 index 000000000..8252d450c --- /dev/null +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -0,0 +1,21 @@ +#include "src/storage/prism/menu_games/AbstractModule.h" + +#include "src/storage/prism/Module.h" + +namespace storm { + namespace prism { + namespace menu_games { + + template + AbstractModule::AbstractModule(storm::expressions::ExpressionManager& expressionManager, storm::prism::Module const& module, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : expressionManager(expressionManager), smtSolverFactory(smtSolverFactory), predicates(initialPredicates), commands(), module(module) { + + // For each concrete command, we create an abstract counterpart. + for (auto const& command : module.getCommands()) { + commands.emplace_back(expressionManager, command, initialPredicates, smtSolverFactory); + } + } + + template class AbstractModule; + } + } +} \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h new file mode 100644 index 000000000..3d292203d --- /dev/null +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -0,0 +1,51 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTMODULE_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTMODULE_H_ + +#include "src/storage/dd/DdType.h" + +#include "src/storage/prism/menu_games/AbstractCommand.h" + +#include "src/storage/expressions/Expression.h" + +#include "src/utility/solver.h" + +namespace storm { + namespace prism { + // Forward-declare concrete module class. + class Module; + + namespace menu_games { + template + class AbstractModule { + public: + /*! + * Constructs an abstract module from the given module and the initial predicates. + * + * @param expressionManager The manager responsible for the expressions of the command. + * @param module The concrete module for which to build the abstraction. + * @param initialPredicates The initial set of predicates. + * @param smtSolverFactory A factory that is to be used for creating new SMT solvers. + */ + AbstractModule(storm::expressions::ExpressionManager& expressionManager, storm::prism::Module const& module, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + + private: + // The manager responsible for the expressions of the module and the SMT solvers. + storm::expressions::ExpressionManager& expressionManager; + + // A factory that can be used to create new SMT solvers. + storm::utility::solver::SmtSolverFactory const& smtSolverFactory; + + // The current set of predicates used in the abstraction. + std::vector predicates; + + // The abstract commands of the abstract module. + std::vector> commands; + + // The concrete module this abstract module refers to. + std::reference_wrapper module; + }; + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTMODULE_H_ */ \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp new file mode 100644 index 000000000..801805054 --- /dev/null +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -0,0 +1,64 @@ +#include "src/storage/prism/menu_games/AbstractProgram.h" + +#include + +#include "src/storage/prism/Program.h" + +#include "src/storage/dd/CuddDdManager.h" + +#include "src/utility/macros.h" +#include "src/exceptions/WrongFormatException.h" + +namespace storm { + namespace prism { + namespace menu_games { + + template + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : expressionManager(expressionManager), smtSolverFactory(std::move(smtSolverFactory)), predicates(initialPredicates), ddManager(new storm::dd::DdManager()), predicateDdVariables(), commandDdVariable(), optionDdVariables(), modules(), program(program) { + + // For now, we assume that there is a single module. If the program has more than one module, it needs + // to be flattened before the procedure. + STORM_LOG_THROW(program.getNumberOfModules() == 1, storm::exceptions::WrongFormatException, "Cannot create abstract program from program containing too many modules."); + + uint_fast64_t totalNumberOfCommands = 0; + for (auto const& module : program.getModules()) { + // If we were requested to add all guards to the set of predicates, we do so now. + if (addAllGuards) { + for (auto const& command : module.getCommands()) { + predicates.push_back(command.getGuardExpression()); + } + } + + totalNumberOfCommands += module.getNumberOfCommands(); + } + + // Create DD variables for all predicates. + for (auto const& predicate : predicates) { + std::stringstream stream; + stream << predicate; + predicateDdVariables.push_back(ddManager->addMetaVariable(stream.str())); + } + + // Create DD variable for the command encoding. + commandDdVariable = ddManager->addMetaVariable("command", 0, totalNumberOfCommands - 1); + + // Create DD variables encoding the nondeterministic choices of player 2. + // NOTE: currently we assume that 100 variables suffice, which corresponds to 2^100 possible choices. + // If for some reason this should not be enough, we could grow this vector dynamically, but odds are + // that it's impossible to treat such models in any event. + for (uint_fast64_t index = 0; index < 100; ++index) { + optionDdVariables.push_back(ddManager->addMetaVariable("opt" + std::to_string(index))); + } + + // For each module of the concrete program, we create an abstract counterpart. + for (auto const& module : program.getModules()) { + modules.emplace_back(expressionManager, module, predicates, *smtSolverFactory); + } + } + + // Explicitly instantiate the class. + template class AbstractProgram; + + } + } +} \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h new file mode 100644 index 000000000..e00f4e8f1 --- /dev/null +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -0,0 +1,69 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTPROGRAM_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTPROGRAM_H_ + +#include "src/storage/dd/DdType.h" + +#include "src/storage/prism/menu_games/AbstractModule.h" + +#include "src/storage/expressions/Expression.h" + +#include "src/utility/solver.h" + +namespace storm { + namespace dd { + template + class DdManager; + } + + namespace prism { + // Forward-declare concrete Program class. + class Program; + + namespace menu_games { + template + class AbstractProgram { + public: + /*! + * Constructs an abstract program from the given program and the initial predicates. + * + * @param expressionManager The manager responsible for the expressions of the program. + * @param program The concrete program for which to build the abstraction. + * @param initialPredicates The initial set of predicates. + * @param smtSolverFactory A factory that is to be used for creating new SMT solvers. + * @param addAllGuards A flag that indicates whether all guards of the program should be added to the initial set of predicates. + */ + AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory = std::unique_ptr(new storm::utility::solver::SmtSolverFactory()), bool addAllGuards = false); + + private: + // The manager responsible for the expressions of the program and the SMT solvers. + storm::expressions::ExpressionManager& expressionManager; + + // A factory that can be used to create new SMT solvers. + std::unique_ptr smtSolverFactory; + + // The current set of predicates used in the abstraction. + std::vector predicates; + + // The manager responsible for the DDs. + std::shared_ptr> ddManager; + + // The DD variables corresponding to the predicates. + std::vector> predicateDdVariables; + + // The DD variable encoding the command (i.e., the nondeterministic choices of player 1). + std::pair commandDdVariable; + + // The DD variables encoding the nondeterministic choices of player 2. + std::vector> optionDdVariables; + + // The abstract modules of the abstract program. + std::vector> modules; + + // The concrete program this abstract program refers to. + std::reference_wrapper program; + }; + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTPROGRAM_H_ */ \ No newline at end of file diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/storage/prism/menu_games/VariablePartition.h b/src/storage/prism/menu_games/VariablePartition.h new file mode 100644 index 000000000..e69de29bb From fd5e90848148ba98eec685ecaed8691ff8f3fa33 Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 10 Sep 2015 19:19:05 +0200 Subject: [PATCH 002/400] more work on variable partition Former-commit-id: ce50d448a4407a8b6daa5b3f1e3401c5efbb49e8 --- .../prism/menu_games/VariablePartition.cpp | 126 ++++++++++++++++ .../prism/menu_games/VariablePartition.h | 137 ++++++++++++++++++ 2 files changed, 263 insertions(+) diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index e69de29bb..c6536ba5b 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -0,0 +1,126 @@ +#include "src/storage/prism/menu_games/VariablePartition.h" + +#include "src/utility/macros.h" + +namespace storm { + namespace prism { + namespace menu_games { + VariablePartition::VariablePartition(std::set const& relevantVariables, std::vector const& expressions) : relevantVariables(relevantVariables), expressionBlocks(expressions.size()) { + for (auto const& expression : expressions) { + this->addExpression(expression); + } + } + + bool VariablePartition::addExpression(storm::expressions::Expression const& expression) { + // Register the expression for all variables that appear in it. + std::set expressionVariables = expression.getVariables(); + for (auto const& variable : expressionVariables) { + variableToExpressionsMapping[variable].insert(this->expressions.size()); + } + + // Add aexpression and relate all the appearing variables. + this->expressions.push_back(expression); + return this->relate(expressionVariables); + } + + bool VariablePartition::areRelated(storm::expressions::Variable const& firstVariable, storm::expressions::Variable const& secondVariable) { + return getBlockIndexOfVariable(firstVariable) == getBlockIndexOfVariable(secondVariable); + } + + bool VariablePartition::relate(storm::expressions::Variable const& firstVariable, storm::expressions::Variable const& secondVariable) { + return this->relate({firstVariable, secondVariable}); + } + + bool VariablePartition::relate(std::set const& variables) { + // Determine all blocks that need to be merged. + std::set blocksToMerge; + for (auto const& variable : variables) { + blocksToMerge.insert(getBlockIndexOfVariable(variable)); + } + + STORM_LOG_ASSERT(!blocksToMerge.empty(), "Found no blocks to merge."); + + // If we found a single block only, there is nothing to do. + if (blocksToMerge.size() == 1) { + return false; + } + + this->mergeBlocks(blocksToMerge); + return true; + } + + void VariablePartition::mergeBlocks(std::set const& blocksToMerge) { + // Determine which block to keep (to merge the other blocks into). + uint_fast64_t blockToKeep = *blocksToMerge.begin(); + + // Merge all blocks into the block to keep. + std::vector> newVariableBlocks; + std::vector> newExpressionBlocks; + for (uint_fast64_t blockIndex = 0; blockIndex < variableBlocks.size(); ++blockIndex) { + + // If the block is the one into which the others are to be merged, we do so. + if (blockIndex == blockToKeep) { + for (auto const& blockToMerge : blocksToMerge) { + if (blockToMerge == blockToKeep) { + continue; + } + + for (auto const& variable : variableBlocks[blockToMerge]) { + variableBlocks[blockToKeep].insert(variable); + } + + for (auto const& expression : expressionBlocks[blockToMerge]) { + expressionBlocks[blockToKeep].insert(expression); + } + } + } + + // Adjust the mapping for all variables we are moving to the new block. + for (auto const& variable : variableBlocks[blockIndex]) { + variableToBlockMapping[variable] = newVariableBlocks.size(); + } + + // Move the current block to the new partition. + newVariableBlocks.emplace_back(std::move(variableBlocks[blockIndex])); + newExpressionBlocks.emplace_back(std::move(expressionBlocks[blockIndex])); + } + } + + std::set const& VariablePartition::getBlockOfVariable(storm::expressions::Variable const& variable) const { + return variableBlocks[getBlockIndexOfVariable(variable)]; + } + + uint_fast64_t VariablePartition::getNumberOfBlocks() const { + return this->variableBlocks.size(); + } + + std::set const& VariablePartition::getVariableBlockWithIndex(uint_fast64_t blockIndex) const { + return this->variableBlocks[blockIndex]; + } + + uint_fast64_t VariablePartition::getBlockIndexOfVariable(storm::expressions::Variable const& variable) const { + STORM_LOG_ASSERT(this->relevantVariables.find(variable) != this->relevantVariables.end(), "Illegal variable '" << variable.getName() << "' for partition."); + return this->variableToBlockMapping.find(variable)->second; + } + + std::set const& VariablePartition::getRelatedExpressions(storm::expressions::Variable const& variable) const { + return this->expressionBlocks[getBlockIndexOfVariable(variable)]; + } + + std::set VariablePartition::getRelatedExpressions(std::set const& variables) const { + // Start by determining the indices of all expression blocks that are related to any of the variables. + std::set relatedExpressionBlockIndices; + for (auto const& variable : variables) { + relatedExpressionBlockIndices.insert(getBlockIndexOfVariable(variable)); + } + + // Then join the expressions of these blocks and return the result. + std::set result; + for (auto const& blockIndex : relatedExpressionBlockIndices) { + result.insert(expressionBlocks[blockIndex].begin(), expressionBlocks[blockIndex].end()); + } + return result; + } + } + } +} \ No newline at end of file diff --git a/src/storage/prism/menu_games/VariablePartition.h b/src/storage/prism/menu_games/VariablePartition.h index e69de29bb..10b605cf8 100644 --- a/src/storage/prism/menu_games/VariablePartition.h +++ b/src/storage/prism/menu_games/VariablePartition.h @@ -0,0 +1,137 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_VARIABLEPARTITION_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_VARIABLEPARTITION_H_ + +#include +#include +#include + +#include "src/storage/expressions/Variable.h" +#include "src/storage/expressions/Expression.h" + +namespace storm { + namespace prism { + namespace menu_games { + + class VariablePartition{ + public: + /*! + * Constructs a new variable partition. + * + * @param relevantVariables The variables of this partition. + * @param expressions The (initial) expressions that define the partition. + */ + VariablePartition(std::set const& relevantVariables, std::vector const& expressions = std::vector()); + + /*! + * Adds the expression and therefore indirectly may cause blocks of variables to be merged. + * + * @param expression The expression to add. + * @return True iff the partition changed. + */ + bool addExpression(storm::expressions::Expression const& expression); + + /*! + * Retrieves whether the two given variables are in the same block of the partition. + * + * @param firstVariable The first variable. + * @param secondVariable The second variable. + * @return True iff the two variables are in the same block. + */ + bool areRelated(storm::expressions::Variable const& firstVariable, storm::expressions::Variable const& secondVariable); + + /*! + * Places the given variables in the same block of the partition and performs the implied merges. + * + * @param firstVariable The first variable. + * @param secondVariable The second variable. + * @return True iff the partition changed. + */ + bool relate(storm::expressions::Variable const& firstVariable, storm::expressions::Variable const& secondVariable); + + /*! + * Places the given variables in the same block of the partition and performs the implied merges. + * + * @param variables The variables to relate. + * @return True iff the partition changed. + */ + bool relate(std::set const& variables); + + /*! + * Retrieves the block of related variables of the given variable. + * + * @param variable The variable whose block to retrieve. + * @return The block of the variable. + */ + std::set const& getBlockOfVariable(storm::expressions::Variable const& variable) const; + + /*! + * Retrieves the block index of the given variable. + * + * @param variable The variable for which to retrieve the block. + * @return The block index of the given variable. + */ + uint_fast64_t getBlockIndexOfVariable(storm::expressions::Variable const& variable) const; + + /*! + * Retrieves the number of blocks of the varible partition. + * + * @return The number of blocks in this partition. + */ + uint_fast64_t getNumberOfBlocks() const; + + /*! + * Retrieves the block with the given index. + * + * @param blockIndex The index of the block to retrieve. + * @return The block with the given index. + */ + std::set const& getVariableBlockWithIndex(uint_fast64_t blockIndex) const; + + /*! + * Retrieves the indices of the expressions related to the given variable. + * + * @param variable The variable for which to retrieve the related expressions. + * @return The related expressions. + */ + std::set const& getRelatedExpressions(storm::expressions::Variable const& variable) const; + + /*! + * Retrieves the indices of the expressions related to any of the given variables. + * + * @param variables The variables for which to retrieve the related expressions. + * @return The related expressions. + */ + std::set getRelatedExpressions(std::set const& variables) const; + + private: + /*! + * Merges the blocks with the given indices. + * + * @param blocksToMerge The indices of the blocks to merge. + */ + void mergeBlocks(std::set const& blocksToMerge); + + // The set of variables relevant for this partition. + std::set relevantVariables; + + // A mapping from variables to their blocks. + std::unordered_map variableToBlockMapping; + + // The variable blocks of the partition. + std::vector> variableBlocks; + + // The expression blocks of the partition. + std::vector> expressionBlocks; + + // A mapping from variables to the indices of all expressions they appear in. + std::unordered_map> variableToExpressionsMapping; + + // The vector of all expressions. + std::vector expressions; + }; + + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_VARIABLEPARTITION_H_ */ \ No newline at end of file From 36e8359efaf730ebd805d1b1311462c44b0d91fc Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 10 Sep 2015 22:50:38 +0200 Subject: [PATCH 003/400] added some useful functions to variable partition Former-commit-id: b290129abb17db1f283de668df8105178d624861 --- .../prism/menu_games/VariablePartition.cpp | 37 +++++++++++++++---- .../prism/menu_games/VariablePartition.h | 21 +++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index c6536ba5b..53c0d2a61 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -1,5 +1,7 @@ #include "src/storage/prism/menu_games/VariablePartition.h" +#include + #include "src/utility/macros.h" namespace storm { @@ -65,13 +67,8 @@ namespace storm { continue; } - for (auto const& variable : variableBlocks[blockToMerge]) { - variableBlocks[blockToKeep].insert(variable); - } - - for (auto const& expression : expressionBlocks[blockToMerge]) { - expressionBlocks[blockToKeep].insert(expression); - } + variableBlocks[blockToKeep].insert(variableBlocks[blockToMerge].begin(), variableBlocks[blockToMerge].end()); + expressionBlocks[blockToKeep].insert(expressionBlocks[blockToMerge].begin(), expressionBlocks[blockToMerge].end()); } } @@ -121,6 +118,32 @@ namespace storm { } return result; } + + std::set const& VariablePartition::getExpressionsUsingVariable(storm::expressions::Variable const& variable) const { + STORM_LOG_ASSERT(this->relevantVariables.find(variable) != this->relevantVariables.end(), "Illegal variable '" << variable.getName() << "' for partition."); + return this->variableToExpressionsMapping.find(variable)->second; + } + + storm::expressions::Expression const& VariablePartition::getExpression(uint_fast64_t expressionIndex) const { + return this->expressions[expressionIndex]; + } + + std::ostream& operator<<(std::ostream& out, VariablePartition const& partition) { + std::vector blocks; + for (auto const& block : partition.variableBlocks) { + std::vector variablesInBlock; + for (auto const& variable : block) { + variablesInBlock.push_back(variable.getName()); + } + blocks.push_back("[" + boost::algorithm::join(variablesInBlock, ", ") + "]"); + } + + out << "{"; + out << boost::join(blocks, ", "); + out << "}"; + return out; + } + } } } \ No newline at end of file diff --git a/src/storage/prism/menu_games/VariablePartition.h b/src/storage/prism/menu_games/VariablePartition.h index 10b605cf8..46651fe8f 100644 --- a/src/storage/prism/menu_games/VariablePartition.h +++ b/src/storage/prism/menu_games/VariablePartition.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "src/storage/expressions/Variable.h" #include "src/storage/expressions/Expression.h" @@ -103,6 +104,24 @@ namespace storm { */ std::set getRelatedExpressions(std::set const& variables) const; + /*! + * Retrieves the indices of the expressions in which the given variable appears. + * + * @param variable The variable for which to retrieve the expressions. + * @return The indices of all expressions using the given variable. + */ + std::set const& getExpressionsUsingVariable(storm::expressions::Variable const& variable) const; + + /*! + * Retrieves the expression with the given index. + * + * @param expressionIndex The index of the expression to retrieve. + * @return The corresponding expression. + */ + storm::expressions::Expression const& getExpression(uint_fast64_t expressionIndex) const; + + friend std::ostream& operator<<(std::ostream& out, VariablePartition const& partition); + private: /*! * Merges the blocks with the given indices. @@ -130,6 +149,8 @@ namespace storm { std::vector expressions; }; + std::ostream& operator<<(std::ostream& out, VariablePartition const& partition); + } } } From b28f36bb347ba69ca8ee720d304e9fc830bcdb75 Mon Sep 17 00:00:00 2001 From: dehnert Date: Fri, 11 Sep 2015 18:54:02 +0200 Subject: [PATCH 004/400] work on game-based abstraction Former-commit-id: 4635199f84146d2f97f0e395c956948ef38f8e3e --- src/storage/prism/Module.cpp | 11 +++++ src/storage/prism/Module.h | 7 +++ src/storage/prism/Program.cpp | 22 ++++++++- src/storage/prism/Program.h | 7 +++ .../prism/menu_games/AbstractCommand.cpp | 33 ++++++++++++- .../prism/menu_games/AbstractCommand.h | 37 +++++++++++--- .../prism/menu_games/AbstractModule.cpp | 7 ++- src/storage/prism/menu_games/AbstractModule.h | 14 +++--- .../prism/menu_games/AbstractProgram.cpp | 29 +++++++---- .../prism/menu_games/AbstractProgram.h | 31 +++++------- .../menu_games/AbstractionDdInformation.cpp | 16 ++++++ .../menu_games/AbstractionDdInformation.h | 49 +++++++++++++++++++ .../AbstractionExpressionInformation.cpp | 16 ++++++ .../AbstractionExpressionInformation.h | 39 +++++++++++++++ .../prism/menu_games/VariablePartition.cpp | 12 +++++ .../prism/menu_games/VariablePartition.h | 8 +++ 16 files changed, 289 insertions(+), 49 deletions(-) create mode 100644 src/storage/prism/menu_games/AbstractionDdInformation.cpp create mode 100644 src/storage/prism/menu_games/AbstractionDdInformation.h create mode 100644 src/storage/prism/menu_games/AbstractionExpressionInformation.cpp create mode 100644 src/storage/prism/menu_games/AbstractionExpressionInformation.h diff --git a/src/storage/prism/Module.cpp b/src/storage/prism/Module.cpp index 47d44269a..08c8b6dc6 100644 --- a/src/storage/prism/Module.cpp +++ b/src/storage/prism/Module.cpp @@ -43,6 +43,17 @@ namespace storm { return this->integerVariables; } + std::set Module::getAllExpressionVariables() const { + std::set result; + for (auto const& var : this->getBooleanVariables()) { + result.insert(var.getExpressionVariable()); + } + for (auto const& var : this->getIntegerVariables()) { + result.insert(var.getExpressionVariable()); + } + return result; + } + std::size_t Module::getNumberOfCommands() const { return this->commands.size(); } diff --git a/src/storage/prism/Module.h b/src/storage/prism/Module.h index bb8c3b8d4..9c22cd7d1 100644 --- a/src/storage/prism/Module.h +++ b/src/storage/prism/Module.h @@ -97,6 +97,13 @@ namespace storm { */ std::vector const& getIntegerVariables() const; + /*! + * Retrieves the set of all expression variables declared in this module. + * + * @return The set of all expression variables. + */ + std::set getAllExpressionVariables() const; + /*! * Retrieves the number of commands of this module. * diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 9ac73881e..a0ce6132a 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -194,6 +194,26 @@ namespace storm { std::vector const& Program::getGlobalIntegerVariables() const { return this->globalIntegerVariables; } + + std::set Program::getAllExpressionVariables() const { + std::set result; + + for (auto const& constant : constants) { + result.insert(constant.getExpressionVariable()); + } + for (auto const& variable : globalBooleanVariables) { + result.insert(variable.getExpressionVariable()); + } + for (auto const& variable : globalIntegerVariables) { + result.insert(variable.getExpressionVariable()); + } + for (auto const& module : modules) { + auto const& moduleVariables = module.getAllExpressionVariables(); + result.insert(moduleVariables.begin(), moduleVariables.end()); + } + + return result; + } bool Program::globalBooleanVariableExists(std::string const& variableName) const { return this->globalBooleanVariableToIndexMap.count(variableName) > 0; @@ -202,8 +222,6 @@ namespace storm { bool Program::globalIntegerVariableExists(std::string const& variableName) const { return this->globalIntegerVariableToIndexMap.count(variableName) > 0; } - - BooleanVariable const& Program::getGlobalBooleanVariable(std::string const& variableName) const { auto const& nameIndexPair = this->globalBooleanVariableToIndexMap.find(variableName); diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index f2b13781d..de3e18fc9 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -166,6 +166,13 @@ namespace storm { */ IntegerVariable const& getGlobalIntegerVariable(std::string const& variableName) const; + /*! + * Retrieves the set of all expression variables declared in this module. + * + * @return The set of all expression variables. + */ + std::set getAllExpressionVariables() const; + /*! * Retrieves the number of global boolean variables of the program. * diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 558d1e77c..52c344a65 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -1,5 +1,8 @@ #include "src/storage/prism/menu_games/AbstractCommand.h" +#include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" +#include "src/storage/prism/menu_games/AbstractionDdInformation.h" + #include "src/storage/prism/Command.h" #include "src/storage/prism/Update.h" @@ -7,10 +10,38 @@ namespace storm { namespace prism { namespace menu_games { template - AbstractCommand::AbstractCommand(storm::expressions::ExpressionManager& expressionManager, storm::prism::Command const& command, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : expressionManager(expressionManager), smtSolver(smtSolverFactory.create(expressionManager)), predicates(initialPredicates), command(command) { + AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.expressionManager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command) { // Intentionally left empty. } + template + std::pair, std::set> AbstractCommand::computeRelevantPredicates(std::vector const& assignments) const { + std::pair, std::set> result; + + // To start with, all predicates related to the guard are relevant source predicates. + result.first = variablePartition.getExpressionsUsingVariables(command.get().getGuardExpression().getVariables()); + + std::set assignedVariables; + for (auto const& assignment : assignments) { + // Also, variables appearing on the right-hand side of an assignment are relevant for source state. + auto const& rightHandSidePredicates = variablePartition.getExpressionsUsingVariables(assignment.getExpression().getVariables()); + result.first.insert(rightHandSidePredicates.begin(), rightHandSidePredicates.end()); + + // Variables that are being assigned are relevant for the target state. + storm::expressions::Variable const& assignedVariable = assignment.getVariable(); + auto const& leftHandSidePredicates = variablePartition.getExpressionsUsingVariable(assignedVariable); + result.second.insert(leftHandSidePredicates.begin(), leftHandSidePredicates.end()); + + // Keep track of all assigned variables, so we can find the related predicates later. + assignedVariables.insert(assignedVariable); + } + + auto const& predicatesRelatedToAssignedVariable = variablePartition.getRelatedExpressions(assignedVariables); + result.first.insert(predicatesRelatedToAssignedVariable.begin(), predicatesRelatedToAssignedVariable.end()); + + return result; + } + template class AbstractCommand; } } diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index a380bc4f6..1b773f477 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -2,6 +2,9 @@ #define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTCOMMAND_H_ #include +#include + +#include "src/storage/prism/menu_games/VariablePartition.h" #include "src/storage/dd/DdType.h" #include "src/storage/expressions/Expression.h" @@ -11,35 +14,53 @@ namespace storm { namespace prism { - // Forward-declare concrete command and update classes. + // Forward-declare concrete command and assignment classes. class Command; + class Assignment; namespace menu_games { + template + class AbstractionDdInformation; + + class AbstractionExpressionInformation; + template class AbstractCommand { public: /*! * Constructs an abstract command from the given command and the initial predicates. * - * @param expressionManager The manager responsible for the expressions of the command. * @param command The concrete command for which to build the abstraction. - * @param initialPredicates The initial set of predicates. + * @param expressionInformation The expression-related information including the manager and the predicates. + * @param ddInformation The DD-related information including the manager. * @param smtSolverFactory A factory that is to be used for creating new SMT solvers. */ - AbstractCommand(storm::expressions::ExpressionManager& expressionManager, storm::prism::Command const& command, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); private: - // The manager responsible for the expressions of the command and the SMT solvers. - storm::expressions::ExpressionManager& expressionManager; + /*! + * Determines the relevant predicates for source as well as target states. + * + * @param assignments The assignments that are to be considered. + * @return A pair whose first component represents the relevant source predicates and whose second + * component represents the relevant target state predicates. + */ + std::pair, std::set> computeRelevantPredicates(std::vector const& assignments) const; // An SMT responsible for this abstract command. std::unique_ptr smtSolver; + + // The expression-related information. + AbstractionExpressionInformation const& expressionInformation; - // The current set of predicates used in the abstraction. - std::vector predicates; + // The DD-related information. + AbstractionDdInformation const& ddInformation; // The concrete command this abstract command refers to. std::reference_wrapper command; + + // The partition of variables and expressions. + VariablePartition variablePartition; }; } } diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp index 8252d450c..860512da7 100644 --- a/src/storage/prism/menu_games/AbstractModule.cpp +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -1,5 +1,8 @@ #include "src/storage/prism/menu_games/AbstractModule.h" +#include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" +#include "src/storage/prism/menu_games/AbstractionDdInformation.h" + #include "src/storage/prism/Module.h" namespace storm { @@ -7,11 +10,11 @@ namespace storm { namespace menu_games { template - AbstractModule::AbstractModule(storm::expressions::ExpressionManager& expressionManager, storm::prism::Module const& module, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : expressionManager(expressionManager), smtSolverFactory(smtSolverFactory), predicates(initialPredicates), commands(), module(module) { + AbstractModule::AbstractModule(storm::prism::Module const& module, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolverFactory(smtSolverFactory), commands(), module(module) { // For each concrete command, we create an abstract counterpart. for (auto const& command : module.getCommands()) { - commands.emplace_back(expressionManager, command, initialPredicates, smtSolverFactory); + commands.emplace_back(command, expressionInformation, ddInformation, smtSolverFactory); } } diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h index 3d292203d..fc1e002e8 100644 --- a/src/storage/prism/menu_games/AbstractModule.h +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -15,23 +15,25 @@ namespace storm { class Module; namespace menu_games { + template + class AbstractionDdInformation; + + class AbstractionExpressionInformation; + template class AbstractModule { public: /*! * Constructs an abstract module from the given module and the initial predicates. * - * @param expressionManager The manager responsible for the expressions of the command. * @param module The concrete module for which to build the abstraction. - * @param initialPredicates The initial set of predicates. + * @param expressionInformation The expression-related information including the manager and the predicates. + * @param ddInformation The DD-related information including the manager. * @param smtSolverFactory A factory that is to be used for creating new SMT solvers. */ - AbstractModule(storm::expressions::ExpressionManager& expressionManager, storm::prism::Module const& module, std::vector const& initialPredicates, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + AbstractModule(storm::prism::Module const& module, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); private: - // The manager responsible for the expressions of the module and the SMT solvers. - storm::expressions::ExpressionManager& expressionManager; - // A factory that can be used to create new SMT solvers. storm::utility::solver::SmtSolverFactory const& smtSolverFactory; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 801805054..f45a9f8d1 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -7,6 +7,7 @@ #include "src/storage/dd/CuddDdManager.h" #include "src/utility/macros.h" +#include "src/utility/solver.h" #include "src/exceptions/WrongFormatException.h" namespace storm { @@ -14,45 +15,53 @@ namespace storm { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : expressionManager(expressionManager), smtSolverFactory(std::move(smtSolverFactory)), predicates(initialPredicates), ddManager(new storm::dd::DdManager()), predicateDdVariables(), commandDdVariable(), optionDdVariables(), modules(), program(program) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>(new storm::dd::DdManager())), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables()), modules(), program(program) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. STORM_LOG_THROW(program.getNumberOfModules() == 1, storm::exceptions::WrongFormatException, "Cannot create abstract program from program containing too many modules."); + std::set allVariables; + + uint_fast64_t totalNumberOfCommands = 0; + uint_fast64_t maximalUpdateCount = 0; for (auto const& module : program.getModules()) { // If we were requested to add all guards to the set of predicates, we do so now. - if (addAllGuards) { - for (auto const& command : module.getCommands()) { - predicates.push_back(command.getGuardExpression()); + for (auto const& command : module.getCommands()) { + if (addAllGuards) { + expressionInformation.predicates.push_back(command.getGuardExpression()); } + maximalUpdateCount = std::max(maximalUpdateCount, static_cast(command.getNumberOfUpdates())); } - + totalNumberOfCommands += module.getNumberOfCommands(); } // Create DD variables for all predicates. - for (auto const& predicate : predicates) { + for (auto const& predicate : expressionInformation.predicates) { std::stringstream stream; stream << predicate; - predicateDdVariables.push_back(ddManager->addMetaVariable(stream.str())); + ddInformation.predicateDdVariables.push_back(ddInformation.ddManager->addMetaVariable(stream.str())); } // Create DD variable for the command encoding. - commandDdVariable = ddManager->addMetaVariable("command", 0, totalNumberOfCommands - 1); + ddInformation.commandDdVariable = ddInformation.ddManager->addMetaVariable("command", 0, totalNumberOfCommands - 1).first; + + // Create DD variable for update encoding. + ddInformation.updateDdVariable = ddInformation.ddManager->addMetaVariable("update", 0, maximalUpdateCount - 1).first; // Create DD variables encoding the nondeterministic choices of player 2. // NOTE: currently we assume that 100 variables suffice, which corresponds to 2^100 possible choices. // If for some reason this should not be enough, we could grow this vector dynamically, but odds are // that it's impossible to treat such models in any event. for (uint_fast64_t index = 0; index < 100; ++index) { - optionDdVariables.push_back(ddManager->addMetaVariable("opt" + std::to_string(index))); + ddInformation.optionDdVariables.push_back(ddInformation.ddManager->addMetaVariable("opt" + std::to_string(index)).first); } // For each module of the concrete program, we create an abstract counterpart. for (auto const& module : program.getModules()) { - modules.emplace_back(expressionManager, module, predicates, *smtSolverFactory); + modules.emplace_back(module, expressionInformation, ddInformation, *smtSolverFactory); } } diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index e00f4e8f1..1ed11dc16 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -3,16 +3,17 @@ #include "src/storage/dd/DdType.h" +#include "src/storage/prism/menu_games/AbstractionDdInformation.h" +#include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" #include "src/storage/prism/menu_games/AbstractModule.h" #include "src/storage/expressions/Expression.h" -#include "src/utility/solver.h" - namespace storm { - namespace dd { - template - class DdManager; + namespace utility { + namespace solver { + class SmtSolverFactory; + } } namespace prism { @@ -20,6 +21,7 @@ namespace storm { class Program; namespace menu_games { + template class AbstractProgram { public: @@ -35,26 +37,15 @@ namespace storm { AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory = std::unique_ptr(new storm::utility::solver::SmtSolverFactory()), bool addAllGuards = false); private: - // The manager responsible for the expressions of the program and the SMT solvers. - storm::expressions::ExpressionManager& expressionManager; // A factory that can be used to create new SMT solvers. std::unique_ptr smtSolverFactory; - // The current set of predicates used in the abstraction. - std::vector predicates; - - // The manager responsible for the DDs. - std::shared_ptr> ddManager; - - // The DD variables corresponding to the predicates. - std::vector> predicateDdVariables; - - // The DD variable encoding the command (i.e., the nondeterministic choices of player 1). - std::pair commandDdVariable; + // A struct containing all DD-related information like variables and managers. + AbstractionDdInformation ddInformation; - // The DD variables encoding the nondeterministic choices of player 2. - std::vector> optionDdVariables; + // A struct containing all expression-related information like variables, managers and the predicates. + AbstractionExpressionInformation expressionInformation; // The abstract modules of the abstract program. std::vector> modules; diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp new file mode 100644 index 000000000..105126772 --- /dev/null +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -0,0 +1,16 @@ +#include "src/storage/prism/menu_games/AbstractionDdInformation.h" + +#include "src/storage/dd/DdManager.h" + +namespace storm { + namespace prism { + namespace menu_games { + + template + AbstractionDdInformation::AbstractionDdInformation(std::shared_ptr> const& manager) : ddManager(manager) { + // Intentionally left empty. + } + + } + } +} \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h new file mode 100644 index 000000000..9944c7e00 --- /dev/null +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -0,0 +1,49 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTIONDDINFORMATION_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTIONDDINFORMATION_H_ + +#include +#include + +#include "src/storage/dd/DdType.h" +#include "src/storage/expressions/Variable.h" + +namespace storm { + namespace dd { + template + class DdManager; + } + + namespace prism { + namespace menu_games { + + template + struct AbstractionDdInformation { + public: + /*! + * Creates a new DdInformation that uses the given manager. + * + * @param manager The manager to use. + */ + AbstractionDdInformation(std::shared_ptr> const& manager); + + // The manager responsible for the DDs. + std::shared_ptr> ddManager; + + // The DD variables corresponding to the predicates. + std::vector> predicateDdVariables; + + // The DD variable encoding the command (i.e., the nondeterministic choices of player 1). + storm::expressions::Variable commandDdVariable; + + // The DD variable encoding the update IDs for all actions. + storm::expressions::Variable updateDdVariable; + + // The DD variables encoding the nondeterministic choices of player 2. + std::vector optionDdVariables; + }; + + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTIONDDINFORMATION_H_ */ diff --git a/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp b/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp new file mode 100644 index 000000000..9530bd96f --- /dev/null +++ b/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp @@ -0,0 +1,16 @@ +#include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" + +#include "src/storage/expressions/ExpressionManager.h" +#include "src/storage/expressions/Expression.h" + +namespace storm { + namespace prism { + namespace menu_games { + + AbstractionExpressionInformation::AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates, std::set const& variables) : expressionManager(expressionManager), predicates(predicates), variables(variables) { + // Intentionally left empty. + } + + } + } +} \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractionExpressionInformation.h b/src/storage/prism/menu_games/AbstractionExpressionInformation.h new file mode 100644 index 000000000..d56c6c72d --- /dev/null +++ b/src/storage/prism/menu_games/AbstractionExpressionInformation.h @@ -0,0 +1,39 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTIONEXPRESSIONINFORMATION_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTIONEXPRESSIONINFORMATION_H_ + +#include +#include + +namespace storm { + namespace expressions { + class ExpressionManager; + class Expression; + class Variable; + } + + namespace prism { + namespace menu_games { + + struct AbstractionExpressionInformation { + public: + /*! + * Creates an expression information object with the given expression manager. + * + * @param expressionManager The expression manager to use. + */ + AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates = std::vector(), std::set const& variables = std::set()); + + // The manager responsible for the expressions of the program and the SMT solvers. + storm::expressions::ExpressionManager& expressionManager; + + // The current set of predicates used in the abstraction. + std::vector predicates; + + // The set of all variables. + std::set variables; + }; + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTIONEXPRESSIONINFORMATION_H_ */ \ No newline at end of file diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index 53c0d2a61..92738dfb8 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -124,6 +124,18 @@ namespace storm { return this->variableToExpressionsMapping.find(variable)->second; } + std::set VariablePartition::getExpressionsUsingVariables(std::set const& variables) const { + std::set result; + + for (auto const& variable : variables) { + STORM_LOG_ASSERT(this->relevantVariables.find(variable) != this->relevantVariables.end(), "Illegal variable '" << variable.getName() << "' for partition."); + auto it = this->variableToExpressionsMapping.find(variable); + result.insert(it->second.begin(), it->second.end()); + } + + return result; + } + storm::expressions::Expression const& VariablePartition::getExpression(uint_fast64_t expressionIndex) const { return this->expressions[expressionIndex]; } diff --git a/src/storage/prism/menu_games/VariablePartition.h b/src/storage/prism/menu_games/VariablePartition.h index 46651fe8f..ae6637943 100644 --- a/src/storage/prism/menu_games/VariablePartition.h +++ b/src/storage/prism/menu_games/VariablePartition.h @@ -111,6 +111,14 @@ namespace storm { * @return The indices of all expressions using the given variable. */ std::set const& getExpressionsUsingVariable(storm::expressions::Variable const& variable) const; + + /*! + * Retrieves the indices of the expressions in which the given variables appear. + * + * @param variables The variables for which to retrieve the expressions. + * @return The indices of all expressions using the given variables. + */ + std::set getExpressionsUsingVariables(std::set const& variables) const; /*! * Retrieves the expression with the given index. From f013ddfb4c3b73205014e4a99c2a93290ffc3467 Mon Sep 17 00:00:00 2001 From: dehnert Date: Sun, 13 Sep 2015 00:20:20 +0200 Subject: [PATCH 005/400] The determined relevant predicates are now added to the SMT solver of an abstract command. Also, variable bounds are enforced. Former-commit-id: 703b49e732251faec598d46730af109a85ae69e5 --- src/storage/prism/IntegerVariable.cpp | 4 + src/storage/prism/IntegerVariable.h | 7 ++ src/storage/prism/Module.cpp | 8 ++ src/storage/prism/Module.h | 7 ++ src/storage/prism/Program.cpp | 14 +++ src/storage/prism/Program.h | 7 ++ src/storage/prism/Update.cpp | 10 ++ src/storage/prism/Update.h | 8 ++ .../prism/menu_games/AbstractCommand.cpp | 102 +++++++++++++++++- .../prism/menu_games/AbstractCommand.h | 49 ++++++++- .../prism/menu_games/AbstractModule.cpp | 22 +++- src/storage/prism/menu_games/AbstractModule.h | 11 +- .../prism/menu_games/AbstractProgram.cpp | 12 ++- .../prism/menu_games/AbstractProgram.h | 8 +- .../menu_games/AbstractionDdInformation.cpp | 2 + .../AbstractionExpressionInformation.cpp | 2 +- .../AbstractionExpressionInformation.h | 5 +- .../abstraction/PrismMenuGameTest.cpp | 29 +++++ 18 files changed, 294 insertions(+), 13 deletions(-) create mode 100644 test/functional/abstraction/PrismMenuGameTest.cpp diff --git a/src/storage/prism/IntegerVariable.cpp b/src/storage/prism/IntegerVariable.cpp index 8ab2f9678..b2306ece0 100644 --- a/src/storage/prism/IntegerVariable.cpp +++ b/src/storage/prism/IntegerVariable.cpp @@ -14,6 +14,10 @@ namespace storm { return this->upperBoundExpression; } + storm::expressions::Expression IntegerVariable::getRangeExpression() const { + return this->getLowerBoundExpression() <= this->getExpressionVariable() && this->getExpressionVariable() <= this->getUpperBoundExpression(); + } + IntegerVariable IntegerVariable::substitute(std::map const& substitution) const { return IntegerVariable(this->getExpressionVariable(), this->getLowerBoundExpression().substitute(substitution), this->getUpperBoundExpression().substitute(substitution), this->getInitialValueExpression().substitute(substitution), this->getFilename(), this->getLineNumber()); } diff --git a/src/storage/prism/IntegerVariable.h b/src/storage/prism/IntegerVariable.h index 024e1d214..eac97235b 100644 --- a/src/storage/prism/IntegerVariable.h +++ b/src/storage/prism/IntegerVariable.h @@ -45,6 +45,13 @@ namespace storm { */ storm::expressions::Expression const& getUpperBoundExpression() const; + /*! + * Retrieves an expression that characterizes the valid range of the variable. + * + * @return An expression that characterizes the valid range of the variable. + */ + storm::expressions::Expression getRangeExpression() const; + /*! * Substitutes all identifiers in the boolean variable according to the given map. * diff --git a/src/storage/prism/Module.cpp b/src/storage/prism/Module.cpp index 08c8b6dc6..76eb9ff42 100644 --- a/src/storage/prism/Module.cpp +++ b/src/storage/prism/Module.cpp @@ -54,6 +54,14 @@ namespace storm { return result; } + std::vector Module::getAllRangeExpressions() const { + std::vector result; + for (auto const& integerVariable : this->integerVariables) { + result.push_back(integerVariable.getRangeExpression()); + } + return result; + } + std::size_t Module::getNumberOfCommands() const { return this->commands.size(); } diff --git a/src/storage/prism/Module.h b/src/storage/prism/Module.h index 9c22cd7d1..7ea7ee54e 100644 --- a/src/storage/prism/Module.h +++ b/src/storage/prism/Module.h @@ -104,6 +104,13 @@ namespace storm { */ std::set getAllExpressionVariables() const; + /*! + * Retrieves a set of expressions characterizing the legal ranges of all variables declared in the module. + * + * @return The expressions characterizing the legal ranges of all variables declared in the module. + */ + std::vector getAllRangeExpressions() const; + /*! * Retrieves the number of commands of this module. * diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index a0ce6132a..7d1486501 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -215,6 +215,20 @@ namespace storm { return result; } + std::vector Program::getAllRangeExpressions() const { + std::vector result; + for (auto const& globalIntegerVariable : this->globalIntegerVariables) { + result.push_back(globalIntegerVariable.getRangeExpression()); + } + + for (auto const& module : modules) { + std::vector moduleRangeExpressions = module.getAllRangeExpressions(); + result.insert(result.end(), moduleRangeExpressions.begin(), moduleRangeExpressions.end()); + } + + return result; + } + bool Program::globalBooleanVariableExists(std::string const& variableName) const { return this->globalBooleanVariableToIndexMap.count(variableName) > 0; } diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index de3e18fc9..8185472ee 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -173,6 +173,13 @@ namespace storm { */ std::set getAllExpressionVariables() const; + /*! + * Retrieves a set of expressions characterizing the legal ranges of all variables. + * + * @return The expressions characterizing the legal ranges of all variables. + */ + std::vector getAllRangeExpressions() const; + /*! * Retrieves the number of global boolean variables of the program. * diff --git a/src/storage/prism/Update.cpp b/src/storage/prism/Update.cpp index 4c3432fde..811a4a426 100644 --- a/src/storage/prism/Update.cpp +++ b/src/storage/prism/Update.cpp @@ -31,6 +31,16 @@ namespace storm { return this->getAssignments()[variableIndexPair->second]; } + std::map Update::getAsVariableToExpressionMap() const { + std::map result; + + for (auto const& assignment : this->getAssignments()) { + result.emplace(assignment.getVariable(), assignment.getExpression()); + } + + return result; + } + uint_fast64_t Update::getGlobalIndex() const { return this->globalIndex; } diff --git a/src/storage/prism/Update.h b/src/storage/prism/Update.h index 7b9ebdbc1..69e918cd3 100644 --- a/src/storage/prism/Update.h +++ b/src/storage/prism/Update.h @@ -59,6 +59,13 @@ namespace storm { */ storm::prism::Assignment const& getAssignment(std::string const& variableName) const; + /*! + * Creates a mapping representation of this update. + * + * @return A mapping from variables to expressions. + */ + std::map getAsVariableToExpressionMap() const; + /*! * Retrieves the global index of the update, that is, a unique index over all modules. * @@ -73,6 +80,7 @@ namespace storm { * @return The resulting update. */ Update substitute(std::map const& substitution) const; + /*! * Removes all assignments which do not change the variable. * diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 52c344a65..7e6263067 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -3,6 +3,9 @@ #include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" #include "src/storage/prism/menu_games/AbstractionDdInformation.h" +#include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/CuddAdd.h" + #include "src/storage/prism/Command.h" #include "src/storage/prism/Update.h" @@ -10,8 +13,15 @@ namespace storm { namespace prism { namespace menu_games { template - AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.expressionManager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command) { - // Intentionally left empty. + AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.expressionManager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), cachedDd(std::make_pair(ddInformation.ddManager->getAddZero(), 0)) { + + // Make the second component of relevant predicates have the right size. + relevantPredicatesAndVariables.second.resize(command.getNumberOfUpdates()); + + // Assert all range expressions to enforce legal variable values. + for (auto const& rangeExpression : expressionInformation.rangeExpressions) { + smtSolver->add(rangeExpression); + } } template @@ -42,6 +52,94 @@ namespace storm { return result; } + template + std::pair, std::vector>> AbstractCommand::computeRelevantPredicates() const { + std::pair, std::vector>> result; + + for (auto const& update : command.get().getUpdates()) { + std::pair, std::set> relevantUpdatePredicates = computeRelevantPredicates(update.getAssignments()); + result.first.insert(relevantUpdatePredicates.first.begin(), relevantUpdatePredicates.first.end()); + result.second.push_back(relevantUpdatePredicates.second); + } + + return result; + } + + template + bool AbstractCommand::relevantPredicatesChanged(std::pair, std::vector>> const& newRelevantPredicates) const { + if (newRelevantPredicates.first.size() > relevantPredicatesAndVariables.first.size()) { + return true; + } + + for (uint_fast64_t index = 0; index < command.get().getNumberOfUpdates(); ++index) { + if (newRelevantPredicates.second[index].size() > relevantPredicatesAndVariables.second[index].size()) { + return true; + } + } + + return false; + } + + template + std::vector> AbstractCommand::declareNewVariables(std::vector> const& oldRelevantPredicates, std::set const& newRelevantPredicates) { + std::vector> result; + + auto oldIt = oldRelevantPredicates.begin(); + auto oldIte = oldRelevantPredicates.end(); + for (auto newIt = newRelevantPredicates.begin(), newIte = newRelevantPredicates.end(); newIt != newIte; ++newIt) { + // If the new variable does not yet exist as a source variable, we create it now. + if (oldIt == oldIte || oldIt->second != *newIt) { + result.push_back(std::make_pair(expressionInformation.expressionManager.declareFreshBooleanVariable(), *newIt)); + } + } + + return result; + } + + template + void AbstractCommand::addMissingPredicates(std::pair, std::vector>> const& newRelevantPredicates) { + // Determine and add new relevant source predicates. + std::vector> newSourceVariables = declareNewVariables(relevantPredicatesAndVariables.first, newRelevantPredicates.first); + for (auto const& element : newSourceVariables) { + smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second])); + } + + // Insert the new variables into the record of relevant source variables. + relevantPredicatesAndVariables.first.insert(relevantPredicatesAndVariables.first.end(), newSourceVariables.begin(), newSourceVariables.end()); + std::sort(relevantPredicatesAndVariables.first.begin(), relevantPredicatesAndVariables.first.end(), [] (std::pair const& first, std::pair const& second) { return first.second < second.second; } ); + + // Do the same for every update. + for (uint_fast64_t index = 0; index < command.get().getNumberOfUpdates(); ++index) { + std::vector> newTargetVariables = declareNewVariables(relevantPredicatesAndVariables.second[index], newRelevantPredicates.second[index]); + for (auto const& element : newSourceVariables) { + smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second].substitute(command.get().getUpdate(index).getAsVariableToExpressionMap()))); + } + + relevantPredicatesAndVariables.second[index].insert(relevantPredicatesAndVariables.second[index].end(), newTargetVariables.begin(), newTargetVariables.end()); + std::sort(relevantPredicatesAndVariables.second[index].begin(), relevantPredicatesAndVariables.second[index].end(), [] (std::pair const& first, std::pair const& second) { return first.second < second.second; } ); + } + } + + template + std::pair, uint_fast64_t> AbstractCommand::computeDd() { + // First, we check whether there is work to be done by recomputing the relevant predicates and checking + // whether they changed. + std::pair, std::vector>> newRelevantPredicates; + bool recomputeDd = this->relevantPredicatesChanged(newRelevantPredicates); + + if (recomputeDd) { + relevantPredicates = std::move(newRelevantPredicates); + + + + storm::dd::Add result; + + return result; + } else { + return cachedDd; + } + } + template class AbstractCommand; } } diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index 1b773f477..eedf28b03 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -37,9 +37,18 @@ namespace storm { */ AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + /*! + * Computes the abstraction of the command wrt. to the current set of predicates. + * + * @return The abstraction of the command in the form of an ADD together with the number of DD variables + * used to encode the choices of player 2. + */ + std::pair, uint_fast64_t> computeDd(); + private: /*! - * Determines the relevant predicates for source as well as target states. + * Determines the relevant predicates for source as well as target states wrt. to the given assignments + * (that, for example, form an update). * * @param assignments The assignments that are to be considered. * @return A pair whose first component represents the relevant source predicates and whose second @@ -47,6 +56,37 @@ namespace storm { */ std::pair, std::set> computeRelevantPredicates(std::vector const& assignments) const; + /*! + * Determines the relevant predicates for source as well as target states. + * + * @return A pair whose first component represents the relevant source predicates and whose second + * component represents the relevant target state predicates. + */ + std::pair, std::vector>> computeRelevantPredicates() const; + + /*! + * Checks whether the relevant predicates changed. + * + * @param newRelevantPredicates The new relevant predicates. + */ + bool relevantPredicatesChanged(std::pair, std::vector>> const& newRelevantPredicates) const; + + /*! + * Declares variables for the predicates that were added. + * + * @param oldRelevantPredicates The old relevant predicates (and the corresponding variables). + * @return Pairs of variable and predicate (indices) for the new relevant predicates. + */ + std::vector> declareNewVariables(std::vector> const& oldRelevantPredicates, std::set const& newRelevantPredicates); + + /*! + * Takes the new relevant predicates and creates the appropriate variables and assertions for the ones + * that are currently missing. + * + * @param newRelevantPredicates The new relevant predicates. + */ + void addMissingPredicates(std::pair, std::vector>> const& newRelevantPredicates); + // An SMT responsible for this abstract command. std::unique_ptr smtSolver; @@ -61,6 +101,13 @@ namespace storm { // The partition of variables and expressions. VariablePartition variablePartition; + + // The currently relevant source/target predicates and the corresponding variables. + std::pair>, std::vector>>> relevantPredicatesAndVariables; + + // The most recent result of a call to computeDd. If nothing has changed regarding the relevant + // predicates, this result may be reused. + std::pair, uint_fast64_t> cachedDd; }; } } diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp index 860512da7..ae1ddedd6 100644 --- a/src/storage/prism/menu_games/AbstractModule.cpp +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -3,6 +3,9 @@ #include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" #include "src/storage/prism/menu_games/AbstractionDdInformation.h" +#include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/CuddAdd.h" + #include "src/storage/prism/Module.h" namespace storm { @@ -10,7 +13,7 @@ namespace storm { namespace menu_games { template - AbstractModule::AbstractModule(storm::prism::Module const& module, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolverFactory(smtSolverFactory), commands(), module(module) { + AbstractModule::AbstractModule(storm::prism::Module const& module, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolverFactory(smtSolverFactory), ddInformation(ddInformation), commands(), module(module) { // For each concrete command, we create an abstract counterpart. for (auto const& command : module.getCommands()) { @@ -18,6 +21,23 @@ namespace storm { } } + template + storm::dd::Add AbstractModule::computeDd() { + // First, we retrieve the abstractions of all commands. + std::vector, uint_fast64_t>> commandDdsAndUsedOptionVariableCounts; + for (auto const& command : commands) { + commandDdsAndUsedOptionVariableCounts.push_back(command.computeDd()); + } + + // Then, we build the module ADD by adding the single command DDs. We need to make sure that all command + // DDs use the same amount DD variable encoding the choices of player 2. + storm::dd::Add result = ddInformation.ddManager->getAddZero(); + + // TODO + + return result; + } + template class AbstractModule; } } diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h index fc1e002e8..6114267b7 100644 --- a/src/storage/prism/menu_games/AbstractModule.h +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -33,12 +33,19 @@ namespace storm { */ AbstractModule(storm::prism::Module const& module, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + /*! + * Computes the abstraction of the module wrt. to the current set of predicates. + * + * @return The abstraction of the module in the form of an ADD. + */ + storm::dd::Add computeDd(); + private: // A factory that can be used to create new SMT solvers. storm::utility::solver::SmtSolverFactory const& smtSolverFactory; - // The current set of predicates used in the abstraction. - std::vector predicates; + // The DD-related information. + AbstractionDdInformation const& ddInformation; // The abstract commands of the abstract module. std::vector> commands; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index f45a9f8d1..42b09be50 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -5,6 +5,7 @@ #include "src/storage/prism/Program.h" #include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/CuddAdd.h" #include "src/utility/macros.h" #include "src/utility/solver.h" @@ -15,15 +16,12 @@ namespace storm { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>(new storm::dd::DdManager())), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables()), modules(), program(program) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. STORM_LOG_THROW(program.getNumberOfModules() == 1, storm::exceptions::WrongFormatException, "Cannot create abstract program from program containing too many modules."); - std::set allVariables; - - uint_fast64_t totalNumberOfCommands = 0; uint_fast64_t maximalUpdateCount = 0; for (auto const& module : program.getModules()) { @@ -65,6 +63,12 @@ namespace storm { } } + template + storm::dd::Add AbstractProgram::computeDd() { + // As long as there is only one module, we build its game representation and return it. + return modules.front().computeDd(); + } + // Explicitly instantiate the class. template class AbstractProgram; diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 1ed11dc16..1c59be4d3 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -36,8 +36,14 @@ namespace storm { */ AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory = std::unique_ptr(new storm::utility::solver::SmtSolverFactory()), bool addAllGuards = false); - private: + /*! + * Uses the current set of predicates to derive the abstract menu game in the form of an ADD. + * + * @return The ADD representing the game. + */ + storm::dd::Add computeDd(); + private: // A factory that can be used to create new SMT solvers. std::unique_ptr smtSolverFactory; diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index 105126772..ebb1790b5 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -10,6 +10,8 @@ namespace storm { AbstractionDdInformation::AbstractionDdInformation(std::shared_ptr> const& manager) : ddManager(manager) { // Intentionally left empty. } + + template class AbstractionDdInformation; } } diff --git a/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp b/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp index 9530bd96f..b3b927d70 100644 --- a/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp @@ -7,7 +7,7 @@ namespace storm { namespace prism { namespace menu_games { - AbstractionExpressionInformation::AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates, std::set const& variables) : expressionManager(expressionManager), predicates(predicates), variables(variables) { + AbstractionExpressionInformation::AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates, std::set const& variables, std::vector const& rangeExpressions) : expressionManager(expressionManager), predicates(predicates), variables(variables), rangeExpressions(rangeExpressions) { // Intentionally left empty. } diff --git a/src/storage/prism/menu_games/AbstractionExpressionInformation.h b/src/storage/prism/menu_games/AbstractionExpressionInformation.h index d56c6c72d..0e03b4d72 100644 --- a/src/storage/prism/menu_games/AbstractionExpressionInformation.h +++ b/src/storage/prism/menu_games/AbstractionExpressionInformation.h @@ -21,7 +21,7 @@ namespace storm { * * @param expressionManager The expression manager to use. */ - AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates = std::vector(), std::set const& variables = std::set()); + AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates = std::vector(), std::set const& variables = std::set(), std::vector const& rangeExpressions = std::vector()); // The manager responsible for the expressions of the program and the SMT solvers. storm::expressions::ExpressionManager& expressionManager; @@ -31,6 +31,9 @@ namespace storm { // The set of all variables. std::set variables; + + // The expression characterizing the legal ranges of all variables. + std::vector rangeExpressions; }; } } diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp new file mode 100644 index 000000000..16027bf3f --- /dev/null +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -0,0 +1,29 @@ +#include "gtest/gtest.h" +#include "storm-config.h" + +#ifdef STORM_HAVE_MSAT + +#include "src/parser/PrismParser.h" + +#include "src/storage/prism/menu_games/AbstractProgram.h" + +#include "src/storage/expressions/Expression.h" + +#include "src/storage/dd/CuddDd.h" +#include "src/storage/dd/CuddAdd.h" +#include "src/storage/dd/CuddBdd.h" + +#include "src/utility/solver.h" + +TEST(PrismMenuGame, CommandAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s") < manager.integer(3)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); +} + +#endif \ No newline at end of file From 1198951c3efa19db8a3098ddf086d0d80ca2b7e7 Mon Sep 17 00:00:00 2001 From: dehnert Date: Tue, 15 Sep 2015 14:10:23 +0200 Subject: [PATCH 006/400] more work on game abstraction of PRISM programs Former-commit-id: b5bec829e2a1fabb7dfc5f2fc78a6d54a0faeb57 --- src/storage/dd/CuddAdd.h | 14 +++ src/storage/dd/CuddBdd.h | 18 ++- .../prism/menu_games/AbstractCommand.cpp | 110 +++++++++++++++--- .../prism/menu_games/AbstractCommand.h | 37 ++++-- .../prism/menu_games/AbstractModule.cpp | 8 +- src/storage/prism/menu_games/AbstractModule.h | 4 +- .../prism/menu_games/AbstractProgram.cpp | 16 ++- .../menu_games/AbstractionDdInformation.cpp | 36 +++++- .../menu_games/AbstractionDdInformation.h | 34 +++++- 9 files changed, 231 insertions(+), 46 deletions(-) diff --git a/src/storage/dd/CuddAdd.h b/src/storage/dd/CuddAdd.h index acdc3d5e1..b7d9fbfcb 100644 --- a/src/storage/dd/CuddAdd.h +++ b/src/storage/dd/CuddAdd.h @@ -2,6 +2,7 @@ #define STORM_STORAGE_DD_CUDDADD_H_ #include +#include #include #include "src/storage/dd/Add.h" @@ -35,6 +36,8 @@ namespace storm { friend class Bdd; friend class Odd; + // Declare the hashing struct for DDs as a friend so it can access the internal DD pointer. + friend struct std::hash>; /*! * Creates an ADD from the given explicit vector. * @@ -825,4 +828,15 @@ namespace storm { } } +namespace std { + template <> struct hash> + { + size_t operator()(storm::dd::Add const& dd) const { + std::size_t seed = 0; + boost::hash_combine(seed, dd.getCuddAdd().getNode()); + return seed; + } + }; +} + #endif /* STORM_STORAGE_DD_CUDDADD_H_ */ \ No newline at end of file diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h index 3ce4f9d89..d9af17483 100644 --- a/src/storage/dd/CuddBdd.h +++ b/src/storage/dd/CuddBdd.h @@ -1,6 +1,8 @@ #ifndef STORM_STORAGE_DD_CUDDBDD_H_ #define STORM_STORAGE_DD_CUDDBDD_H_ +#include + #include "src/storage/dd/Bdd.h" #include "src/storage/dd/CuddDd.h" #include "src/utility/OsDetection.h" @@ -21,8 +23,6 @@ namespace storm { class BitVector; } - - namespace dd { // Forward-declare some classes. template class DdManager; @@ -51,6 +51,9 @@ namespace storm { friend class Add; friend class Odd; + // Declare the hashing struct for DDs as a friend so it can access the internal DD pointer. + friend struct std::hash>; + // Instantiate all copy/move constructors/assignments with the default implementation. Bdd() = default; Bdd(Bdd const& other) = default; @@ -357,4 +360,15 @@ namespace storm { } } +namespace std { + template <> struct hash> + { + size_t operator()(storm::dd::Bdd const& dd) const { + std::size_t seed = 0; + boost::hash_combine(seed, dd.getCuddBdd().getNode()); + return seed; + } + }; +} + #endif /* STORM_STORAGE_DD_CUDDBDD_H_ */ \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 7e6263067..6f01b1cae 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -1,5 +1,7 @@ #include "src/storage/prism/menu_games/AbstractCommand.h" +#include + #include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" #include "src/storage/prism/menu_games/AbstractionDdInformation.h" @@ -13,7 +15,7 @@ namespace storm { namespace prism { namespace menu_games { template - AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.expressionManager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), cachedDd(std::make_pair(ddInformation.ddManager->getAddZero(), 0)) { + AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.expressionManager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), cachedDd(std::make_pair(ddInformation.manager->getBddZero(), 0)), decisionVariables() { // Make the second component of relevant predicates have the right size. relevantPredicatesAndVariables.second.resize(command.getNumberOfUpdates()); @@ -37,7 +39,7 @@ namespace storm { auto const& rightHandSidePredicates = variablePartition.getExpressionsUsingVariables(assignment.getExpression().getVariables()); result.first.insert(rightHandSidePredicates.begin(), rightHandSidePredicates.end()); - // Variables that are being assigned are relevant for the target state. + // Variables that are being assigned are relevant for the successor state. storm::expressions::Variable const& assignedVariable = assignment.getVariable(); auto const& leftHandSidePredicates = variablePartition.getExpressionsUsingVariable(assignedVariable); result.second.insert(leftHandSidePredicates.begin(), leftHandSidePredicates.end()); @@ -102,6 +104,7 @@ namespace storm { std::vector> newSourceVariables = declareNewVariables(relevantPredicatesAndVariables.first, newRelevantPredicates.first); for (auto const& element : newSourceVariables) { smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second])); + decisionVariables.push_back(element.first); } // Insert the new variables into the record of relevant source variables. @@ -110,34 +113,109 @@ namespace storm { // Do the same for every update. for (uint_fast64_t index = 0; index < command.get().getNumberOfUpdates(); ++index) { - std::vector> newTargetVariables = declareNewVariables(relevantPredicatesAndVariables.second[index], newRelevantPredicates.second[index]); - for (auto const& element : newSourceVariables) { + std::vector> newSuccessorVariables = declareNewVariables(relevantPredicatesAndVariables.second[index], newRelevantPredicates.second[index]); + for (auto const& element : newSuccessorVariables) { smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second].substitute(command.get().getUpdate(index).getAsVariableToExpressionMap()))); + decisionVariables.push_back(element.first); } - relevantPredicatesAndVariables.second[index].insert(relevantPredicatesAndVariables.second[index].end(), newTargetVariables.begin(), newTargetVariables.end()); + relevantPredicatesAndVariables.second[index].insert(relevantPredicatesAndVariables.second[index].end(), newSuccessorVariables.begin(), newSuccessorVariables.end()); std::sort(relevantPredicatesAndVariables.second[index].begin(), relevantPredicatesAndVariables.second[index].end(), [] (std::pair const& first, std::pair const& second) { return first.second < second.second; } ); } } template - std::pair, uint_fast64_t> AbstractCommand::computeDd() { - // First, we check whether there is work to be done by recomputing the relevant predicates and checking - // whether they changed. - std::pair, std::vector>> newRelevantPredicates; - bool recomputeDd = this->relevantPredicatesChanged(newRelevantPredicates); + storm::dd::Bdd AbstractCommand::getSourceStateBdd(storm::solver::SmtSolver::ModelReference const& model) const { + storm::dd::Bdd result = ddInformation.manager->getBddOne(); + for (auto const& variableIndexPair : relevantPredicatesAndVariables.first) { + if (model.getBooleanValue(variableIndexPair.first)) { + result &= ddInformation.predicateBdds[variableIndexPair.second].first; + } else { + result &= !ddInformation.predicateBdds[variableIndexPair.second].first; + } + } + return result; + } + + template + storm::dd::Bdd AbstractCommand::getDistributionBdd(storm::solver::SmtSolver::ModelReference const& model) const { + storm::dd::Bdd result = ddInformation.manager->getBddZero(); - if (recomputeDd) { - relevantPredicates = std::move(newRelevantPredicates); - + for (uint_fast64_t updateIndex = 0; updateIndex < command.get().getNumberOfUpdates(); ++updateIndex) { + storm::dd::Bdd updateBdd = ddInformation.manager->getBddOne(); + + for (auto const& variableIndexPair : relevantPredicatesAndVariables.second[updateIndex]) { + if (model.getBooleanValue(variableIndexPair.first)) { + updateBdd &= ddInformation.predicateBdds[variableIndexPair.second].second; + } else { + updateBdd &= !ddInformation.predicateBdds[variableIndexPair.second].second; + } + updateBdd &= ddInformation.manager->getEncoding(ddInformation.updateDdVariable, updateIndex); + } + // Compute the identities that are missing for this update. + auto firstIt = relevantPredicatesAndVariables.first.begin(); + auto firstIte = relevantPredicatesAndVariables.first.end(); + auto secondIt = relevantPredicatesAndVariables.second[updateIndex].begin(); + auto secondIte = relevantPredicatesAndVariables.second[updateIndex].end(); - storm::dd::Add result; + for (; firstIt != firstIte; ++firstIt) { + if (secondIt == secondIte || firstIt->second != secondIt->second) { + result &= ddInformation.predicateIdentities[firstIt->second]; + } else if (secondIt != secondIte) { + ++secondIt; + } + } - return result; - } else { + result |= updateBdd; + } + + return result; + } + + template + std::pair, uint_fast64_t> AbstractCommand::computeDd() { + // First, we check whether there is work to be done by recomputing the relevant predicates and checking + // whether they changed. + std::pair, std::vector>> newRelevantPredicates = this->computeRelevantPredicates(); + + // If the DD does not need recomputation, we can return the cached result. + bool recomputeDd = this->relevantPredicatesChanged(newRelevantPredicates); + if (!recomputeDd) { + // FIXME: multiply identity of new predicates return cachedDd; } + + // If the DD needs recomputation, it is because of new relevant predicates, so we need to assert the appropriate clauses in the solver. + addMissingPredicates(newRelevantPredicates); + + // Create a mapping from source state DDs to their distributions. + std::unordered_map, std::vector>> sourceToDistributionsMap; + smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); return true; } ); + + // Now we search for the maximal number of choices of player 2 to determine how many DD variables we + // need to encode the nondeterminism. + uint_fast64_t maximalNumberOfChoices = 0; + for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { + maximalNumberOfChoices = std::max(maximalNumberOfChoices, static_cast(sourceDistributionsPair.second.size())); + } + + uint_fast64_t numberOfVariablesNeeded = static_cast(std::ceil(std::log2(maximalNumberOfChoices))); + + // Finally, build overall result. + storm::dd::Bdd resultBdd = ddInformation.manager->getBddZero(); + for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { + uint_fast64_t distributionIndex = 0; + storm::dd::Bdd allDistributions = ddInformation.manager->getBddZero(); + for (auto const& distribution : sourceDistributionsPair.second) { + allDistributions |= distribution && ddInformation.encodeDistributionIndex(numberOfVariablesNeeded, distributionIndex); + } + resultBdd |= sourceDistributionsPair.first && allDistributions; + } + + // Cache the result before returning it. + cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); + return cachedDd; } template class AbstractCommand; diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index eedf28b03..72a08ace6 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -2,7 +2,9 @@ #define STORM_STORAGE_PRISM_MENU_GAMES_ABSTRACTCOMMAND_H_ #include +#include #include +#include #include "src/storage/prism/menu_games/VariablePartition.h" @@ -40,27 +42,27 @@ namespace storm { /*! * Computes the abstraction of the command wrt. to the current set of predicates. * - * @return The abstraction of the command in the form of an ADD together with the number of DD variables + * @return The abstraction of the command in the form of a BDD together with the number of DD variables * used to encode the choices of player 2. */ - std::pair, uint_fast64_t> computeDd(); + std::pair, uint_fast64_t> computeDd(); private: /*! - * Determines the relevant predicates for source as well as target states wrt. to the given assignments + * Determines the relevant predicates for source as well as successor states wrt. to the given assignments * (that, for example, form an update). * * @param assignments The assignments that are to be considered. * @return A pair whose first component represents the relevant source predicates and whose second - * component represents the relevant target state predicates. + * component represents the relevant successor state predicates. */ std::pair, std::set> computeRelevantPredicates(std::vector const& assignments) const; /*! - * Determines the relevant predicates for source as well as target states. + * Determines the relevant predicates for source as well as successor states. * * @return A pair whose first component represents the relevant source predicates and whose second - * component represents the relevant target state predicates. + * component represents the relevant successor state predicates. */ std::pair, std::vector>> computeRelevantPredicates() const; @@ -87,6 +89,22 @@ namespace storm { */ void addMissingPredicates(std::pair, std::vector>> const& newRelevantPredicates); + /*! + * Translates the given model to a source state DD. + * + * @param model The model to translate. + * @return The source state encoded as a DD. + */ + storm::dd::Bdd getSourceStateBdd(storm::solver::SmtSolver::ModelReference const& model) const; + + /*! + * Translates the given model to a distribution over successor states. + * + * @param model The model to translate. + * @return The source state encoded as a DD. + */ + storm::dd::Bdd getDistributionBdd(storm::solver::SmtSolver::ModelReference const& model) const; + // An SMT responsible for this abstract command. std::unique_ptr smtSolver; @@ -102,12 +120,15 @@ namespace storm { // The partition of variables and expressions. VariablePartition variablePartition; - // The currently relevant source/target predicates and the corresponding variables. + // The currently relevant source/successor predicates and the corresponding variables. std::pair>, std::vector>>> relevantPredicatesAndVariables; // The most recent result of a call to computeDd. If nothing has changed regarding the relevant // predicates, this result may be reused. - std::pair, uint_fast64_t> cachedDd; + std::pair, uint_fast64_t> cachedDd; + + // All relevant decision variables over which to perform AllSat. + std::vector decisionVariables; }; } } diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp index ae1ddedd6..63afe243f 100644 --- a/src/storage/prism/menu_games/AbstractModule.cpp +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -22,16 +22,16 @@ namespace storm { } template - storm::dd::Add AbstractModule::computeDd() { + storm::dd::Bdd AbstractModule::computeDd() { // First, we retrieve the abstractions of all commands. - std::vector, uint_fast64_t>> commandDdsAndUsedOptionVariableCounts; - for (auto const& command : commands) { + std::vector, uint_fast64_t>> commandDdsAndUsedOptionVariableCounts; + for (auto& command : commands) { commandDdsAndUsedOptionVariableCounts.push_back(command.computeDd()); } // Then, we build the module ADD by adding the single command DDs. We need to make sure that all command // DDs use the same amount DD variable encoding the choices of player 2. - storm::dd::Add result = ddInformation.ddManager->getAddZero(); + storm::dd::Bdd result = ddInformation.manager->getBddZero(); // TODO diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h index 6114267b7..96ed4cbfd 100644 --- a/src/storage/prism/menu_games/AbstractModule.h +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -36,9 +36,9 @@ namespace storm { /*! * Computes the abstraction of the module wrt. to the current set of predicates. * - * @return The abstraction of the module in the form of an ADD. + * @return The abstraction of the module in the form of a BDD. */ - storm::dd::Add computeDd(); + storm::dd::Bdd computeDd(); private: // A factory that can be used to create new SMT solvers. diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 42b09be50..9e28539a8 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -1,7 +1,5 @@ #include "src/storage/prism/menu_games/AbstractProgram.h" -#include - #include "src/storage/prism/Program.h" #include "src/storage/dd/CuddDdManager.h" @@ -38,23 +36,22 @@ namespace storm { // Create DD variables for all predicates. for (auto const& predicate : expressionInformation.predicates) { - std::stringstream stream; - stream << predicate; - ddInformation.predicateDdVariables.push_back(ddInformation.ddManager->addMetaVariable(stream.str())); + ddInformation.addPredicate(predicate); } // Create DD variable for the command encoding. - ddInformation.commandDdVariable = ddInformation.ddManager->addMetaVariable("command", 0, totalNumberOfCommands - 1).first; + ddInformation.commandDdVariable = ddInformation.manager->addMetaVariable("command", 0, totalNumberOfCommands - 1).first; // Create DD variable for update encoding. - ddInformation.updateDdVariable = ddInformation.ddManager->addMetaVariable("update", 0, maximalUpdateCount - 1).first; + ddInformation.updateDdVariable = ddInformation.manager->addMetaVariable("update", 0, maximalUpdateCount - 1).first; // Create DD variables encoding the nondeterministic choices of player 2. // NOTE: currently we assume that 100 variables suffice, which corresponds to 2^100 possible choices. // If for some reason this should not be enough, we could grow this vector dynamically, but odds are // that it's impossible to treat such models in any event. for (uint_fast64_t index = 0; index < 100; ++index) { - ddInformation.optionDdVariables.push_back(ddInformation.ddManager->addMetaVariable("opt" + std::to_string(index)).first); + storm::expressions::Variable newOptionVar = ddInformation.manager->addMetaVariable("opt" + std::to_string(index)).first; + ddInformation.optionDdVariables.push_back(std::make_pair(newOptionVar, ddInformation.manager->getRange(newOptionVar))); } // For each module of the concrete program, we create an abstract counterpart. @@ -66,7 +63,8 @@ namespace storm { template storm::dd::Add AbstractProgram::computeDd() { // As long as there is only one module, we build its game representation and return it. - return modules.front().computeDd(); + // FIXME: multiply with probabilities for updates. + return modules.front().computeDd().toAdd(); } // Explicitly instantiate the class. diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index ebb1790b5..f85831006 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -1,17 +1,47 @@ #include "src/storage/prism/menu_games/AbstractionDdInformation.h" -#include "src/storage/dd/DdManager.h" +#include + +#include "src/storage/expressions/Expression.h" + +#include "src/storage/dd/CuddDdManager.h" +#include "src/storage/dd/CuddBdd.h" +#include "src/storage/dd/CuddAdd.h" namespace storm { namespace prism { namespace menu_games { template - AbstractionDdInformation::AbstractionDdInformation(std::shared_ptr> const& manager) : ddManager(manager) { + AbstractionDdInformation::AbstractionDdInformation(std::shared_ptr> const& manager) : manager(manager) { // Intentionally left empty. } + + template + storm::dd::Bdd AbstractionDdInformation::encodeDistributionIndex(uint_fast64_t numberOfVariables, uint_fast64_t distributionIndex) const { + storm::dd::Bdd result = manager->getBddOne(); + for (uint_fast64_t bitIndex = 0; bitIndex < numberOfVariables; ++bitIndex) { + if ((distributionIndex & 1) != 0) { + result &= optionDdVariables[bitIndex].second; + } else { + result &= !optionDdVariables[bitIndex].second; + } + distributionIndex >>= 1; + } + return result; + } + + template + void AbstractionDdInformation::addPredicate(storm::expressions::Expression const& predicate) { + std::stringstream stream; + stream << predicate; + std::pair newMetaVariable = manager->addMetaVariable(stream.str()); + predicateDdVariables.push_back(newMetaVariable); + predicateBdds.emplace_back(manager->getRange(newMetaVariable.first), manager->getRange(newMetaVariable.second)); + predicateIdentities.push_back(manager->getIdentity(newMetaVariable.first).equals(manager->getIdentity(newMetaVariable.second)).toBdd()); + } - template class AbstractionDdInformation; + template struct AbstractionDdInformation; } } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index 9944c7e00..388a5bc8b 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -11,6 +11,13 @@ namespace storm { namespace dd { template class DdManager; + + template + class Bdd; + } + + namespace expressions { + class Expression; } namespace prism { @@ -26,11 +33,34 @@ namespace storm { */ AbstractionDdInformation(std::shared_ptr> const& manager); + /*! + * Encodes the given distribution index by using the given number of variables from the optionDdVariables + * vector. + * + * @param numberOfVariables The number of variables to use. + * @param distributionIndex The distribution index to encode. + * @return The encoded distribution index. + */ + storm::dd::Bdd encodeDistributionIndex(uint_fast64_t numberOfVariables, uint_fast64_t distributionIndex) const; + + /*! + * Adds the given predicate and creates all associated ressources. + * + * @param predicate The predicate to add. + */ + void addPredicate(storm::expressions::Expression const& predicate); + // The manager responsible for the DDs. - std::shared_ptr> ddManager; + std::shared_ptr> manager; // The DD variables corresponding to the predicates. std::vector> predicateDdVariables; + + // The BDDs corresponding to the predicates. + std::vector, storm::dd::Bdd>> predicateBdds; + + // The BDDs representing the predicate identities (i.e. source and successor variable have the same truth value). + std::vector> predicateIdentities; // The DD variable encoding the command (i.e., the nondeterministic choices of player 1). storm::expressions::Variable commandDdVariable; @@ -39,7 +69,7 @@ namespace storm { storm::expressions::Variable updateDdVariable; // The DD variables encoding the nondeterministic choices of player 2. - std::vector optionDdVariables; + std::vector>> optionDdVariables; }; } From c6f1cb40d322520c4617dcbd664d544e0205e5d1 Mon Sep 17 00:00:00 2001 From: dehnert Date: Tue, 15 Sep 2015 18:00:08 +0200 Subject: [PATCH 007/400] more work on games Former-commit-id: d89f025da4caa15ac9d834d5294a199cf6d17018 --- .../prism/menu_games/AbstractCommand.cpp | 136 ++++++++++++------ .../prism/menu_games/AbstractCommand.h | 22 ++- .../prism/menu_games/AbstractModule.cpp | 21 ++- src/storage/prism/menu_games/AbstractModule.h | 9 +- .../prism/menu_games/AbstractProgram.cpp | 8 +- .../prism/menu_games/AbstractProgram.h | 2 +- .../menu_games/AbstractionDdInformation.cpp | 13 +- .../menu_games/AbstractionDdInformation.h | 9 ++ .../prism/menu_games/VariablePartition.cpp | 10 +- .../abstraction/PrismMenuGameTest.cpp | 3 + 10 files changed, 179 insertions(+), 54 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 6f01b1cae..ef5011b6c 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -11,6 +11,8 @@ #include "src/storage/prism/Command.h" #include "src/storage/prism/Update.h" +#include "src/utility/macros.h" + namespace storm { namespace prism { namespace menu_games { @@ -24,6 +26,80 @@ namespace storm { for (auto const& rangeExpression : expressionInformation.rangeExpressions) { smtSolver->add(rangeExpression); } + + // Assert the guard of the command. + smtSolver->add(command.getGuardExpression()); + + // Refine the command based on all initial predicates. + std::vector allPredicateIndices(expressionInformation.predicates.size()); + for (auto index = 0; index < expressionInformation.predicates.size(); ++index) { + allPredicateIndices[index] = index; + } + this->refine(allPredicateIndices); + } + + template + void AbstractCommand::refine(std::vector const& predicates) { + // Add all predicates to the variable partition. + for (auto predicateIndex : predicates) { + variablePartition.addExpression(expressionInformation.predicates[predicateIndex]); + } + + STORM_LOG_TRACE("Current variable partition is: " << variablePartition); + + // Next, we check whether there is work to be done by recomputing the relevant predicates and checking + // whether they changed. + std::pair, std::vector>> newRelevantPredicates = this->computeRelevantPredicates(); + + // If the DD does not need recomputation, we can return the cached result. + bool recomputeDd = this->relevantPredicatesChanged(newRelevantPredicates); + if (!recomputeDd) { + // If the new predicates are unrelated to the BDD of this command, we need to multiply their identities. + for (auto predicateIndex : predicates) { + cachedDd.first &= ddInformation.predicateIdentities[predicateIndex]; + } + } else { + // If the DD needs recomputation, it is because of new relevant predicates, so we need to assert the appropriate clauses in the solver. + addMissingPredicates(newRelevantPredicates); + + // Finally recompute the cached BDD. + this->recomputeCachedBdd(); + } + } + + template + void AbstractCommand::recomputeCachedBdd() { + STORM_LOG_TRACE("Recomputing BDD for command " << command.get()); + + // Create a mapping from source state DDs to their distributions. + std::unordered_map, std::vector>> sourceToDistributionsMap; + smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); return true; } ); + + // Now we search for the maximal number of choices of player 2 to determine how many DD variables we + // need to encode the nondeterminism. + uint_fast64_t maximalNumberOfChoices = 0; + for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { + maximalNumberOfChoices = std::max(maximalNumberOfChoices, static_cast(sourceDistributionsPair.second.size())); + } + + uint_fast64_t numberOfVariablesNeeded = static_cast(std::ceil(std::log2(maximalNumberOfChoices))); + + // Finally, build overall result. + storm::dd::Bdd resultBdd = ddInformation.manager->getBddZero(); + for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { + uint_fast64_t distributionIndex = 0; + storm::dd::Bdd allDistributions = ddInformation.manager->getBddZero(); + for (auto const& distribution : sourceDistributionsPair.second) { + allDistributions |= distribution && ddInformation.encodeDistributionIndex(numberOfVariablesNeeded, distributionIndex); + } + resultBdd |= sourceDistributionsPair.first && allDistributions; + } + + resultBdd &= computeMissingSourceStateIdentities(); + resultBdd &= ddInformation.manager->getEncoding(ddInformation.commandDdVariable, command.get().getGlobalIndex()); + + // Cache the result before returning it. + cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); } template @@ -126,6 +202,7 @@ namespace storm { template storm::dd::Bdd AbstractCommand::getSourceStateBdd(storm::solver::SmtSolver::ModelReference const& model) const { + STORM_LOG_TRACE("Building source state BDD."); storm::dd::Bdd result = ddInformation.manager->getBddOne(); for (auto const& variableIndexPair : relevantPredicatesAndVariables.first) { if (model.getBooleanValue(variableIndexPair.first)) { @@ -139,11 +216,13 @@ namespace storm { template storm::dd::Bdd AbstractCommand::getDistributionBdd(storm::solver::SmtSolver::ModelReference const& model) const { + STORM_LOG_TRACE("Building distribution BDD."); storm::dd::Bdd result = ddInformation.manager->getBddZero(); for (uint_fast64_t updateIndex = 0; updateIndex < command.get().getNumberOfUpdates(); ++updateIndex) { storm::dd::Bdd updateBdd = ddInformation.manager->getBddOne(); + // Translate block variables for this update into a successor block. for (auto const& variableIndexPair : relevantPredicatesAndVariables.second[updateIndex]) { if (model.getBooleanValue(variableIndexPair.first)) { updateBdd &= ddInformation.predicateBdds[variableIndexPair.second].second; @@ -159,9 +238,12 @@ namespace storm { auto secondIt = relevantPredicatesAndVariables.second[updateIndex].begin(); auto secondIte = relevantPredicatesAndVariables.second[updateIndex].end(); + // Go through all relevant source predicates. This is guaranteed to be a superset of the set of + // relevant successor predicates for any update. for (; firstIt != firstIte; ++firstIt) { + // If the predicates do not match, there is a predicate missing, so we need to add its identity. if (secondIt == secondIte || firstIt->second != secondIt->second) { - result &= ddInformation.predicateIdentities[firstIt->second]; + updateBdd &= ddInformation.predicateIdentities[firstIt->second]; } else if (secondIt != secondIte) { ++secondIt; } @@ -172,49 +254,23 @@ namespace storm { return result; } - + template - std::pair, uint_fast64_t> AbstractCommand::computeDd() { - // First, we check whether there is work to be done by recomputing the relevant predicates and checking - // whether they changed. - std::pair, std::vector>> newRelevantPredicates = this->computeRelevantPredicates(); - - // If the DD does not need recomputation, we can return the cached result. - bool recomputeDd = this->relevantPredicatesChanged(newRelevantPredicates); - if (!recomputeDd) { - // FIXME: multiply identity of new predicates - return cachedDd; - } - - // If the DD needs recomputation, it is because of new relevant predicates, so we need to assert the appropriate clauses in the solver. - addMissingPredicates(newRelevantPredicates); - - // Create a mapping from source state DDs to their distributions. - std::unordered_map, std::vector>> sourceToDistributionsMap; - smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); return true; } ); - - // Now we search for the maximal number of choices of player 2 to determine how many DD variables we - // need to encode the nondeterminism. - uint_fast64_t maximalNumberOfChoices = 0; - for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { - maximalNumberOfChoices = std::max(maximalNumberOfChoices, static_cast(sourceDistributionsPair.second.size())); - } - - uint_fast64_t numberOfVariablesNeeded = static_cast(std::ceil(std::log2(maximalNumberOfChoices))); + storm::dd::Bdd AbstractCommand::computeMissingSourceStateIdentities() const { + auto relevantIt = relevantPredicatesAndVariables.first.begin(); + auto relevantIte = relevantPredicatesAndVariables.first.end(); - // Finally, build overall result. - storm::dd::Bdd resultBdd = ddInformation.manager->getBddZero(); - for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { - uint_fast64_t distributionIndex = 0; - storm::dd::Bdd allDistributions = ddInformation.manager->getBddZero(); - for (auto const& distribution : sourceDistributionsPair.second) { - allDistributions |= distribution && ddInformation.encodeDistributionIndex(numberOfVariablesNeeded, distributionIndex); + storm::dd::Bdd result = ddInformation.manager->getBddOne(); + for (uint_fast64_t predicateIndex = 0; predicateIndex < expressionInformation.predicates.size(); ++predicateIndex) { + if (relevantIt == relevantIte || relevantIt->second != predicateIndex) { + result &= ddInformation.predicateIdentities[predicateIndex]; } - resultBdd |= sourceDistributionsPair.first && allDistributions; } - - // Cache the result before returning it. - cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); + return result; + } + + template + std::pair, uint_fast64_t> AbstractCommand::getAbstractBdd() { return cachedDd; } diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index 72a08ace6..4402bb4bf 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -39,13 +39,20 @@ namespace storm { */ AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + /*! + * Refines the abstract command with the given predicates. + * + * @param predicates The new predicates. + */ + void refine(std::vector const& predicates); + /*! * Computes the abstraction of the command wrt. to the current set of predicates. * * @return The abstraction of the command in the form of a BDD together with the number of DD variables * used to encode the choices of player 2. */ - std::pair, uint_fast64_t> computeDd(); + std::pair, uint_fast64_t> getAbstractBdd(); private: /*! @@ -105,6 +112,19 @@ namespace storm { */ storm::dd::Bdd getDistributionBdd(storm::solver::SmtSolver::ModelReference const& model) const; + /*! + * Recomputes the cached BDD. This needs to be triggered if any relevant predicates change. + */ + void recomputeCachedBdd(); + + /*! + * Computes the missing source state identities. + * + * @return A BDD that represents the source state identities for predicates that are irrelevant for the + * source states. + */ + storm::dd::Bdd computeMissingSourceStateIdentities() const; + // An SMT responsible for this abstract command. std::unique_ptr smtSolver; diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp index 63afe243f..5da411997 100644 --- a/src/storage/prism/menu_games/AbstractModule.cpp +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -22,19 +22,28 @@ namespace storm { } template - storm::dd::Bdd AbstractModule::computeDd() { + void AbstractModule::refine(std::vector const& predicates) { + for (auto& command : commands) { + command.refine(predicates); + } + } + + template + storm::dd::Bdd AbstractModule::getAbstractBdd() { // First, we retrieve the abstractions of all commands. std::vector, uint_fast64_t>> commandDdsAndUsedOptionVariableCounts; + uint_fast64_t maximalNumberOfUsedOptionVariables = 0; for (auto& command : commands) { - commandDdsAndUsedOptionVariableCounts.push_back(command.computeDd()); + commandDdsAndUsedOptionVariableCounts.push_back(command.getAbstractBdd()); + maximalNumberOfUsedOptionVariables = std::max(maximalNumberOfUsedOptionVariables, commandDdsAndUsedOptionVariableCounts.back().second); } - // Then, we build the module ADD by adding the single command DDs. We need to make sure that all command + // Then, we build the module BDD by adding the single command DDs. We need to make sure that all command // DDs use the same amount DD variable encoding the choices of player 2. storm::dd::Bdd result = ddInformation.manager->getBddZero(); - - // TODO - + for (auto const& commandDd : commandDdsAndUsedOptionVariableCounts) { + result |= commandDd.first && ddInformation.getMissingOptionVariableCube(commandDd.second, maximalNumberOfUsedOptionVariables); + } return result; } diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h index 96ed4cbfd..77101e3d7 100644 --- a/src/storage/prism/menu_games/AbstractModule.h +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -33,12 +33,19 @@ namespace storm { */ AbstractModule(storm::prism::Module const& module, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + /*! + * Refines the abstract module with the given predicates. + * + * @param predicates The new predicate indices. + */ + void refine(std::vector const& predicates); + /*! * Computes the abstraction of the module wrt. to the current set of predicates. * * @return The abstraction of the module in the form of a BDD. */ - storm::dd::Bdd computeDd(); + storm::dd::Bdd getAbstractBdd(); private: // A factory that can be used to create new SMT solvers. diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 9e28539a8..d978ef9b6 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -56,15 +56,17 @@ namespace storm { // For each module of the concrete program, we create an abstract counterpart. for (auto const& module : program.getModules()) { - modules.emplace_back(module, expressionInformation, ddInformation, *smtSolverFactory); + modules.emplace_back(module, expressionInformation, ddInformation, *this->smtSolverFactory); } } template - storm::dd::Add AbstractProgram::computeDd() { + storm::dd::Add AbstractProgram::getAbstractAdd() { // As long as there is only one module, we build its game representation and return it. + + // FIXME: multiply with probabilities for updates. - return modules.front().computeDd().toAdd(); + return modules.front().getAbstractBdd().toAdd(); } // Explicitly instantiate the class. diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 1c59be4d3..0da48b672 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -41,7 +41,7 @@ namespace storm { * * @return The ADD representing the game. */ - storm::dd::Add computeDd(); + storm::dd::Add getAbstractAdd(); private: // A factory that can be used to create new SMT solvers. diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index f85831006..7946115fb 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -37,10 +37,21 @@ namespace storm { stream << predicate; std::pair newMetaVariable = manager->addMetaVariable(stream.str()); predicateDdVariables.push_back(newMetaVariable); - predicateBdds.emplace_back(manager->getRange(newMetaVariable.first), manager->getRange(newMetaVariable.second)); + predicateBdds.emplace_back(manager->getEncoding(newMetaVariable.first, 1), manager->getEncoding(newMetaVariable.second, 1)); predicateIdentities.push_back(manager->getIdentity(newMetaVariable.first).equals(manager->getIdentity(newMetaVariable.second)).toBdd()); } + template + storm::dd::Bdd AbstractionDdInformation::getMissingOptionVariableCube(uint_fast64_t lastUsed, uint_fast64_t lastToBe) const { + storm::dd::Bdd result = manager->getBddOne(); + + for (uint_fast64_t index = lastUsed + 1; index <= lastToBe; ++index) { + result &= optionDdVariables[index].second; + } + + return result; + } + template struct AbstractionDdInformation; } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index 388a5bc8b..b0b239107 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -50,6 +50,15 @@ namespace storm { */ void addPredicate(storm::expressions::Expression const& predicate); + /*! + * Retrieves the cube of option variables in the range (lastUsed, lastToBe] the given indices. + * + * @param lastUsed The last variable before the range to return. + * @param lastToBe The last variable of the range to return. + * @return The cube of variables in the given range. + */ + storm::dd::Bdd getMissingOptionVariableCube(uint_fast64_t lastUsed, uint_fast64_t lastToBe) const; + // The manager responsible for the DDs. std::shared_ptr> manager; diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index 92738dfb8..a8705e599 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -7,7 +7,15 @@ namespace storm { namespace prism { namespace menu_games { - VariablePartition::VariablePartition(std::set const& relevantVariables, std::vector const& expressions) : relevantVariables(relevantVariables), expressionBlocks(expressions.size()) { + VariablePartition::VariablePartition(std::set const& relevantVariables, std::vector const& expressions) : relevantVariables(relevantVariables), expressionBlocks(relevantVariables.size()) { + // Assign each variable to a new block. + uint_fast64_t currentBlock = 0; + for (auto const& variable : relevantVariables) { + this->variableToBlockMapping[variable] = currentBlock; + this->variableToExpressionsMapping[variable] = std::set(); + } + + // Add all expressions, which might relate some variables. for (auto const& expression : expressions) { this->addExpression(expression); } diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 16027bf3f..76321e237 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -24,6 +24,9 @@ TEST(PrismMenuGame, CommandAbstractionTest) { initialPredicates.push_back(manager.getVariableExpression("s") < manager.integer(3)); storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction = abstractProgram.getAbstractAdd(); + abstraction.exportToDot("abstr.dot"); } #endif \ No newline at end of file From 97c90d5437f50875576e985b8fd72573098619ab Mon Sep 17 00:00:00 2001 From: dehnert Date: Tue, 15 Sep 2015 22:25:38 +0200 Subject: [PATCH 008/400] added correct insertion of probabilities into BDD and reachability analysis Former-commit-id: 51e91d4d6450d97b887b3e7cf429f581a3907b8a --- .../prism/menu_games/AbstractCommand.cpp | 10 ++++++++++ .../prism/menu_games/AbstractCommand.h | 7 +++++++ .../prism/menu_games/AbstractModule.cpp | 9 +++++++++ src/storage/prism/menu_games/AbstractModule.h | 7 +++++++ .../prism/menu_games/AbstractProgram.cpp | 19 ++++++++++++++++--- .../prism/menu_games/AbstractProgram.h | 13 +++++++++++++ .../menu_games/AbstractionDdInformation.cpp | 2 ++ .../menu_games/AbstractionDdInformation.h | 7 +++++++ 8 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index ef5011b6c..4e8c2abc2 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -274,6 +274,16 @@ namespace storm { return cachedDd; } + template + storm::dd::Add AbstractCommand::getCommandUpdateProbabilitiesAdd() const { + storm::dd::Add result = ddInformation.manager->getAddZero(); + for (uint_fast64_t updateIndex = 0; updateIndex < command.get().getNumberOfUpdates(); ++updateIndex) { + result += ddInformation.manager->getEncoding(ddInformation.updateDdVariable, updateIndex).toAdd() * ddInformation.manager->getConstant(command.get().getUpdate(updateIndex).getLikelihoodExpression().evaluateAsDouble()); + } + result *= ddInformation.manager->getEncoding(ddInformation.commandDdVariable, command.get().getGlobalIndex()).toAdd(); + return result; + } + template class AbstractCommand; } } diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index 4402bb4bf..5d8765053 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -54,6 +54,13 @@ namespace storm { */ std::pair, uint_fast64_t> getAbstractBdd(); + /*! + * Retrieves an ADD that maps the encoding of the command and its updates to their probabilities. + * + * @return The command-update probability ADD. + */ + storm::dd::Add getCommandUpdateProbabilitiesAdd() const; + private: /*! * Determines the relevant predicates for source as well as successor states wrt. to the given assignments diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp index 5da411997..2a6fde0b2 100644 --- a/src/storage/prism/menu_games/AbstractModule.cpp +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -47,6 +47,15 @@ namespace storm { return result; } + template + storm::dd::Add AbstractModule::getCommandUpdateProbabilitiesAdd() const { + storm::dd::Add result = ddInformation.manager->getAddZero(); + for (auto const& command : commands) { + result += command.getCommandUpdateProbabilitiesAdd(); + } + return result; + } + template class AbstractModule; } } diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h index 77101e3d7..b345613bf 100644 --- a/src/storage/prism/menu_games/AbstractModule.h +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -47,6 +47,13 @@ namespace storm { */ storm::dd::Bdd getAbstractBdd(); + /*! + * Retrieves an ADD that maps the encodings of commands and their updates to their probabilities. + * + * @return The command-update probability ADD. + */ + storm::dd::Add getCommandUpdateProbabilitiesAdd() const; + private: // A factory that can be used to create new SMT solvers. storm::utility::solver::SmtSolverFactory const& smtSolverFactory; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index d978ef9b6..2fe2e593b 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -58,15 +58,28 @@ namespace storm { for (auto const& module : program.getModules()) { modules.emplace_back(module, expressionInformation, ddInformation, *this->smtSolverFactory); } + + // Finally, retrieve the command-update probability ADD, so we can multiply it with the abstraction BDD later. + commandUpdateProbabilitiesAdd = modules.front().getCommandUpdateProbabilitiesAdd(); } template storm::dd::Add AbstractProgram::getAbstractAdd() { // As long as there is only one module, we build its game representation and return it. + return modules.front().getAbstractBdd().toAdd() * commandUpdateProbabilitiesAdd; + } + + template + storm::dd::Bdd AbstractProgram::getReachableStates(storm::dd::Bdd const& initialStates, storm::dd::Bdd const& transitionRelation) { + storm::dd::Bdd frontier = initialStates; + + storm::dd::Bdd reachableStates = initialStates; + while (!frontier.isZero()) { + frontier = frontier.andExists(transitionRelation, ddInformation.successorVariables); + reachableStates |= frontier; + } - - // FIXME: multiply with probabilities for updates. - return modules.front().getAbstractBdd().toAdd(); + return reachableStates; } // Explicitly instantiate the class. diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 0da48b672..801acab62 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -44,6 +44,16 @@ namespace storm { storm::dd::Add getAbstractAdd(); private: + /*! + * Computes the reachable states of the transition relation. + * + * @param initialStates The BDD representing the initial states of the model. + * @param transitionRelation The BDD representing the transition relation that does only contain state + * and successor variables. + * @return The BDD representing the reachable states. + */ + storm::dd::Bdd getReachableStates(storm::dd::Bdd const& initialStates, storm::dd::Bdd const& transitionRelation); + // A factory that can be used to create new SMT solvers. std::unique_ptr smtSolverFactory; @@ -58,6 +68,9 @@ namespace storm { // The concrete program this abstract program refers to. std::reference_wrapper program; + + // An ADD characterizing the probabilities of commands and their updates. + storm::dd::Add commandUpdateProbabilitiesAdd; }; } } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index 7946115fb..01a673d96 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -39,6 +39,8 @@ namespace storm { predicateDdVariables.push_back(newMetaVariable); predicateBdds.emplace_back(manager->getEncoding(newMetaVariable.first, 1), manager->getEncoding(newMetaVariable.second, 1)); predicateIdentities.push_back(manager->getIdentity(newMetaVariable.first).equals(manager->getIdentity(newMetaVariable.second)).toBdd()); + sourceVariables.insert(newMetaVariable.first); + successorVariables.insert(newMetaVariable.second); } template diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index b0b239107..b013589a8 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -3,6 +3,7 @@ #include #include +#include #include "src/storage/dd/DdType.h" #include "src/storage/expressions/Variable.h" @@ -64,6 +65,12 @@ namespace storm { // The DD variables corresponding to the predicates. std::vector> predicateDdVariables; + + // The set of all source variables. + std::set sourceVariables; + + // The set of all source variables. + std::set successorVariables; // The BDDs corresponding to the predicates. std::vector, storm::dd::Bdd>> predicateBdds; From 75632f932d3790da25f8b96179f4b885e1b3302f Mon Sep 17 00:00:00 2001 From: dehnert Date: Wed, 16 Sep 2015 15:16:36 +0200 Subject: [PATCH 009/400] added state-set abstractor as a means to, e.g., derive the initial states BDD Former-commit-id: 34257c7196a5411f4ef36debd1dc0bea46493259 --- .../prism/menu_games/AbstractCommand.cpp | 25 +--- .../prism/menu_games/AbstractCommand.h | 29 +++-- .../prism/menu_games/AbstractModule.cpp | 4 +- src/storage/prism/menu_games/AbstractModule.h | 8 +- .../prism/menu_games/AbstractProgram.cpp | 50 +++++++- .../prism/menu_games/AbstractProgram.h | 11 ++ .../menu_games/AbstractionDdInformation.cpp | 17 +++ .../menu_games/AbstractionDdInformation.h | 11 ++ .../AbstractionExpressionInformation.cpp | 2 +- .../AbstractionExpressionInformation.h | 9 +- .../prism/menu_games/StateSetAbstractor.cpp | 114 +++++++++++++++++ .../prism/menu_games/StateSetAbstractor.h | 117 ++++++++++++++++++ .../prism/menu_games/VariablePartition.cpp | 7 +- 13 files changed, 357 insertions(+), 47 deletions(-) create mode 100644 src/storage/prism/menu_games/StateSetAbstractor.cpp create mode 100644 src/storage/prism/menu_games/StateSetAbstractor.h diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 4e8c2abc2..3acf648ec 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -11,13 +11,14 @@ #include "src/storage/prism/Command.h" #include "src/storage/prism/Update.h" +#include "src/utility/solver.h" #include "src/utility/macros.h" namespace storm { namespace prism { namespace menu_games { template - AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.expressionManager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), cachedDd(std::make_pair(ddInformation.manager->getBddZero(), 0)), decisionVariables() { + AbstractCommand::AbstractCommand(storm::prism::Command const& command, AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.manager)), expressionInformation(expressionInformation), ddInformation(ddInformation), command(command), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), cachedDd(std::make_pair(ddInformation.manager->getBddZero(), 0)), decisionVariables() { // Make the second component of relevant predicates have the right size. relevantPredicatesAndVariables.second.resize(command.getNumberOfUpdates()); @@ -98,7 +99,7 @@ namespace storm { resultBdd &= computeMissingSourceStateIdentities(); resultBdd &= ddInformation.manager->getEncoding(ddInformation.commandDdVariable, command.get().getGlobalIndex()); - // Cache the result before returning it. + // Cache the result. cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); } @@ -158,26 +159,10 @@ namespace storm { return false; } - template - std::vector> AbstractCommand::declareNewVariables(std::vector> const& oldRelevantPredicates, std::set const& newRelevantPredicates) { - std::vector> result; - - auto oldIt = oldRelevantPredicates.begin(); - auto oldIte = oldRelevantPredicates.end(); - for (auto newIt = newRelevantPredicates.begin(), newIte = newRelevantPredicates.end(); newIt != newIte; ++newIt) { - // If the new variable does not yet exist as a source variable, we create it now. - if (oldIt == oldIte || oldIt->second != *newIt) { - result.push_back(std::make_pair(expressionInformation.expressionManager.declareFreshBooleanVariable(), *newIt)); - } - } - - return result; - } - template void AbstractCommand::addMissingPredicates(std::pair, std::vector>> const& newRelevantPredicates) { // Determine and add new relevant source predicates. - std::vector> newSourceVariables = declareNewVariables(relevantPredicatesAndVariables.first, newRelevantPredicates.first); + std::vector> newSourceVariables = AbstractionDdInformation::declareNewVariables(expressionInformation.manager, relevantPredicatesAndVariables.first, newRelevantPredicates.first); for (auto const& element : newSourceVariables) { smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second])); decisionVariables.push_back(element.first); @@ -189,7 +174,7 @@ namespace storm { // Do the same for every update. for (uint_fast64_t index = 0; index < command.get().getNumberOfUpdates(); ++index) { - std::vector> newSuccessorVariables = declareNewVariables(relevantPredicatesAndVariables.second[index], newRelevantPredicates.second[index]); + std::vector> newSuccessorVariables = AbstractionDdInformation::declareNewVariables(expressionInformation.manager, relevantPredicatesAndVariables.second[index], newRelevantPredicates.second[index]); for (auto const& element : newSuccessorVariables) { smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second].substitute(command.get().getUpdate(index).getAsVariableToExpressionMap()))); decisionVariables.push_back(element.first); diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index 5d8765053..844231edb 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -12,9 +12,22 @@ #include "src/storage/expressions/Expression.h" #include "src/solver/SmtSolver.h" -#include "src/utility/solver.h" namespace storm { + namespace utility { + namespace solver { + class SmtSolverFactory; + } + } + + namespace dd { + template + class Bdd; + + template + class Add; + } + namespace prism { // Forward-declare concrete command and assignment classes. class Command; @@ -22,9 +35,9 @@ namespace storm { namespace menu_games { template - class AbstractionDdInformation; + struct AbstractionDdInformation; - class AbstractionExpressionInformation; + struct AbstractionExpressionInformation; template class AbstractCommand { @@ -86,15 +99,7 @@ namespace storm { * @param newRelevantPredicates The new relevant predicates. */ bool relevantPredicatesChanged(std::pair, std::vector>> const& newRelevantPredicates) const; - - /*! - * Declares variables for the predicates that were added. - * - * @param oldRelevantPredicates The old relevant predicates (and the corresponding variables). - * @return Pairs of variable and predicate (indices) for the new relevant predicates. - */ - std::vector> declareNewVariables(std::vector> const& oldRelevantPredicates, std::set const& newRelevantPredicates); - + /*! * Takes the new relevant predicates and creates the appropriate variables and assertions for the ones * that are currently missing. diff --git a/src/storage/prism/menu_games/AbstractModule.cpp b/src/storage/prism/menu_games/AbstractModule.cpp index 2a6fde0b2..514af9c2b 100644 --- a/src/storage/prism/menu_games/AbstractModule.cpp +++ b/src/storage/prism/menu_games/AbstractModule.cpp @@ -29,7 +29,7 @@ namespace storm { } template - storm::dd::Bdd AbstractModule::getAbstractBdd() { + std::pair, uint_fast64_t> AbstractModule::getAbstractBdd() { // First, we retrieve the abstractions of all commands. std::vector, uint_fast64_t>> commandDdsAndUsedOptionVariableCounts; uint_fast64_t maximalNumberOfUsedOptionVariables = 0; @@ -44,7 +44,7 @@ namespace storm { for (auto const& commandDd : commandDdsAndUsedOptionVariableCounts) { result |= commandDd.first && ddInformation.getMissingOptionVariableCube(commandDd.second, maximalNumberOfUsedOptionVariables); } - return result; + return std::make_pair(result, maximalNumberOfUsedOptionVariables); } template diff --git a/src/storage/prism/menu_games/AbstractModule.h b/src/storage/prism/menu_games/AbstractModule.h index b345613bf..ee0ad5b74 100644 --- a/src/storage/prism/menu_games/AbstractModule.h +++ b/src/storage/prism/menu_games/AbstractModule.h @@ -16,9 +16,9 @@ namespace storm { namespace menu_games { template - class AbstractionDdInformation; + struct AbstractionDdInformation; - class AbstractionExpressionInformation; + struct AbstractionExpressionInformation; template class AbstractModule { @@ -43,9 +43,9 @@ namespace storm { /*! * Computes the abstraction of the module wrt. to the current set of predicates. * - * @return The abstraction of the module in the form of a BDD. + * @return The abstraction of the module in the form of a BDD together with how many option variables were used. */ - storm::dd::Bdd getAbstractBdd(); + std::pair, uint_fast64_t> getAbstractBdd(); /*! * Retrieves an ADD that maps the encodings of commands and their updates to their probabilities. diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 2fe2e593b..a9c0f7aa5 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -14,7 +14,7 @@ namespace storm { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. @@ -59,14 +59,51 @@ namespace storm { modules.emplace_back(module, expressionInformation, ddInformation, *this->smtSolverFactory); } + // Add the initial state expression to the initial state abstractor. + initialStateAbstractor.addPredicate(program.getInitialConstruct().getInitialStatesExpression()); + // Finally, retrieve the command-update probability ADD, so we can multiply it with the abstraction BDD later. commandUpdateProbabilitiesAdd = modules.front().getCommandUpdateProbabilitiesAdd(); } + template + void AbstractProgram::refine(std::vector const& predicates) { + // Add the predicates to the global list of predicates. + uint_fast64_t firstNewPredicateIndex = expressionInformation.predicates.size(); + expressionInformation.predicates.insert(expressionInformation.predicates.end(), predicates.begin(), predicates.end()); + + // Create a list of indices of the predicates, so we can refine the abstract modules and the state set abstractors. + std::vector newPredicateIndices; + for (uint_fast64_t index = firstNewPredicateIndex; expressionInformation.predicates.size(); ++index) { + newPredicateIndices.push_back(index); + } + + // Refine all abstract modules. + for (auto& module : modules) { + module.refine(newPredicateIndices); + } + + // Refine initial state abstractor. + initialStateAbstractor.refine(newPredicateIndices); + } + template storm::dd::Add AbstractProgram::getAbstractAdd() { - // As long as there is only one module, we build its game representation and return it. - return modules.front().getAbstractBdd().toAdd() * commandUpdateProbabilitiesAdd; + // As long as there is only one module, we only build its game representation. + std::pair, uint_fast64_t> gameBdd = modules.front().getAbstractBdd(); + + // Construct a set of all unnecessary variables, so we can abstract from it. + std::set variablesToAbstract = {ddInformation.commandDdVariable, ddInformation.updateDdVariable}; + for (uint_fast64_t index = 0; index < gameBdd.second; ++index) { + variablesToAbstract.insert(ddInformation.optionDdVariables[index].first); + } + + // Do a reachability analysis on the raw transition relation. + storm::dd::Bdd transitionRelation = gameBdd.first.existsAbstract(variablesToAbstract); + storm::dd::Bdd reachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); + + // Construct the final game by cutting away the transitions of unreachable states. + return (gameBdd.first && reachableStates).toAdd() * commandUpdateProbabilitiesAdd; } template @@ -74,9 +111,14 @@ namespace storm { storm::dd::Bdd frontier = initialStates; storm::dd::Bdd reachableStates = initialStates; + uint_fast64_t reachabilityIteration = 0; while (!frontier.isZero()) { - frontier = frontier.andExists(transitionRelation, ddInformation.successorVariables); + ++reachabilityIteration; + frontier = frontier.andExists(transitionRelation, ddInformation.sourceVariables); + frontier = frontier.swapVariables(ddInformation.predicateDdVariables); + frontier &= !reachableStates; reachableStates |= frontier; + STORM_LOG_TRACE("Iteration " << reachabilityIteration << " of reachability analysis."); } return reachableStates; diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 801acab62..d9cf6b4ad 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -5,6 +5,7 @@ #include "src/storage/prism/menu_games/AbstractionDdInformation.h" #include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" +#include "src/storage/prism/menu_games/StateSetAbstractor.h" #include "src/storage/prism/menu_games/AbstractModule.h" #include "src/storage/expressions/Expression.h" @@ -43,6 +44,13 @@ namespace storm { */ storm::dd::Add getAbstractAdd(); + /*! + * Refines the abstract module with the given predicates. + * + * @param predicates The new predicates. + */ + void refine(std::vector const& predicates); + private: /*! * Computes the reachable states of the transition relation. @@ -69,6 +77,9 @@ namespace storm { // The concrete program this abstract program refers to. std::reference_wrapper program; + // A state-set abstractor used to determine the initial states of the abstraction. + StateSetAbstractor initialStateAbstractor; + // An ADD characterizing the probabilities of commands and their updates. storm::dd::Add commandUpdateProbabilitiesAdd; }; diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index 01a673d96..0848ceffb 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -2,6 +2,7 @@ #include +#include "src/storage/expressions/ExpressionManager.h" #include "src/storage/expressions/Expression.h" #include "src/storage/dd/CuddDdManager.h" @@ -54,6 +55,22 @@ namespace storm { return result; } + template + std::vector> AbstractionDdInformation::declareNewVariables(storm::expressions::ExpressionManager& manager, std::vector> const& oldRelevantPredicates, std::set const& newRelevantPredicates) { + std::vector> result; + + auto oldIt = oldRelevantPredicates.begin(); + auto oldIte = oldRelevantPredicates.end(); + for (auto newIt = newRelevantPredicates.begin(), newIte = newRelevantPredicates.end(); newIt != newIte; ++newIt) { + // If the new variable does not yet exist as a source variable, we create it now. + if (oldIt == oldIte || oldIt->second != *newIt) { + result.push_back(std::make_pair(manager.declareFreshBooleanVariable(), *newIt)); + } + } + + return result; + } + template struct AbstractionDdInformation; } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index b013589a8..cc20253da 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -60,6 +60,17 @@ namespace storm { */ storm::dd::Bdd getMissingOptionVariableCube(uint_fast64_t lastUsed, uint_fast64_t lastToBe) const; + /*! + * Examines the old and new relevant predicates and declares decision variables for the missing relevant + * predicates. + * + * @param manager The manager in which to declare the decision variable. + * @param oldRelevantPredicates The previously relevant predicates. + * @param newRelevantPredicates The new relevant predicates. + * @return Pairs of decision variables and their index for the missing predicates. + */ + static std::vector> declareNewVariables(storm::expressions::ExpressionManager& manager, std::vector> const& oldRelevantPredicates, std::set const& newRelevantPredicates); + // The manager responsible for the DDs. std::shared_ptr> manager; diff --git a/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp b/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp index b3b927d70..d82401545 100644 --- a/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionExpressionInformation.cpp @@ -7,7 +7,7 @@ namespace storm { namespace prism { namespace menu_games { - AbstractionExpressionInformation::AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates, std::set const& variables, std::vector const& rangeExpressions) : expressionManager(expressionManager), predicates(predicates), variables(variables), rangeExpressions(rangeExpressions) { + AbstractionExpressionInformation::AbstractionExpressionInformation(storm::expressions::ExpressionManager& manager, std::vector const& predicates, std::set const& variables, std::vector const& rangeExpressions) : manager(manager), predicates(predicates), variables(variables), rangeExpressions(rangeExpressions) { // Intentionally left empty. } diff --git a/src/storage/prism/menu_games/AbstractionExpressionInformation.h b/src/storage/prism/menu_games/AbstractionExpressionInformation.h index 0e03b4d72..b2961b7a1 100644 --- a/src/storage/prism/menu_games/AbstractionExpressionInformation.h +++ b/src/storage/prism/menu_games/AbstractionExpressionInformation.h @@ -19,12 +19,15 @@ namespace storm { /*! * Creates an expression information object with the given expression manager. * - * @param expressionManager The expression manager to use. + * @param manager The expression manager to use. + * @param predicates The initial set of predicates. + * @param variables The variables. + * @param rangeExpressions A set of expressions that enforce the variable bounds. */ - AbstractionExpressionInformation(storm::expressions::ExpressionManager& expressionManager, std::vector const& predicates = std::vector(), std::set const& variables = std::set(), std::vector const& rangeExpressions = std::vector()); + AbstractionExpressionInformation(storm::expressions::ExpressionManager& manager, std::vector const& predicates = std::vector(), std::set const& variables = std::set(), std::vector const& rangeExpressions = std::vector()); // The manager responsible for the expressions of the program and the SMT solvers. - storm::expressions::ExpressionManager& expressionManager; + storm::expressions::ExpressionManager& manager; // The current set of predicates used in the abstraction. std::vector predicates; diff --git a/src/storage/prism/menu_games/StateSetAbstractor.cpp b/src/storage/prism/menu_games/StateSetAbstractor.cpp new file mode 100644 index 000000000..da5219043 --- /dev/null +++ b/src/storage/prism/menu_games/StateSetAbstractor.cpp @@ -0,0 +1,114 @@ +#include "src/storage/prism/menu_games/StateSetAbstractor.h" + +#include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" +#include "src/storage/prism/menu_games/AbstractionDdInformation.h" + +#include "src/storage/dd/CuddDdManager.h" + +#include "src/utility/macros.h" +#include "src/utility/solver.h" + +namespace storm { + namespace prism { + namespace menu_games { + + template + StateSetAbstractor::StateSetAbstractor(AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.manager)), expressionInformation(expressionInformation), ddInformation(ddInformation), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), concretePredicateVariables(), cachedBdd(ddInformation.manager->getBddZero()) { + + // Assert all range expressions to enforce legal variable values. + for (auto const& rangeExpression : expressionInformation.rangeExpressions) { + smtSolver->add(rangeExpression); + } + + // Refine the command based on all initial predicates. + std::vector allPredicateIndices(expressionInformation.predicates.size()); + for (auto index = 0; index < expressionInformation.predicates.size(); ++index) { + allPredicateIndices[index] = index; + } + this->refine(allPredicateIndices); + } + + template + void StateSetAbstractor::addPredicate(storm::expressions::Expression const& predicate) { + smtSolver->add(predicate); + + // Extract the variables of the predicate, so we know which variables were used when abstracting. + std::set usedVariables = predicate.getVariables(); + concretePredicateVariables.insert(usedVariables.begin(), usedVariables.end()); + variablePartition.relate(usedVariables); + + // Since the new predicate might have changed the abstractions, we need to recompute it. + this->refine(); + } + + template + void StateSetAbstractor::addMissingPredicates(std::set const& newRelevantPredicateIndices) { + std::vector> newPredicateVariables = AbstractionDdInformation::declareNewVariables(expressionInformation.manager, relevantPredicatesAndVariables, newRelevantPredicateIndices); + + for (auto const& element : newPredicateVariables) { + smtSolver->add(storm::expressions::iff(element.first, expressionInformation.predicates[element.second])); + decisionVariables.push_back(element.first); + } + + relevantPredicatesAndVariables.insert(relevantPredicatesAndVariables.end(), newPredicateVariables.begin(), newPredicateVariables.end()); + std::sort(relevantPredicatesAndVariables.begin(), relevantPredicatesAndVariables.end(), [] (std::pair const& firstPair, std::pair const& secondPair) { return firstPair.second < secondPair.second; } ); + } + + template + void StateSetAbstractor::refine(std::vector const& newPredicates) { + // Make the partition aware of the new predicates, which may make more predicates relevant to the abstraction. + for (auto const& predicateIndex : newPredicates) { + variablePartition.addExpression(expressionInformation.predicates[predicateIndex]); + } + + // Now check whether we need to recompute the cached BDD. + std::set newRelevantPredicateIndices = variablePartition.getRelatedExpressions(concretePredicateVariables); + STORM_LOG_TRACE("Found " << newRelevantPredicateIndices.size() << " relevant predicates in abstractor."); + + // Since the number of relevant predicates is monotonic, we can simply check for the size here. + STORM_LOG_ASSERT(newRelevantPredicateIndices.size() >= relevantPredicatesAndVariables.size(), "Illegal size of relevant predicates."); + bool recomputeDd = newRelevantPredicateIndices.size() > relevantPredicatesAndVariables.size(); + + if (recomputeDd) { + // If we need to recompute the BDD, we start by introducing decision variables and the corresponding + // constraints in the SMT problem. + addMissingPredicates(newRelevantPredicateIndices); + + // Finally recompute the cached BDD. + this->recomputeCachedBdd(); + } + } + + template + storm::dd::Bdd StateSetAbstractor::getStateBdd(storm::solver::SmtSolver::ModelReference const& model) const { + STORM_LOG_TRACE("Building source state BDD."); + storm::dd::Bdd result = ddInformation.manager->getBddOne(); + for (auto const& variableIndexPair : relevantPredicatesAndVariables) { + if (model.getBooleanValue(variableIndexPair.first)) { + result &= ddInformation.predicateBdds[variableIndexPair.second].first; + } else { + result &= !ddInformation.predicateBdds[variableIndexPair.second].first; + } + } + return result; + } + + template + void StateSetAbstractor::recomputeCachedBdd() { + STORM_LOG_TRACE("Recomputing BDD for state set abstraction."); + + storm::dd::Bdd result = ddInformation.manager->getBddZero(); + smtSolver->allSat(decisionVariables, [&result,this] (storm::solver::SmtSolver::ModelReference const& model) { result |= getStateBdd(model); return true; } ); + + cachedBdd = result; + } + + template + storm::dd::Bdd StateSetAbstractor::getAbstractStates() const { + return cachedBdd; + } + + template class StateSetAbstractor; + } + } +} diff --git a/src/storage/prism/menu_games/StateSetAbstractor.h b/src/storage/prism/menu_games/StateSetAbstractor.h new file mode 100644 index 000000000..7e5b17178 --- /dev/null +++ b/src/storage/prism/menu_games/StateSetAbstractor.h @@ -0,0 +1,117 @@ +#ifndef STORM_STORAGE_PRISM_MENU_GAMES_STATESETABSTRACTOR_H_ +#define STORM_STORAGE_PRISM_MENU_GAMES_STATESETABSTRACTOR_H_ + +#include +#include + +#include "src/storage/dd/DdType.h" + +#include "src/solver/SmtSolver.h" + +#include "src/storage/prism/menu_games/VariablePartition.h" + +namespace storm { + namespace utility { + namespace solver { + class SmtSolverFactory; + } + } + + namespace dd { + template + class Bdd; + + template + class Add; + } + + namespace prism { + namespace menu_games { + template + class AbstractionDdInformation; + + class AbstractionExpressionInformation; + + template + class StateSetAbstractor { + public: + /*! + * Creates a state set abstractor. + * + * @param expressionInformation The expression-related information including the manager and the predicates. + * @param ddInformation The DD-related information including the manager. + * @param smtSolverFactory A factory that can create new SMT solvers. + */ + StateSetAbstractor(AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory); + + /*! + * Adds the given (concrete) predicate to the abstractor and therefore restricts the abstraction to + * abstract states that contain at least some states satisfying the predicate. + */ + void addPredicate(storm::expressions::Expression const& predicate); + + /*! + * Refines the abstractor by making the given predicates new abstract predicates. + * + * @param newPredicateIndices The indices of the new predicates. + */ + void refine(std::vector const& newPredicateIndices = std::vector()); + + /*! + * Retrieves the set of abstract states matching all predicates added to this abstractor. + * + * @return The set of matching abstract states in the form of a BDD + */ + storm::dd::Bdd getAbstractStates() const; + + private: + /*! + * Creates decision variables and the corresponding constraints for the missing predicates. + * + * @param newRelevantPredicateIndices The set of all relevant predicate indices. + */ + void addMissingPredicates(std::set const& newRelevantPredicateIndices); + + /*! + * Recomputes the cached BDD. This needs to be triggered if any relevant predicates change. + */ + void recomputeCachedBdd(); + + /*! + * Translates the given model to a state DD. + * + * @param model The model to translate. + * @return The state encoded as a DD. + */ + storm::dd::Bdd getStateBdd(storm::solver::SmtSolver::ModelReference const& model) const; + + // The SMT solver used for abstracting the set of states. + std::unique_ptr smtSolver; + + // The expression-related information. + AbstractionExpressionInformation const& expressionInformation; + + // The DD-related information. + AbstractionDdInformation const& ddInformation; + + // The partition of the variables. + VariablePartition variablePartition; + + // The set of relevant predicates and the corresponding decision variables. + std::vector> relevantPredicatesAndVariables; + + // The set of all variables appearing in the concrete predicates. + std::set concretePredicateVariables; + + // The set of all decision variables over which to perform the all-sat enumeration. + std::vector decisionVariables; + + // The cached BDD representing the abstraction. This variable is written to in refinement steps (if work + // needed to be done). + storm::dd::Bdd cachedBdd; + }; + } + } +} + +#endif /* STORM_STORAGE_PRISM_MENU_GAMES_STATESETABSTRACTOR_H_ */ diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index a8705e599..a5058db1d 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -27,8 +27,13 @@ namespace storm { for (auto const& variable : expressionVariables) { variableToExpressionsMapping[variable].insert(this->expressions.size()); } + + // Add the expression to the block of the first variable. When relating the variables, the blocks will + // get merged (if necessary). + STORM_LOG_ASSERT(!expressionVariables.empty(), "Found no variables in expression."); + expressionBlocks[getBlockIndexOfVariable(*expressionVariables.begin())].insert(this->expressions.size()); - // Add aexpression and relate all the appearing variables. + // Add expression and relate all the appearing variables. this->expressions.push_back(expression); return this->relate(expressionVariables); } From 88bcd7d74cd1d46e9e38321dfeeeab04c71a2ae5 Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 17 Sep 2015 19:11:36 +0200 Subject: [PATCH 010/400] deadlock states now get fixed in abstract game Former-commit-id: efaa5d007e80d6ba0aa524feb4174cc8a2240d40 --- .../prism/menu_games/AbstractProgram.cpp | 25 ++++++++++++++++--- .../prism/menu_games/AbstractProgram.h | 6 +++++ .../menu_games/AbstractionDdInformation.cpp | 3 ++- .../menu_games/AbstractionDdInformation.h | 3 +++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index a9c0f7aa5..47f617dd0 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -14,7 +14,7 @@ namespace storm { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), lastAbstractBdd(ddInformation.manager->getBddZero()), lastAbstractAdd(ddInformation.manager->getAddZero()) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. @@ -91,6 +91,14 @@ namespace storm { storm::dd::Add AbstractProgram::getAbstractAdd() { // As long as there is only one module, we only build its game representation. std::pair, uint_fast64_t> gameBdd = modules.front().getAbstractBdd(); + + // If the abstraction did not change, we can return the most recenty obtained ADD. + if (gameBdd.first == lastAbstractBdd) { + return lastAbstractAdd; + } + + // Otherwise, we remember that the abstract BDD changed and perform a reachability analysis. + lastAbstractBdd = gameBdd.first; // Construct a set of all unnecessary variables, so we can abstract from it. std::set variablesToAbstract = {ddInformation.commandDdVariable, ddInformation.updateDdVariable}; @@ -99,11 +107,22 @@ namespace storm { } // Do a reachability analysis on the raw transition relation. - storm::dd::Bdd transitionRelation = gameBdd.first.existsAbstract(variablesToAbstract); + storm::dd::Bdd transitionRelation = lastAbstractBdd.existsAbstract(variablesToAbstract); storm::dd::Bdd reachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); + // Find the deadlock states in the model. + storm::dd::Bdd deadlockStates = transitionRelation.existsAbstract(ddInformation.successorVariables); + deadlockStates = reachableStates && !deadlockStates; + + // If there are deadlock states, we fix them now. + storm::dd::Add deadlockTransitions = ddInformation.manager->getAddZero(); + if (!deadlockStates.isZero()) { + deadlockTransitions = (deadlockStates && ddInformation.allPredicateIdentities && ddInformation.manager->getEncoding(ddInformation.commandDdVariable, 0) && ddInformation.manager->getEncoding(ddInformation.updateDdVariable, 0) && ddInformation.getMissingOptionVariableCube(0, gameBdd.second)).toAdd(); + } + // Construct the final game by cutting away the transitions of unreachable states. - return (gameBdd.first && reachableStates).toAdd() * commandUpdateProbabilitiesAdd; + lastAbstractAdd = (lastAbstractBdd && reachableStates).toAdd() * commandUpdateProbabilitiesAdd + deadlockTransitions; + return lastAbstractAdd; } template diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index d9cf6b4ad..88094a632 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -82,6 +82,12 @@ namespace storm { // An ADD characterizing the probabilities of commands and their updates. storm::dd::Add commandUpdateProbabilitiesAdd; + + // A BDD that is the result of the last abstraction of the system. + storm::dd::Bdd lastAbstractBdd; + + // An ADD that is the result of the last abstraction of the system. + storm::dd::Add lastAbstractAdd; }; } } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index 0848ceffb..6395fa951 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -14,7 +14,7 @@ namespace storm { namespace menu_games { template - AbstractionDdInformation::AbstractionDdInformation(std::shared_ptr> const& manager) : manager(manager) { + AbstractionDdInformation::AbstractionDdInformation(std::shared_ptr> const& manager) : manager(manager), allPredicateIdentities(manager->getBddOne()) { // Intentionally left empty. } @@ -40,6 +40,7 @@ namespace storm { predicateDdVariables.push_back(newMetaVariable); predicateBdds.emplace_back(manager->getEncoding(newMetaVariable.first, 1), manager->getEncoding(newMetaVariable.second, 1)); predicateIdentities.push_back(manager->getIdentity(newMetaVariable.first).equals(manager->getIdentity(newMetaVariable.second)).toBdd()); + allPredicateIdentities &= predicateIdentities.back(); sourceVariables.insert(newMetaVariable.first); successorVariables.insert(newMetaVariable.second); } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index cc20253da..ac5fa4316 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -89,6 +89,9 @@ namespace storm { // The BDDs representing the predicate identities (i.e. source and successor variable have the same truth value). std::vector> predicateIdentities; + // A BDD that represents the identity of all predicate variables. + storm::dd::Bdd allPredicateIdentities; + // The DD variable encoding the command (i.e., the nondeterministic choices of player 1). storm::expressions::Variable commandDdVariable; From 8911d2ba63031bbbd4c0fd07a46eec72d9faf35f Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 17 Sep 2015 23:46:45 +0200 Subject: [PATCH 011/400] added debug output and fixed some bugs Former-commit-id: 8d2b7a4dd5266f7968b5ee3684b3db68f26b3686 --- .../prism/menu_games/AbstractCommand.cpp | 12 ++++++++++++ .../prism/menu_games/AbstractProgram.cpp | 19 ++++++++++++++++++- .../abstraction/PrismMenuGameTest.cpp | 7 ++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 3acf648ec..4bd5a3ec4 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -71,6 +71,7 @@ namespace storm { template void AbstractCommand::recomputeCachedBdd() { STORM_LOG_TRACE("Recomputing BDD for command " << command.get()); + std::cout << "recomputing " << command.get() << std::endl; // Create a mapping from source state DDs to their distributions. std::unordered_map, std::vector>> sourceToDistributionsMap; @@ -171,6 +172,10 @@ namespace storm { // Insert the new variables into the record of relevant source variables. relevantPredicatesAndVariables.first.insert(relevantPredicatesAndVariables.first.end(), newSourceVariables.begin(), newSourceVariables.end()); std::sort(relevantPredicatesAndVariables.first.begin(), relevantPredicatesAndVariables.first.end(), [] (std::pair const& first, std::pair const& second) { return first.second < second.second; } ); + std::cout << "sorted!" << std::endl; + for (auto const& el : relevantPredicatesAndVariables.first) { + std::cout << el.first.getName() << " // " << el.second << std::endl; + } // Do the same for every update. for (uint_fast64_t index = 0; index < command.get().getNumberOfUpdates(); ++index) { @@ -190,6 +195,7 @@ namespace storm { STORM_LOG_TRACE("Building source state BDD."); storm::dd::Bdd result = ddInformation.manager->getBddOne(); for (auto const& variableIndexPair : relevantPredicatesAndVariables.first) { + std::cout << "size: " << ddInformation.predicateBdds.size() << " and index " << variableIndexPair.second << std::endl; if (model.getBooleanValue(variableIndexPair.first)) { result &= ddInformation.predicateBdds[variableIndexPair.second].first; } else { @@ -244,11 +250,17 @@ namespace storm { storm::dd::Bdd AbstractCommand::computeMissingSourceStateIdentities() const { auto relevantIt = relevantPredicatesAndVariables.first.begin(); auto relevantIte = relevantPredicatesAndVariables.first.end(); + std::cout << "the size is " << relevantPredicatesAndVariables.first.size() << std::endl; storm::dd::Bdd result = ddInformation.manager->getBddOne(); for (uint_fast64_t predicateIndex = 0; predicateIndex < expressionInformation.predicates.size(); ++predicateIndex) { if (relevantIt == relevantIte || relevantIt->second != predicateIndex) { + std::cout << (relevantIt == relevantIte) << std::endl; + std::cout << relevantIt->second << " vs " << predicateIndex << std::endl; + std::cout << "multiplying identity " << predicateIndex << std::endl; result &= ddInformation.predicateIdentities[predicateIndex]; + } else { + ++relevantIt; } } return result; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 47f617dd0..14a364321 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -68,23 +68,34 @@ namespace storm { template void AbstractProgram::refine(std::vector const& predicates) { + std::cout << "refining!" << std::endl; + // Add the predicates to the global list of predicates. uint_fast64_t firstNewPredicateIndex = expressionInformation.predicates.size(); expressionInformation.predicates.insert(expressionInformation.predicates.end(), predicates.begin(), predicates.end()); + + // Create DD variables and some auxiliary data structures for the new predicates. + for (auto const& predicate : predicates) { + ddInformation.addPredicate(predicate); + } // Create a list of indices of the predicates, so we can refine the abstract modules and the state set abstractors. std::vector newPredicateIndices; - for (uint_fast64_t index = firstNewPredicateIndex; expressionInformation.predicates.size(); ++index) { + for (uint_fast64_t index = firstNewPredicateIndex; index < expressionInformation.predicates.size(); ++index) { newPredicateIndices.push_back(index); } + std::cout << "refining modules" << std::endl; // Refine all abstract modules. for (auto& module : modules) { module.refine(newPredicateIndices); } + std::cout << "refining initial" << std::endl; // Refine initial state abstractor. initialStateAbstractor.refine(newPredicateIndices); + + std::cout << "done " << std::endl; } template @@ -97,6 +108,8 @@ namespace storm { return lastAbstractAdd; } + std::cout << "abstr DD new " << std::endl; + // Otherwise, we remember that the abstract BDD changed and perform a reachability analysis. lastAbstractBdd = gameBdd.first; @@ -106,9 +119,13 @@ namespace storm { variablesToAbstract.insert(ddInformation.optionDdVariables[index].first); } + std::cout << "reachability... " << std::endl; + // Do a reachability analysis on the raw transition relation. storm::dd::Bdd transitionRelation = lastAbstractBdd.existsAbstract(variablesToAbstract); storm::dd::Bdd reachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); + + std::cout << "done " << std::endl; // Find the deadlock states in the model. storm::dd::Bdd deadlockStates = transitionRelation.existsAbstract(ddInformation.successorVariables); diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 76321e237..d3bd2d56a 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -26,7 +26,12 @@ TEST(PrismMenuGame, CommandAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); storm::dd::Add abstraction = abstractProgram.getAbstractAdd(); - abstraction.exportToDot("abstr.dot"); + abstraction.exportToDot("abstr1.dot"); + + abstractProgram.refine({manager.getVariableExpression("s") == manager.integer(7)}); + abstraction = abstractProgram.getAbstractAdd(); + abstraction.exportToDot("abstr2.dot"); + } #endif \ No newline at end of file From 5934d67514f16ff674c4ebeb2b390a1e5074faf2 Mon Sep 17 00:00:00 2001 From: dehnert Date: Fri, 18 Sep 2015 16:06:52 +0200 Subject: [PATCH 012/400] DD meta variables can now be inserted at particular locations. added some tests for game abstraction Former-commit-id: 1c870dc0de8b420f6782325a4b1b65fdf21c2a75 --- src/storage/dd/CuddAdd.cpp | 4 + src/storage/dd/CuddAdd.h | 7 + src/storage/dd/CuddBdd.cpp | 5 +- src/storage/dd/CuddBdd.h | 7 + src/storage/dd/CuddDd.h | 7 + src/storage/dd/CuddDdManager.cpp | 48 +++++- src/storage/dd/CuddDdManager.h | 12 +- src/storage/dd/MetaVariablePosition.h | 15 ++ .../prism/menu_games/AbstractCommand.cpp | 55 +++++-- .../prism/menu_games/AbstractCommand.h | 23 ++- .../prism/menu_games/AbstractProgram.cpp | 24 +-- .../menu_games/AbstractionDdInformation.cpp | 22 ++- .../menu_games/AbstractionDdInformation.h | 8 +- .../abstraction/PrismMenuGameTest.cpp | 155 +++++++++++++++++- 14 files changed, 330 insertions(+), 62 deletions(-) create mode 100644 src/storage/dd/MetaVariablePosition.h diff --git a/src/storage/dd/CuddAdd.cpp b/src/storage/dd/CuddAdd.cpp index 691b500a1..58f17137b 100644 --- a/src/storage/dd/CuddAdd.cpp +++ b/src/storage/dd/CuddAdd.cpp @@ -420,6 +420,10 @@ namespace storm { return static_cast(this->getCuddAdd().NodeReadIndex()); } + uint_fast64_t Add::getLevel() const { + return static_cast(this->getDdManager()->getCuddManager().ReadPerm(this->getIndex())); + } + template std::vector Add::toVector() const { return this->toVector(Odd(*this)); diff --git a/src/storage/dd/CuddAdd.h b/src/storage/dd/CuddAdd.h index b7d9fbfcb..f80422493 100644 --- a/src/storage/dd/CuddAdd.h +++ b/src/storage/dd/CuddAdd.h @@ -520,6 +520,13 @@ namespace storm { */ virtual uint_fast64_t getIndex() const override; + /*! + * Retrieves the level of the topmost variable in the DD. + * + * @return The level of the topmost variable in DD. + */ + virtual uint_fast64_t getLevel() const override; + /*! * Converts the ADD to a vector. * diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp index 3c2ff4ad3..09e59f390 100644 --- a/src/storage/dd/CuddBdd.cpp +++ b/src/storage/dd/CuddBdd.cpp @@ -35,7 +35,6 @@ namespace storm { this->cuddBdd = fromVector(ddManager, explicitValues, odd, metaVariables, std::bind(std::less_equal(), value, std::placeholders::_1)); break; } - } Add Bdd::toAdd() const { @@ -242,6 +241,10 @@ namespace storm { return static_cast(this->getCuddBdd().NodeReadIndex()); } + uint_fast64_t Bdd::getLevel() const { + return static_cast(this->getDdManager()->getCuddManager().ReadPerm(this->getIndex())); + } + void Bdd::exportToDot(std::string const& filename) const { if (filename.empty()) { std::vector cuddBddVector = { this->getCuddBdd() }; diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h index d9af17483..a21402c86 100644 --- a/src/storage/dd/CuddBdd.h +++ b/src/storage/dd/CuddBdd.h @@ -262,6 +262,13 @@ namespace storm { */ virtual uint_fast64_t getIndex() const override; + /*! + * Retrieves the level of the topmost variable in the DD. + * + * @return The level of the topmost variable in DD. + */ + virtual uint_fast64_t getLevel() const override; + /*! * Exports the BDD to the given file in the dot format. * diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h index 0077bcc8a..dbfa5ddae 100644 --- a/src/storage/dd/CuddDd.h +++ b/src/storage/dd/CuddDd.h @@ -68,6 +68,13 @@ namespace storm { */ virtual uint_fast64_t getIndex() const = 0; + /*! + * Retrieves the level of the topmost variable in the DD. + * + * @return The level of the topmost variable in DD. + */ + virtual uint_fast64_t getLevel() const = 0; + /*! * Retrieves whether the given meta variable is contained in the DD. * diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index b5f31e442..b01b12470 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -114,7 +114,7 @@ namespace storm { return result; } - std::pair DdManager::addMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high) { + std::pair DdManager::addMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, boost::optional> const& position) { // Check whether the variable name is legal. STORM_LOG_THROW(name != "" && name.back() != '\'', storm::exceptions::InvalidArgumentException, "Illegal name of meta variable: '" << name << "'."); @@ -126,14 +126,32 @@ namespace storm { std::size_t numberOfBits = static_cast(std::ceil(std::log2(high - low + 1))); + // If a specific position was requested, we compute it now. + boost::optional level; + if (position) { + storm::dd::DdMetaVariable beforeVariable = this->getMetaVariable(position.get().second); + level = position.get().first == MetaVariablePosition::Above ? std::numeric_limits::max() : std::numeric_limits::min(); + for (auto const& ddVariable : beforeVariable.getDdVariables()) { + level = position.get().first == MetaVariablePosition::Above ? std::min(level.get(), ddVariable.getLevel()) : std::max(level.get(), ddVariable.getLevel()); + } + if (position.get().first == MetaVariablePosition::Below) { + ++level.get(); + } + } + storm::expressions::Variable unprimed = manager->declareBitVectorVariable(name, numberOfBits); storm::expressions::Variable primed = manager->declareBitVectorVariable(name + "'", numberOfBits); std::vector> variables; std::vector> variablesPrime; for (std::size_t i = 0; i < numberOfBits; ++i) { - variables.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {unprimed})); - variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {primed})); + if (level) { + variables.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddNewVarAtLevel(level.get() + 2 * i), {unprimed})); + variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddNewVarAtLevel(level.get() + 2 * i + 1), {primed})); + } else { + variables.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {unprimed})); + variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {primed})); + } } // Now group the non-primed and primed variable. @@ -147,20 +165,38 @@ namespace storm { return std::make_pair(unprimed, primed); } - std::pair DdManager::addMetaVariable(std::string const& name) { + std::pair DdManager::addMetaVariable(std::string const& name, boost::optional> const& position) { // Check whether the variable name is legal. STORM_LOG_THROW(name != "" && name.back() != '\'', storm::exceptions::InvalidArgumentException, "Illegal name of meta variable: '" << name << "'."); // Check whether a meta variable already exists. STORM_LOG_THROW(!this->hasMetaVariable(name), storm::exceptions::InvalidArgumentException, "A meta variable '" << name << "' already exists."); + // If a specific position was requested, we compute it now. + boost::optional level; + if (position) { + storm::dd::DdMetaVariable beforeVariable = this->getMetaVariable(position.get().second); + level = position.get().first == MetaVariablePosition::Above ? std::numeric_limits::max() : std::numeric_limits::min(); + for (auto const& ddVariable : beforeVariable.getDdVariables()) { + level = position.get().first == MetaVariablePosition::Above ? std::min(level.get(), ddVariable.getLevel()) : std::max(level.get(), ddVariable.getLevel()); + } + if (position.get().first == MetaVariablePosition::Below) { + ++level.get(); + } + } + storm::expressions::Variable unprimed = manager->declareBooleanVariable(name); storm::expressions::Variable primed = manager->declareBooleanVariable(name + "'"); std::vector> variables; std::vector> variablesPrime; - variables.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {unprimed})); - variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {primed})); + if (position) { + variables.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddNewVarAtLevel(level.get()), {unprimed})); + variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddNewVarAtLevel(level.get() + 1), {primed})); + } else { + variables.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {unprimed})); + variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {primed})); + } // Now group the non-primed and primed variable. this->getCuddManager().MakeTreeNode(variables.front().getIndex(), 2, MTR_FIXED); diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h index ec6a3b57d..e70ea0144 100644 --- a/src/storage/dd/CuddDdManager.h +++ b/src/storage/dd/CuddDdManager.h @@ -3,7 +3,9 @@ #include #include +#include +#include "src/storage/dd/MetaVariablePosition.h" #include "src/storage/dd/DdManager.h" #include "src/storage/dd/CuddDdMetaVariable.h" #include "src/utility/OsDetection.h" @@ -26,7 +28,7 @@ namespace storm { friend class Add; friend class Odd; friend class DdForwardIterator; - + /*! * Creates an empty manager without any meta variables. */ @@ -110,15 +112,19 @@ namespace storm { * @param variableName The name of the new variable. * @param low The lowest value of the range of the variable. * @param high The highest value of the range of the variable. + * @param position A pair indicating the position of the new meta variable. If not given, the meta variable + * will be created below all existing ones. */ - std::pair addMetaVariable(std::string const& variableName, int_fast64_t low, int_fast64_t high); + std::pair addMetaVariable(std::string const& variableName, int_fast64_t low, int_fast64_t high, boost::optional> const& position = boost::none); /*! * Adds a boolean meta variable. * * @param variableName The name of the new variable. + * @param position A pair indicating the position of the new meta variable. If not given, the meta variable + * will be created below all existing ones. */ - std::pair addMetaVariable(std::string const& variableName); + std::pair addMetaVariable(std::string const& variableName, boost::optional> const& position = boost::none); /*! * Retrieves the names of all meta variables that have been added to the manager. diff --git a/src/storage/dd/MetaVariablePosition.h b/src/storage/dd/MetaVariablePosition.h new file mode 100644 index 000000000..5fb9724f2 --- /dev/null +++ b/src/storage/dd/MetaVariablePosition.h @@ -0,0 +1,15 @@ +#ifndef STORM_STORAGE_DD_METAVARIABLEPOSITION_H_ +#define STORM_STORAGE_DD_METAVARIABLEPOSITION_H_ + +#include "src/storage/dd/DdType.h" + +namespace storm { + namespace dd { + + // An enum that expresses the relation of two meta variables relative to each other. + enum class MetaVariablePosition { Above, Below }; + + } +} + +#endif /* STORM_STORAGE_DD_METAVARIABLEPOSITION_H_ */ diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 4bd5a3ec4..3b91eb01e 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -71,11 +71,11 @@ namespace storm { template void AbstractCommand::recomputeCachedBdd() { STORM_LOG_TRACE("Recomputing BDD for command " << command.get()); - std::cout << "recomputing " << command.get() << std::endl; // Create a mapping from source state DDs to their distributions. std::unordered_map, std::vector>> sourceToDistributionsMap; - smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); return true; } ); + uint_fast64_t modelCounter = 0; + smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this,&modelCounter] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); ++modelCounter; return true; } ); // Now we search for the maximal number of choices of player 2 to determine how many DD variables we // need to encode the nondeterminism. @@ -88,17 +88,27 @@ namespace storm { // Finally, build overall result. storm::dd::Bdd resultBdd = ddInformation.manager->getBddZero(); + uint_fast64_t sourceStateIndex = 0; for (auto const& sourceDistributionsPair : sourceToDistributionsMap) { - uint_fast64_t distributionIndex = 0; + STORM_LOG_ASSERT(!sourceDistributionsPair.first.isZero(), "The source BDD must not be empty."); + STORM_LOG_ASSERT(!sourceDistributionsPair.second.empty(), "The distributions must not be empty."); storm::dd::Bdd allDistributions = ddInformation.manager->getBddZero(); + uint_fast64_t distributionIndex = 0; for (auto const& distribution : sourceDistributionsPair.second) { allDistributions |= distribution && ddInformation.encodeDistributionIndex(numberOfVariablesNeeded, distributionIndex); + ++distributionIndex; + STORM_LOG_ASSERT(!allDistributions.isZero(), "The BDD must not be empty."); } resultBdd |= sourceDistributionsPair.first && allDistributions; + ++sourceStateIndex; + STORM_LOG_ASSERT(!resultBdd.isZero(), "The BDD must not be empty."); } - resultBdd &= computeMissingSourceStateIdentities(); + STORM_LOG_ASSERT(sourceToDistributionsMap.empty() || !resultBdd.isZero(), "The BDD must not be empty, if there were distributions."); + resultBdd &= computeMissingIdentities(); + STORM_LOG_ASSERT(sourceToDistributionsMap.empty() || !resultBdd.isZero(), "The BDD must not be empty, if there were distributions."); resultBdd &= ddInformation.manager->getEncoding(ddInformation.commandDdVariable, command.get().getGlobalIndex()); + STORM_LOG_ASSERT(sourceToDistributionsMap.empty() || !resultBdd.isZero(), "The BDD must not be empty, if there were distributions."); // Cache the result. cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); @@ -172,10 +182,6 @@ namespace storm { // Insert the new variables into the record of relevant source variables. relevantPredicatesAndVariables.first.insert(relevantPredicatesAndVariables.first.end(), newSourceVariables.begin(), newSourceVariables.end()); std::sort(relevantPredicatesAndVariables.first.begin(), relevantPredicatesAndVariables.first.end(), [] (std::pair const& first, std::pair const& second) { return first.second < second.second; } ); - std::cout << "sorted!" << std::endl; - for (auto const& el : relevantPredicatesAndVariables.first) { - std::cout << el.first.getName() << " // " << el.second << std::endl; - } // Do the same for every update. for (uint_fast64_t index = 0; index < command.get().getNumberOfUpdates(); ++index) { @@ -195,13 +201,14 @@ namespace storm { STORM_LOG_TRACE("Building source state BDD."); storm::dd::Bdd result = ddInformation.manager->getBddOne(); for (auto const& variableIndexPair : relevantPredicatesAndVariables.first) { - std::cout << "size: " << ddInformation.predicateBdds.size() << " and index " << variableIndexPair.second << std::endl; if (model.getBooleanValue(variableIndexPair.first)) { result &= ddInformation.predicateBdds[variableIndexPair.second].first; } else { result &= !ddInformation.predicateBdds[variableIndexPair.second].first; } } + + STORM_LOG_ASSERT(!result.isZero(), "Source must not be empty."); return result; } @@ -223,6 +230,24 @@ namespace storm { updateBdd &= ddInformation.manager->getEncoding(ddInformation.updateDdVariable, updateIndex); } + result |= updateBdd; + } + + STORM_LOG_ASSERT(!result.isZero(), "Distribution must not be empty."); + return result; + } + + template + storm::dd::Bdd AbstractCommand::computeMissingIdentities() const { + storm::dd::Bdd identities = computeMissingGlobalIdentities(); + identities &= computeMissingUpdateIdentities(); + return identities; + } + + template + storm::dd::Bdd AbstractCommand::computeMissingUpdateIdentities() const { + storm::dd::Bdd result = ddInformation.manager->getBddZero(); + for (uint_fast64_t updateIndex = 0; updateIndex < command.get().getNumberOfUpdates(); ++updateIndex) { // Compute the identities that are missing for this update. auto firstIt = relevantPredicatesAndVariables.first.begin(); auto firstIte = relevantPredicatesAndVariables.first.end(); @@ -231,33 +256,29 @@ namespace storm { // Go through all relevant source predicates. This is guaranteed to be a superset of the set of // relevant successor predicates for any update. + storm::dd::Bdd updateIdentity = ddInformation.manager->getBddOne(); for (; firstIt != firstIte; ++firstIt) { // If the predicates do not match, there is a predicate missing, so we need to add its identity. if (secondIt == secondIte || firstIt->second != secondIt->second) { - updateBdd &= ddInformation.predicateIdentities[firstIt->second]; + updateIdentity &= ddInformation.predicateIdentities[firstIt->second]; } else if (secondIt != secondIte) { ++secondIt; } } - result |= updateBdd; + result |= updateIdentity && ddInformation.manager->getEncoding(ddInformation.updateDdVariable, updateIndex); } - return result; } template - storm::dd::Bdd AbstractCommand::computeMissingSourceStateIdentities() const { + storm::dd::Bdd AbstractCommand::computeMissingGlobalIdentities() const { auto relevantIt = relevantPredicatesAndVariables.first.begin(); auto relevantIte = relevantPredicatesAndVariables.first.end(); - std::cout << "the size is " << relevantPredicatesAndVariables.first.size() << std::endl; storm::dd::Bdd result = ddInformation.manager->getBddOne(); for (uint_fast64_t predicateIndex = 0; predicateIndex < expressionInformation.predicates.size(); ++predicateIndex) { if (relevantIt == relevantIte || relevantIt->second != predicateIndex) { - std::cout << (relevantIt == relevantIte) << std::endl; - std::cout << relevantIt->second << " vs " << predicateIndex << std::endl; - std::cout << "multiplying identity " << predicateIndex << std::endl; result &= ddInformation.predicateIdentities[predicateIndex]; } else { ++relevantIt; diff --git a/src/storage/prism/menu_games/AbstractCommand.h b/src/storage/prism/menu_games/AbstractCommand.h index 844231edb..ecd8ff971 100644 --- a/src/storage/prism/menu_games/AbstractCommand.h +++ b/src/storage/prism/menu_games/AbstractCommand.h @@ -128,14 +128,29 @@ namespace storm { * Recomputes the cached BDD. This needs to be triggered if any relevant predicates change. */ void recomputeCachedBdd(); + + /*! + * Computes the missing state identities. + * + * @return A BDD that represents the all missing state identities. + */ + storm::dd::Bdd computeMissingIdentities() const; + + /*! + * Computes the missing state identities for the updates. + * + * @return A BDD that represents the state identities for predicates that are irrelevant for the + * successor states. + */ + storm::dd::Bdd computeMissingUpdateIdentities() const; /*! - * Computes the missing source state identities. + * Computes the globally missing state identities. * - * @return A BDD that represents the source state identities for predicates that are irrelevant for the - * source states. + * @return A BDD that represents the global state identities for predicates that are irrelevant for the + * source and successor states. */ - storm::dd::Bdd computeMissingSourceStateIdentities() const; + storm::dd::Bdd computeMissingGlobalIdentities() const; // An SMT responsible for this abstract command. std::unique_ptr smtSolver; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 14a364321..792efc323 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -34,11 +34,6 @@ namespace storm { totalNumberOfCommands += module.getNumberOfCommands(); } - // Create DD variables for all predicates. - for (auto const& predicate : expressionInformation.predicates) { - ddInformation.addPredicate(predicate); - } - // Create DD variable for the command encoding. ddInformation.commandDdVariable = ddInformation.manager->addMetaVariable("command", 0, totalNumberOfCommands - 1).first; @@ -51,7 +46,12 @@ namespace storm { // that it's impossible to treat such models in any event. for (uint_fast64_t index = 0; index < 100; ++index) { storm::expressions::Variable newOptionVar = ddInformation.manager->addMetaVariable("opt" + std::to_string(index)).first; - ddInformation.optionDdVariables.push_back(std::make_pair(newOptionVar, ddInformation.manager->getRange(newOptionVar))); + ddInformation.optionDdVariables.push_back(std::make_pair(newOptionVar, ddInformation.manager->getIdentity(newOptionVar).toBdd())); + } + + // Create DD variables for all predicates. + for (auto const& predicate : expressionInformation.predicates) { + ddInformation.addPredicate(predicate); } // For each module of the concrete program, we create an abstract counterpart. @@ -68,8 +68,6 @@ namespace storm { template void AbstractProgram::refine(std::vector const& predicates) { - std::cout << "refining!" << std::endl; - // Add the predicates to the global list of predicates. uint_fast64_t firstNewPredicateIndex = expressionInformation.predicates.size(); expressionInformation.predicates.insert(expressionInformation.predicates.end(), predicates.begin(), predicates.end()); @@ -85,17 +83,13 @@ namespace storm { newPredicateIndices.push_back(index); } - std::cout << "refining modules" << std::endl; // Refine all abstract modules. for (auto& module : modules) { module.refine(newPredicateIndices); } - std::cout << "refining initial" << std::endl; // Refine initial state abstractor. initialStateAbstractor.refine(newPredicateIndices); - - std::cout << "done " << std::endl; } template @@ -108,8 +102,6 @@ namespace storm { return lastAbstractAdd; } - std::cout << "abstr DD new " << std::endl; - // Otherwise, we remember that the abstract BDD changed and perform a reachability analysis. lastAbstractBdd = gameBdd.first; @@ -118,15 +110,11 @@ namespace storm { for (uint_fast64_t index = 0; index < gameBdd.second; ++index) { variablesToAbstract.insert(ddInformation.optionDdVariables[index].first); } - - std::cout << "reachability... " << std::endl; // Do a reachability analysis on the raw transition relation. storm::dd::Bdd transitionRelation = lastAbstractBdd.existsAbstract(variablesToAbstract); storm::dd::Bdd reachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); - std::cout << "done " << std::endl; - // Find the deadlock states in the model. storm::dd::Bdd deadlockStates = transitionRelation.existsAbstract(ddInformation.successorVariables); deadlockStates = reachableStates && !deadlockStates; diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index 6395fa951..bb98b5a0b 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -9,6 +9,8 @@ #include "src/storage/dd/CuddBdd.h" #include "src/storage/dd/CuddAdd.h" +#include "src/utility/macros.h" + namespace storm { namespace prism { namespace menu_games { @@ -22,6 +24,7 @@ namespace storm { storm::dd::Bdd AbstractionDdInformation::encodeDistributionIndex(uint_fast64_t numberOfVariables, uint_fast64_t distributionIndex) const { storm::dd::Bdd result = manager->getBddOne(); for (uint_fast64_t bitIndex = 0; bitIndex < numberOfVariables; ++bitIndex) { + STORM_LOG_ASSERT(!(optionDdVariables[bitIndex].second.isZero() || optionDdVariables[bitIndex].second.isOne()), "Option variable is corrupted."); if ((distributionIndex & 1) != 0) { result &= optionDdVariables[bitIndex].second; } else { @@ -29,6 +32,7 @@ namespace storm { } distributionIndex >>= 1; } + STORM_LOG_ASSERT(!result.isZero(), "Update BDD encoding must not be zero."); return result; } @@ -36,7 +40,15 @@ namespace storm { void AbstractionDdInformation::addPredicate(storm::expressions::Expression const& predicate) { std::stringstream stream; stream << predicate; - std::pair newMetaVariable = manager->addMetaVariable(stream.str()); + std::pair newMetaVariable; + + // Create the new predicate variable below all other predicate variables. + if (predicateDdVariables.empty()) { + newMetaVariable = manager->addMetaVariable(stream.str()); + } else { + newMetaVariable = manager->addMetaVariable(stream.str(), std::make_pair(storm::dd::MetaVariablePosition::Below, predicateDdVariables.back().second)); + } + predicateDdVariables.push_back(newMetaVariable); predicateBdds.emplace_back(manager->getEncoding(newMetaVariable.first, 1), manager->getEncoding(newMetaVariable.second, 1)); predicateIdentities.push_back(manager->getIdentity(newMetaVariable.first).equals(manager->getIdentity(newMetaVariable.second)).toBdd()); @@ -46,13 +58,15 @@ namespace storm { } template - storm::dd::Bdd AbstractionDdInformation::getMissingOptionVariableCube(uint_fast64_t lastUsed, uint_fast64_t lastToBe) const { + storm::dd::Bdd AbstractionDdInformation::getMissingOptionVariableCube(uint_fast64_t begin, uint_fast64_t end) const { storm::dd::Bdd result = manager->getBddOne(); - for (uint_fast64_t index = lastUsed + 1; index <= lastToBe; ++index) { + for (uint_fast64_t index = begin; index < end; ++index) { result &= optionDdVariables[index].second; } + STORM_LOG_ASSERT(!result.isZero(), "Update variable cube must not be zero."); + return result; } @@ -66,6 +80,8 @@ namespace storm { // If the new variable does not yet exist as a source variable, we create it now. if (oldIt == oldIte || oldIt->second != *newIt) { result.push_back(std::make_pair(manager.declareFreshBooleanVariable(), *newIt)); + } else { + ++oldIt; } } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index ac5fa4316..ef8e08267 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -52,13 +52,13 @@ namespace storm { void addPredicate(storm::expressions::Expression const& predicate); /*! - * Retrieves the cube of option variables in the range (lastUsed, lastToBe] the given indices. + * Retrieves the cube of option variables in the range [begin, end) the given indices. * - * @param lastUsed The last variable before the range to return. - * @param lastToBe The last variable of the range to return. + * @param begin The first variable of the range to return. + * @param end One past the last variable of the range to return. * @return The cube of variables in the given range. */ - storm::dd::Bdd getMissingOptionVariableCube(uint_fast64_t lastUsed, uint_fast64_t lastToBe) const; + storm::dd::Bdd getMissingOptionVariableCube(uint_fast64_t begin, uint_fast64_t end) const; /*! * Examines the old and new relevant predicates and declares decision variables for the missing relevant diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index d3bd2d56a..466f7693a 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -15,7 +15,7 @@ #include "src/utility/solver.h" -TEST(PrismMenuGame, CommandAbstractionTest) { +TEST(PrismMenuGame, DieProgramAbstractionTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); std::vector initialPredicates; @@ -25,13 +25,156 @@ TEST(PrismMenuGame, CommandAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction = abstractProgram.getAbstractAdd(); - abstraction.exportToDot("abstr1.dot"); + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - abstractProgram.refine({manager.getVariableExpression("s") == manager.integer(7)}); - abstraction = abstractProgram.getAbstractAdd(); - abstraction.exportToDot("abstr2.dot"); + EXPECT_EQ(19, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, DieProgramAbstractionAndRefinementTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s") < manager.integer(3)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("s") == manager.integer(7)})); + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(26, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, CrowdsProgramAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); + program = program.substituteConstants(); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("phase") < manager.integer(3)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(46, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, CrowdsProgramAbstractionAndRefinementTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); + program = program.substituteConstants(); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("phase") < manager.integer(3)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(46, abstraction.getNodeCount()); + + ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("observe0") + manager.getVariableExpression("observe1") + manager.getVariableExpression("observe2") + manager.getVariableExpression("observe3") + manager.getVariableExpression("observe4") <= manager.getVariableExpression("runCount")})); + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(75, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, TwoDiceProgramAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s1") < manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(0)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(38, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, TwoDiceProgramAbstractionAndRefinementTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s1") < manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(0)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(38, abstraction.getNodeCount()); + + ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("d1") + manager.getVariableExpression("d2") == manager.integer(7)})); + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(107, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, WlanProgramAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s1") < manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("bc1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.getVariableExpression("c2")); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(219, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, WlanProgramAbstractionAndRefinementTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s1") < manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("bc1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.getVariableExpression("c2")); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(219, abstraction.getNodeCount()); + + ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("backoff1") < manager.integer(7)})); + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + EXPECT_EQ(292, abstraction.getNodeCount()); } #endif \ No newline at end of file From e8794dee22b83e61642794c6e9c0e39f078c9c82 Mon Sep 17 00:00:00 2001 From: dehnert Date: Fri, 18 Sep 2015 16:58:09 +0200 Subject: [PATCH 013/400] added more tests, not working yet, however Former-commit-id: 2badd7ce35cfc4ac92f236dd6c28f0b4d252bd94 --- .../prism/menu_games/AbstractCommand.cpp | 15 +- .../prism/menu_games/AbstractProgram.cpp | 16 ++- .../prism/menu_games/AbstractProgram.h | 13 +- .../prism/menu_games/VariablePartition.cpp | 2 + .../abstraction/PrismMenuGameTest.cpp | 128 ++++++++++++++++-- 5 files changed, 160 insertions(+), 14 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 3b91eb01e..267ee38b6 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -71,11 +71,12 @@ namespace storm { template void AbstractCommand::recomputeCachedBdd() { STORM_LOG_TRACE("Recomputing BDD for command " << command.get()); + std::cout << "recomputing " << command.get() << std::endl; // Create a mapping from source state DDs to their distributions. std::unordered_map, std::vector>> sourceToDistributionsMap; uint_fast64_t modelCounter = 0; - smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this,&modelCounter] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); ++modelCounter; return true; } ); + smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this,&modelCounter] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); ++modelCounter; std::cout << "model cnt: " << modelCounter << std::endl; return true; } ); // Now we search for the maximal number of choices of player 2 to determine how many DD variables we // need to encode the nondeterminism. @@ -121,6 +122,11 @@ namespace storm { // To start with, all predicates related to the guard are relevant source predicates. result.first = variablePartition.getExpressionsUsingVariables(command.get().getGuardExpression().getVariables()); + std::cout << "using" << std::endl; + for (auto const& el : result.first) { + std::cout << expressionInformation.predicates[el] << std::endl; + } + std::set assignedVariables; for (auto const& assignment : assignments) { // Also, variables appearing on the right-hand side of an assignment are relevant for source state. @@ -137,6 +143,13 @@ namespace storm { } auto const& predicatesRelatedToAssignedVariable = variablePartition.getRelatedExpressions(assignedVariables); + + std::cout << variablePartition << std::endl; + std::cout << "related" << std::endl; + for (auto const& el : predicatesRelatedToAssignedVariable) { + std::cout << expressionInformation.predicates[el] << std::endl; + } + result.first.insert(predicatesRelatedToAssignedVariable.begin(), predicatesRelatedToAssignedVariable.end()); return result; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 792efc323..c515a9074 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -8,13 +8,14 @@ #include "src/utility/macros.h" #include "src/utility/solver.h" #include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/InvalidArgumentException.h" namespace storm { namespace prism { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), lastAbstractBdd(ddInformation.manager->getBddZero()), lastAbstractAdd(ddInformation.manager->getAddZero()) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), lastAbstractBdd(ddInformation.manager->getBddZero()), lastAbstractAdd(ddInformation.manager->getAddZero()), lastReachableStates(ddInformation.manager->getBddZero()) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. @@ -74,6 +75,7 @@ namespace storm { // Create DD variables and some auxiliary data structures for the new predicates. for (auto const& predicate : predicates) { + STORM_LOG_THROW(predicate.hasBooleanType(), storm::exceptions::InvalidArgumentException, "Expecting a predicate of type bool."); ddInformation.addPredicate(predicate); } @@ -113,11 +115,11 @@ namespace storm { // Do a reachability analysis on the raw transition relation. storm::dd::Bdd transitionRelation = lastAbstractBdd.existsAbstract(variablesToAbstract); - storm::dd::Bdd reachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); + lastReachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); // Find the deadlock states in the model. storm::dd::Bdd deadlockStates = transitionRelation.existsAbstract(ddInformation.successorVariables); - deadlockStates = reachableStates && !deadlockStates; + deadlockStates = lastReachableStates && !deadlockStates; // If there are deadlock states, we fix them now. storm::dd::Add deadlockTransitions = ddInformation.manager->getAddZero(); @@ -126,10 +128,16 @@ namespace storm { } // Construct the final game by cutting away the transitions of unreachable states. - lastAbstractAdd = (lastAbstractBdd && reachableStates).toAdd() * commandUpdateProbabilitiesAdd + deadlockTransitions; + lastAbstractAdd = (lastAbstractBdd && lastReachableStates).toAdd() * commandUpdateProbabilitiesAdd + deadlockTransitions; return lastAbstractAdd; } + + template + storm::dd::Bdd AbstractProgram::getReachableStates() { + return lastReachableStates; + } + template storm::dd::Bdd AbstractProgram::getReachableStates(storm::dd::Bdd const& initialStates, storm::dd::Bdd const& transitionRelation) { storm::dd::Bdd frontier = initialStates; diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 88094a632..c1f54b585 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -44,6 +44,14 @@ namespace storm { */ storm::dd::Add getAbstractAdd(); + /*! + * Retrieves the reachable state space of the abstract game (that was previously retrieved via the + * appropriate method. + * + * @return The reachable state space in the form of a BDD. + */ + storm::dd::Bdd getReachableStates(); + /*! * Refines the abstract module with the given predicates. * @@ -85,9 +93,12 @@ namespace storm { // A BDD that is the result of the last abstraction of the system. storm::dd::Bdd lastAbstractBdd; - + // An ADD that is the result of the last abstraction of the system. storm::dd::Add lastAbstractAdd; + + // A BDD that is the result of the reachability analysis on the abstraction of the system. + storm::dd::Bdd lastReachableStates; }; } } diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index a5058db1d..fc69ac1a4 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -10,9 +10,11 @@ namespace storm { VariablePartition::VariablePartition(std::set const& relevantVariables, std::vector const& expressions) : relevantVariables(relevantVariables), expressionBlocks(relevantVariables.size()) { // Assign each variable to a new block. uint_fast64_t currentBlock = 0; + variableBlocks.resize(relevantVariables.size()); for (auto const& variable : relevantVariables) { this->variableToBlockMapping[variable] = currentBlock; this->variableToExpressionsMapping[variable] = std::set(); + variableBlocks[currentBlock].insert(variable); } // Add all expressions, which might relate some variables. diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 466f7693a..8fdd03cdd 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -15,7 +15,7 @@ #include "src/utility/solver.h" -TEST(PrismMenuGame, DieProgramAbstractionTest) { +TEST(PrismMenuGame, DieAbstractionTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); std::vector initialPredicates; @@ -31,7 +31,7 @@ TEST(PrismMenuGame, DieProgramAbstractionTest) { EXPECT_EQ(19, abstraction.getNodeCount()); } -TEST(PrismMenuGame, DieProgramAbstractionAndRefinementTest) { +TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); std::vector initialPredicates; @@ -50,7 +50,43 @@ TEST(PrismMenuGame, DieProgramAbstractionAndRefinementTest) { EXPECT_EQ(26, abstraction.getNodeCount()); } -TEST(PrismMenuGame, CrowdsProgramAbstractionTest) { +TEST(PrismMenuGame, DieFullAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("d") == manager.integer(6)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(313, abstraction.getNodeCount()); + + storm::dd::Bdd reachableStatesInAbstraction; + ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); + + EXPECT_EQ(13, reachableStatesInAbstraction.getNonZeroCount()); +} + +TEST(PrismMenuGame, CrowdsAbstractionTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); program = program.substituteConstants(); @@ -67,7 +103,7 @@ TEST(PrismMenuGame, CrowdsProgramAbstractionTest) { EXPECT_EQ(46, abstraction.getNodeCount()); } -TEST(PrismMenuGame, CrowdsProgramAbstractionAndRefinementTest) { +TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); program = program.substituteConstants(); @@ -89,7 +125,83 @@ TEST(PrismMenuGame, CrowdsProgramAbstractionAndRefinementTest) { EXPECT_EQ(75, abstraction.getNodeCount()); } -TEST(PrismMenuGame, TwoDiceProgramAbstractionTest) { +TEST(PrismMenuGame, CrowdsFullAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); + program = program.substituteConstants(); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("phase") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("phase") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("phase") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("phase") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("phase") == manager.integer(4)); + + initialPredicates.push_back(manager.getVariableExpression("good")); + + initialPredicates.push_back(manager.getVariableExpression("runCount") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("runCount") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("runCount") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("runCount") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("runCount") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("runCount") == manager.integer(5)); + + initialPredicates.push_back(manager.getVariableExpression("observe0") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("observe0") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("observe0") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("observe0") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("observe0") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("observe0") == manager.integer(5)); + + initialPredicates.push_back(manager.getVariableExpression("observe1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("observe1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("observe1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("observe1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("observe1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("observe1") == manager.integer(5)); + + initialPredicates.push_back(manager.getVariableExpression("observe2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("observe2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("observe2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("observe2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("observe2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("observe2") == manager.integer(5)); + + initialPredicates.push_back(manager.getVariableExpression("observe3") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("observe3") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("observe3") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("observe3") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("observe3") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("observe3") == manager.integer(5)); + + initialPredicates.push_back(manager.getVariableExpression("observe4") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("observe4") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("observe4") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("observe4") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("observe4") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("observe4") == manager.integer(5)); + + initialPredicates.push_back(manager.getVariableExpression("lastSeen") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("lastSeen") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("lastSeen") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("lastSeen") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("lastSeen") == manager.integer(4)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(46, abstraction.getNodeCount()); + + storm::dd::Bdd reachableStatesInAbstraction; + ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); + + EXPECT_EQ(13, reachableStatesInAbstraction.getNonZeroCount()); +} + +TEST(PrismMenuGame, TwoDiceAbstractionTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); @@ -108,7 +220,7 @@ TEST(PrismMenuGame, TwoDiceProgramAbstractionTest) { EXPECT_EQ(38, abstraction.getNodeCount()); } -TEST(PrismMenuGame, TwoDiceProgramAbstractionAndRefinementTest) { +TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); @@ -132,7 +244,7 @@ TEST(PrismMenuGame, TwoDiceProgramAbstractionAndRefinementTest) { EXPECT_EQ(107, abstraction.getNodeCount()); } -TEST(PrismMenuGame, WlanProgramAbstractionTest) { +TEST(PrismMenuGame, WlanAbstractionTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); @@ -152,7 +264,7 @@ TEST(PrismMenuGame, WlanProgramAbstractionTest) { EXPECT_EQ(219, abstraction.getNodeCount()); } -TEST(PrismMenuGame, WlanProgramAbstractionAndRefinementTest) { +TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); From 0cd148c60020bf5f3adbbaf23e6aedb52e3cb331 Mon Sep 17 00:00:00 2001 From: dehnert Date: Fri, 18 Sep 2015 21:19:49 +0200 Subject: [PATCH 014/400] fixed more bugs. however, a test still fails, because the abstraction is wrong Former-commit-id: 6e326acaf3c89621f0e6861736ca6d7f60c99a85 --- .../prism/menu_games/AbstractCommand.cpp | 22 +- .../prism/menu_games/AbstractProgram.cpp | 2 + .../prism/menu_games/VariablePartition.cpp | 69 ++++--- .../abstraction/PrismMenuGameTest.cpp | 193 +++++++++++++++++- 4 files changed, 237 insertions(+), 49 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index 267ee38b6..bd019a4a9 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -56,9 +56,8 @@ namespace storm { bool recomputeDd = this->relevantPredicatesChanged(newRelevantPredicates); if (!recomputeDd) { // If the new predicates are unrelated to the BDD of this command, we need to multiply their identities. - for (auto predicateIndex : predicates) { - cachedDd.first &= ddInformation.predicateIdentities[predicateIndex]; - } + cachedDd.first &= computeMissingGlobalIdentities(); + cachedDd.first.toAdd().exportToDot("cmd" + std::to_string(command.get().getGlobalIndex()) + ".dot"); } else { // If the DD needs recomputation, it is because of new relevant predicates, so we need to assert the appropriate clauses in the solver. addMissingPredicates(newRelevantPredicates); @@ -71,12 +70,11 @@ namespace storm { template void AbstractCommand::recomputeCachedBdd() { STORM_LOG_TRACE("Recomputing BDD for command " << command.get()); - std::cout << "recomputing " << command.get() << std::endl; - + // Create a mapping from source state DDs to their distributions. std::unordered_map, std::vector>> sourceToDistributionsMap; uint_fast64_t modelCounter = 0; - smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this,&modelCounter] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); ++modelCounter; std::cout << "model cnt: " << modelCounter << std::endl; return true; } ); + smtSolver->allSat(decisionVariables, [&sourceToDistributionsMap,this,&modelCounter] (storm::solver::SmtSolver::ModelReference const& model) { sourceToDistributionsMap[getSourceStateBdd(model)].push_back(getDistributionBdd(model)); return true; } ); // Now we search for the maximal number of choices of player 2 to determine how many DD variables we // need to encode the nondeterminism. @@ -112,6 +110,7 @@ namespace storm { STORM_LOG_ASSERT(sourceToDistributionsMap.empty() || !resultBdd.isZero(), "The BDD must not be empty, if there were distributions."); // Cache the result. + resultBdd.toAdd().exportToDot("cmd" + std::to_string(command.get().getGlobalIndex()) + ".dot"); cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); } @@ -122,11 +121,6 @@ namespace storm { // To start with, all predicates related to the guard are relevant source predicates. result.first = variablePartition.getExpressionsUsingVariables(command.get().getGuardExpression().getVariables()); - std::cout << "using" << std::endl; - for (auto const& el : result.first) { - std::cout << expressionInformation.predicates[el] << std::endl; - } - std::set assignedVariables; for (auto const& assignment : assignments) { // Also, variables appearing on the right-hand side of an assignment are relevant for source state. @@ -144,12 +138,6 @@ namespace storm { auto const& predicatesRelatedToAssignedVariable = variablePartition.getRelatedExpressions(assignedVariables); - std::cout << variablePartition << std::endl; - std::cout << "related" << std::endl; - for (auto const& el : predicatesRelatedToAssignedVariable) { - std::cout << expressionInformation.predicates[el] << std::endl; - } - result.first.insert(predicatesRelatedToAssignedVariable.begin(), predicatesRelatedToAssignedVariable.end()); return result; diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index c515a9074..476b18aac 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -69,6 +69,8 @@ namespace storm { template void AbstractProgram::refine(std::vector const& predicates) { + std::cout << " >>>> refine <<<<<<" << std::endl; + // Add the predicates to the global list of predicates. uint_fast64_t firstNewPredicateIndex = expressionInformation.predicates.size(); expressionInformation.predicates.insert(expressionInformation.predicates.end(), predicates.begin(), predicates.end()); diff --git a/src/storage/prism/menu_games/VariablePartition.cpp b/src/storage/prism/menu_games/VariablePartition.cpp index fc69ac1a4..f48df618f 100644 --- a/src/storage/prism/menu_games/VariablePartition.cpp +++ b/src/storage/prism/menu_games/VariablePartition.cpp @@ -15,6 +15,7 @@ namespace storm { this->variableToBlockMapping[variable] = currentBlock; this->variableToExpressionsMapping[variable] = std::set(); variableBlocks[currentBlock].insert(variable); + ++currentBlock; } // Add all expressions, which might relate some variables. @@ -67,35 +68,42 @@ namespace storm { } void VariablePartition::mergeBlocks(std::set const& blocksToMerge) { - // Determine which block to keep (to merge the other blocks into). - uint_fast64_t blockToKeep = *blocksToMerge.begin(); - // Merge all blocks into the block to keep. std::vector> newVariableBlocks; std::vector> newExpressionBlocks; + + std::set::const_iterator blocksToMergeIt = blocksToMerge.begin(); + std::set::const_iterator blocksToMergeIte = blocksToMerge.end(); + + // Determine which block to keep (to merge the other blocks into). + uint_fast64_t blockToKeep = *blocksToMergeIt; + ++blocksToMergeIt; + for (uint_fast64_t blockIndex = 0; blockIndex < variableBlocks.size(); ++blockIndex) { - - // If the block is the one into which the others are to be merged, we do so. - if (blockIndex == blockToKeep) { - for (auto const& blockToMerge : blocksToMerge) { - if (blockToMerge == blockToKeep) { - continue; - } - - variableBlocks[blockToKeep].insert(variableBlocks[blockToMerge].begin(), variableBlocks[blockToMerge].end()); - expressionBlocks[blockToKeep].insert(expressionBlocks[blockToMerge].begin(), expressionBlocks[blockToMerge].end()); + // If the block is the next one to merge into the block to keep, do so now. + if (blocksToMergeIt != blocksToMergeIte && *blocksToMergeIt == blockIndex && blockIndex != blockToKeep) { + // Adjust the mapping for all variables of the old block. + for (auto const& variable : variableBlocks[blockIndex]) { + variableToBlockMapping[variable] = blockToKeep; + } + + newVariableBlocks[blockToKeep].insert(variableBlocks[blockIndex].begin(), variableBlocks[blockIndex].end()); + newExpressionBlocks[blockToKeep].insert(expressionBlocks[blockIndex].begin(), expressionBlocks[blockIndex].end()); + ++blocksToMergeIt; + } else { + // Otherwise just move the current block to the new partition. + newVariableBlocks.emplace_back(std::move(variableBlocks[blockIndex])); + newExpressionBlocks.emplace_back(std::move(expressionBlocks[blockIndex])); + + // Adjust the mapping for all variables of the old block. + for (auto const& variable : variableBlocks[blockIndex]) { + variableToBlockMapping[variable] = newVariableBlocks.size() - 1; } } - - // Adjust the mapping for all variables we are moving to the new block. - for (auto const& variable : variableBlocks[blockIndex]) { - variableToBlockMapping[variable] = newVariableBlocks.size(); - } - - // Move the current block to the new partition. - newVariableBlocks.emplace_back(std::move(variableBlocks[blockIndex])); - newExpressionBlocks.emplace_back(std::move(expressionBlocks[blockIndex])); } + + variableBlocks = std::move(newVariableBlocks); + expressionBlocks = std::move(newExpressionBlocks); } std::set const& VariablePartition::getBlockOfVariable(storm::expressions::Variable const& variable) const { @@ -157,12 +165,23 @@ namespace storm { std::ostream& operator<<(std::ostream& out, VariablePartition const& partition) { std::vector blocks; - for (auto const& block : partition.variableBlocks) { + for (uint_fast64_t index = 0; index < partition.variableBlocks.size(); ++index) { + auto const& variableBlock = partition.variableBlocks[index]; + auto const& expressionBlock = partition.expressionBlocks[index]; + std::vector variablesInBlock; - for (auto const& variable : block) { + for (auto const& variable : variableBlock) { variablesInBlock.push_back(variable.getName()); } - blocks.push_back("[" + boost::algorithm::join(variablesInBlock, ", ") + "]"); + + std::vector expressionsInBlock; + for (auto const& expression : expressionBlock) { + std::stringstream stream; + stream << partition.expressions[expression]; + expressionsInBlock.push_back(stream.str()); + } + + blocks.push_back("<[" + boost::algorithm::join(variablesInBlock, ", ") + "], [" + boost::algorithm::join(expressionsInBlock, ", ") + "]>"); } out << "{"; diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 8fdd03cdd..26b41e87a 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -193,12 +193,10 @@ TEST(PrismMenuGame, CrowdsFullAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(46, abstraction.getNodeCount()); - storm::dd::Bdd reachableStatesInAbstraction; ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); - EXPECT_EQ(13, reachableStatesInAbstraction.getNonZeroCount()); + EXPECT_EQ(8607, reachableStatesInAbstraction.getNonZeroCount()); } TEST(PrismMenuGame, TwoDiceAbstractionTest) { @@ -240,8 +238,66 @@ TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("d1") + manager.getVariableExpression("d2") == manager.integer(7)})); ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(95, abstraction.getNodeCount()); + + storm::dd::Bdd reachableStatesInAbstraction; + ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); +} - EXPECT_EQ(107, abstraction.getNodeCount()); +TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(6)); + + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(6)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(1635, abstraction.getNodeCount()); + + storm::dd::Bdd reachableStatesInAbstraction; + ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); + + EXPECT_EQ(169, reachableStatesInAbstraction.getNonZeroCount()); } TEST(PrismMenuGame, WlanAbstractionTest) { @@ -261,7 +317,7 @@ TEST(PrismMenuGame, WlanAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(219, abstraction.getNodeCount()); + EXPECT_EQ(221, abstraction.getNodeCount()); } TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { @@ -281,12 +337,135 @@ TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(219, abstraction.getNodeCount()); + EXPECT_EQ(221, abstraction.getNodeCount()); ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("backoff1") < manager.integer(7)})); ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(292, abstraction.getNodeCount()); + EXPECT_EQ(287, abstraction.getNodeCount()); +} + +TEST(PrismMenuGame, WlanFullAbstractionTest) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("col") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("col") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("col") == manager.integer(2)); + + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.integer(2)); + + initialPredicates.push_back(manager.getVariableExpression("c2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("c2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("c2") == manager.integer(2)); + + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(12)); + + initialPredicates.push_back(manager.getVariableExpression("slot1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("slot1") == manager.integer(1)); + + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(12)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(13)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(14)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(15)); + + initialPredicates.push_back(manager.getVariableExpression("bc1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("bc1") == manager.integer(1)); + + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(12)); + + initialPredicates.push_back(manager.getVariableExpression("slot2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("slot2") == manager.integer(1)); + + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(12)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(13)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(14)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(15)); + + initialPredicates.push_back(manager.getVariableExpression("bc2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("bc2") == manager.integer(1)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::dd::Add abstraction; + ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + EXPECT_EQ(2861, abstraction.getNodeCount()); + + storm::dd::Bdd reachableStatesInAbstraction; + ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); + + EXPECT_EQ(37, reachableStatesInAbstraction.getNonZeroCount()); } #endif \ No newline at end of file From 1199ab95e3d0f81a833f806b4236999f55d02bdb Mon Sep 17 00:00:00 2001 From: dehnert Date: Sat, 19 Sep 2015 13:29:41 +0200 Subject: [PATCH 015/400] fixed bug in expressions. all tests now passing Former-commit-id: 86b4b2a04a79da080c74255e17dc9dd8f08ebe1e --- src/storage/expressions/UnaryExpression.cpp | 2 +- .../prism/menu_games/AbstractCommand.cpp | 11 ++-- .../prism/menu_games/AbstractProgram.cpp | 2 - .../abstraction/PrismMenuGameTest.cpp | 63 +++++++------------ 4 files changed, 30 insertions(+), 48 deletions(-) diff --git a/src/storage/expressions/UnaryExpression.cpp b/src/storage/expressions/UnaryExpression.cpp index 1223368fd..451e0521e 100644 --- a/src/storage/expressions/UnaryExpression.cpp +++ b/src/storage/expressions/UnaryExpression.cpp @@ -18,7 +18,7 @@ namespace storm { } void UnaryExpression::gatherVariables(std::set& variables) const { - return; + this->getOperand()->gatherVariables(variables); } std::shared_ptr const& UnaryExpression::getOperand() const { diff --git a/src/storage/prism/menu_games/AbstractCommand.cpp b/src/storage/prism/menu_games/AbstractCommand.cpp index bd019a4a9..de66bf091 100644 --- a/src/storage/prism/menu_games/AbstractCommand.cpp +++ b/src/storage/prism/menu_games/AbstractCommand.cpp @@ -57,7 +57,6 @@ namespace storm { if (!recomputeDd) { // If the new predicates are unrelated to the BDD of this command, we need to multiply their identities. cachedDd.first &= computeMissingGlobalIdentities(); - cachedDd.first.toAdd().exportToDot("cmd" + std::to_string(command.get().getGlobalIndex()) + ".dot"); } else { // If the DD needs recomputation, it is because of new relevant predicates, so we need to assert the appropriate clauses in the solver. addMissingPredicates(newRelevantPredicates); @@ -70,7 +69,7 @@ namespace storm { template void AbstractCommand::recomputeCachedBdd() { STORM_LOG_TRACE("Recomputing BDD for command " << command.get()); - + // Create a mapping from source state DDs to their distributions. std::unordered_map, std::vector>> sourceToDistributionsMap; uint_fast64_t modelCounter = 0; @@ -110,7 +109,6 @@ namespace storm { STORM_LOG_ASSERT(sourceToDistributionsMap.empty() || !resultBdd.isZero(), "The BDD must not be empty, if there were distributions."); // Cache the result. - resultBdd.toAdd().exportToDot("cmd" + std::to_string(command.get().getGlobalIndex()) + ".dot"); cachedDd = std::make_pair(resultBdd, numberOfVariablesNeeded); } @@ -118,9 +116,6 @@ namespace storm { std::pair, std::set> AbstractCommand::computeRelevantPredicates(std::vector const& assignments) const { std::pair, std::set> result; - // To start with, all predicates related to the guard are relevant source predicates. - result.first = variablePartition.getExpressionsUsingVariables(command.get().getGuardExpression().getVariables()); - std::set assignedVariables; for (auto const& assignment : assignments) { // Also, variables appearing on the right-hand side of an assignment are relevant for source state. @@ -147,6 +142,10 @@ namespace storm { std::pair, std::vector>> AbstractCommand::computeRelevantPredicates() const { std::pair, std::vector>> result; + // To start with, all predicates related to the guard are relevant source predicates. + result.first = variablePartition.getExpressionsUsingVariables(command.get().getGuardExpression().getVariables()); + + // Then, we add the predicates that become relevant, because of some update. for (auto const& update : command.get().getUpdates()) { std::pair, std::set> relevantUpdatePredicates = computeRelevantPredicates(update.getAssignments()); result.first.insert(relevantUpdatePredicates.first.begin(), relevantUpdatePredicates.first.end()); diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 476b18aac..c515a9074 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -69,8 +69,6 @@ namespace storm { template void AbstractProgram::refine(std::vector const& predicates) { - std::cout << " >>>> refine <<<<<<" << std::endl; - // Add the predicates to the global list of predicates. uint_fast64_t firstNewPredicateIndex = expressionInformation.predicates.size(); expressionInformation.predicates.insert(expressionInformation.predicates.end(), predicates.begin(), predicates.end()); diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 26b41e87a..0fcf721dc 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -28,7 +28,8 @@ TEST(PrismMenuGame, DieAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(19, abstraction.getNodeCount()); + EXPECT_EQ(15, abstraction.getNonZeroCount()); + EXPECT_EQ(2, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { @@ -47,7 +48,8 @@ TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("s") == manager.integer(7)})); ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(26, abstraction.getNodeCount()); + EXPECT_EQ(15, abstraction.getNonZeroCount()); + EXPECT_EQ(3, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, DieFullAbstractionTest) { @@ -78,12 +80,8 @@ TEST(PrismMenuGame, DieFullAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(313, abstraction.getNodeCount()); - - storm::dd::Bdd reachableStatesInAbstraction; - ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); - - EXPECT_EQ(13, reachableStatesInAbstraction.getNonZeroCount()); + EXPECT_EQ(20, abstraction.getNonZeroCount()); + EXPECT_EQ(13, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, CrowdsAbstractionTest) { @@ -100,7 +98,8 @@ TEST(PrismMenuGame, CrowdsAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(46, abstraction.getNodeCount()); + EXPECT_EQ(16, abstraction.getNonZeroCount()); + EXPECT_EQ(2, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { @@ -117,12 +116,11 @@ TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(46, abstraction.getNodeCount()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("observe0") + manager.getVariableExpression("observe1") + manager.getVariableExpression("observe2") + manager.getVariableExpression("observe3") + manager.getVariableExpression("observe4") <= manager.getVariableExpression("runCount")})); ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(75, abstraction.getNodeCount()); + EXPECT_EQ(38, abstraction.getNonZeroCount()); + EXPECT_EQ(4, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, CrowdsFullAbstractionTest) { @@ -193,10 +191,8 @@ TEST(PrismMenuGame, CrowdsFullAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - storm::dd::Bdd reachableStatesInAbstraction; - ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); - - EXPECT_EQ(8607, reachableStatesInAbstraction.getNonZeroCount()); + EXPECT_EQ(15113, abstraction.getNonZeroCount()); + EXPECT_EQ(8607, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, TwoDiceAbstractionTest) { @@ -215,7 +211,8 @@ TEST(PrismMenuGame, TwoDiceAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(38, abstraction.getNodeCount()); + EXPECT_EQ(58, abstraction.getNonZeroCount()); + EXPECT_EQ(4, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { @@ -234,15 +231,11 @@ TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(38, abstraction.getNodeCount()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("d1") + manager.getVariableExpression("d2") == manager.integer(7)})); ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(95, abstraction.getNodeCount()); - - storm::dd::Bdd reachableStatesInAbstraction; - ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); + EXPECT_EQ(212, abstraction.getNonZeroCount()); + EXPECT_EQ(8, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { @@ -292,12 +285,8 @@ TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(1635, abstraction.getNodeCount()); - - storm::dd::Bdd reachableStatesInAbstraction; - ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); - - EXPECT_EQ(169, reachableStatesInAbstraction.getNonZeroCount()); + EXPECT_EQ(436, abstraction.getNonZeroCount()); + EXPECT_EQ(169, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, WlanAbstractionTest) { @@ -317,7 +306,8 @@ TEST(PrismMenuGame, WlanAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(221, abstraction.getNodeCount()); + EXPECT_EQ(307, abstraction.getNonZeroCount()); + EXPECT_EQ(4, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { @@ -337,12 +327,11 @@ TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(221, abstraction.getNodeCount()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("backoff1") < manager.integer(7)})); ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(287, abstraction.getNodeCount()); + EXPECT_EQ(612, abstraction.getNonZeroCount()); + EXPECT_EQ(8, abstractProgram.getReachableStates().getNonZeroCount()); } TEST(PrismMenuGame, WlanFullAbstractionTest) { @@ -460,12 +449,8 @@ TEST(PrismMenuGame, WlanFullAbstractionTest) { storm::dd::Add abstraction; ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(2861, abstraction.getNodeCount()); - - storm::dd::Bdd reachableStatesInAbstraction; - ASSERT_NO_THROW(reachableStatesInAbstraction = abstractProgram.getReachableStates()); - - EXPECT_EQ(37, reachableStatesInAbstraction.getNonZeroCount()); + EXPECT_EQ(59, abstraction.getNonZeroCount()); + EXPECT_EQ(37, abstractProgram.getReachableStates().getNonZeroCount()); } #endif \ No newline at end of file From 7cd1e6324f0ec2c0fd08217cc77be3c5e4ddda22 Mon Sep 17 00:00:00 2001 From: dehnert Date: Sat, 19 Sep 2015 16:41:06 +0200 Subject: [PATCH 016/400] the abstraction now properly builds an instance of the game class Former-commit-id: 26d4effa00c02769afe67d3d3c2bc3deaac3b207 --- src/models/symbolic/Model.cpp | 3 +- src/storage/expressions/Expression.cpp | 4 + src/storage/expressions/Expression.h | 8 ++ .../prism/menu_games/AbstractProgram.cpp | 69 ++++++++---- .../prism/menu_games/AbstractProgram.h | 39 ++++--- .../abstraction/PrismMenuGameTest.cpp | 102 ++++++++---------- 6 files changed, 130 insertions(+), 95 deletions(-) diff --git a/src/models/symbolic/Model.cpp b/src/models/symbolic/Model.cpp index 408f8af7c..d61d51115 100644 --- a/src/models/symbolic/Model.cpp +++ b/src/models/symbolic/Model.cpp @@ -66,11 +66,12 @@ namespace storm { template storm::dd::Bdd Model::getStates(std::string const& label) const { STORM_LOG_THROW(labelToExpressionMap.find(label) != labelToExpressionMap.end(), storm::exceptions::IllegalArgumentException, "The label " << label << " is invalid for the labeling of the model."); - return rowExpressionAdapter->translateExpression(labelToExpressionMap.at(label)).toBdd() && this->reachableStates; + return this->getStates(labelToExpressionMap.at(label)); } template storm::dd::Bdd Model::getStates(storm::expressions::Expression const& expression) const { + STORM_LOG_THROW(rowExpressionAdapter != nullptr, storm::exceptions::InvalidOperationException, "Cannot create BDD for expression without expression adapter."); return rowExpressionAdapter->translateExpression(expression).toBdd() && this->reachableStates; } diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index ec723eedf..7e3c6f43f 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -94,6 +94,10 @@ namespace storm { bool Expression::isFalse() const { return this->getBaseExpression().isFalse(); } + + bool Expression::isSame(storm::expressions::Expression const& other) const { + return this->expressionPtr == other.expressionPtr; + } std::set Expression::getVariables() const { std::set result; diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 285a508b2..109439948 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -190,6 +190,14 @@ namespace storm { */ bool isFalse() const; + /*! + * Checks whether the two expressions are the same. Note that this does not check for syntactical or even + * semantical equivalence, but only returns true if both are the very same expressions. + * + * @return True iff the two expressions are the same. + */ + bool isSame(storm::expressions::Expression const& other) const; + /*! * Retrieves whether this expression is a relation expression, i.e., an expression that has a relation * (equal, not equal, less, less or equal, etc.) as its top-level operator. diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index c515a9074..3d57f0bdd 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -5,6 +5,8 @@ #include "src/storage/dd/CuddDdManager.h" #include "src/storage/dd/CuddAdd.h" +#include "src/models/symbolic/StandardRewardModel.h" + #include "src/utility/macros.h" #include "src/utility/solver.h" #include "src/exceptions/WrongFormatException.h" @@ -15,7 +17,7 @@ namespace storm { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), lastAbstractBdd(ddInformation.manager->getBddZero()), lastAbstractAdd(ddInformation.manager->getAddZero()), lastReachableStates(ddInformation.manager->getBddZero()) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), currentGame(nullptr) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. @@ -65,10 +67,15 @@ namespace storm { // Finally, retrieve the command-update probability ADD, so we can multiply it with the abstraction BDD later. commandUpdateProbabilitiesAdd = modules.front().getCommandUpdateProbabilitiesAdd(); + + // Finally, we build the game the first time. + currentGame = buildGame(); } template void AbstractProgram::refine(std::vector const& predicates) { + STORM_LOG_THROW(!predicates.empty(), storm::exceptions::InvalidArgumentException, "Cannot refine without predicates."); + // Add the predicates to the global list of predicates. uint_fast64_t firstNewPredicateIndex = expressionInformation.predicates.size(); expressionInformation.predicates.insert(expressionInformation.predicates.end(), predicates.begin(), predicates.end()); @@ -92,20 +99,34 @@ namespace storm { // Refine initial state abstractor. initialStateAbstractor.refine(newPredicateIndices); + + // Finally, we rebuild the game. + currentGame = buildGame(); + } + + template + storm::models::symbolic::StochasticTwoPlayerGame AbstractProgram::getAbstractGame() { + STORM_LOG_ASSERT(currentGame != nullptr, "Game was not properly created."); + return *currentGame; + } + + template + storm::dd::Bdd AbstractProgram::getStates(storm::expressions::Expression const& predicate) { + STORM_LOG_ASSERT(currentGame != nullptr, "Game was not properly created."); + uint_fast64_t index = 0; + for (auto const& knownPredicate : expressionInformation.predicates) { + if (knownPredicate.isSame(predicate)) { + return currentGame->getReachableStates() && ddInformation.predicateBdds[index].first; + } + ++index; + } + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The given predicate is illegal, since it was neither used as an initial predicate nor used to refine the abstraction."); } template - storm::dd::Add AbstractProgram::getAbstractAdd() { + std::unique_ptr> AbstractProgram::buildGame() { // As long as there is only one module, we only build its game representation. std::pair, uint_fast64_t> gameBdd = modules.front().getAbstractBdd(); - - // If the abstraction did not change, we can return the most recenty obtained ADD. - if (gameBdd.first == lastAbstractBdd) { - return lastAbstractAdd; - } - - // Otherwise, we remember that the abstract BDD changed and perform a reachability analysis. - lastAbstractBdd = gameBdd.first; // Construct a set of all unnecessary variables, so we can abstract from it. std::set variablesToAbstract = {ddInformation.commandDdVariable, ddInformation.updateDdVariable}; @@ -114,12 +135,13 @@ namespace storm { } // Do a reachability analysis on the raw transition relation. - storm::dd::Bdd transitionRelation = lastAbstractBdd.existsAbstract(variablesToAbstract); - lastReachableStates = this->getReachableStates(initialStateAbstractor.getAbstractStates(), transitionRelation); - + storm::dd::Bdd transitionRelation = gameBdd.first.existsAbstract(variablesToAbstract); + storm::dd::Bdd initialStates = initialStateAbstractor.getAbstractStates(); + storm::dd::Bdd reachableStates = this->getReachableStates(initialStates, transitionRelation); + // Find the deadlock states in the model. storm::dd::Bdd deadlockStates = transitionRelation.existsAbstract(ddInformation.successorVariables); - deadlockStates = lastReachableStates && !deadlockStates; + deadlockStates = reachableStates && !deadlockStates; // If there are deadlock states, we fix them now. storm::dd::Add deadlockTransitions = ddInformation.manager->getAddZero(); @@ -127,15 +149,18 @@ namespace storm { deadlockTransitions = (deadlockStates && ddInformation.allPredicateIdentities && ddInformation.manager->getEncoding(ddInformation.commandDdVariable, 0) && ddInformation.manager->getEncoding(ddInformation.updateDdVariable, 0) && ddInformation.getMissingOptionVariableCube(0, gameBdd.second)).toAdd(); } - // Construct the final game by cutting away the transitions of unreachable states. - lastAbstractAdd = (lastAbstractBdd && lastReachableStates).toAdd() * commandUpdateProbabilitiesAdd + deadlockTransitions; - return lastAbstractAdd; - } + // Construct the transition matrix by cutting away the transitions of unreachable states. + storm::dd::Add transitionMatrix = (gameBdd.first && reachableStates).toAdd() * commandUpdateProbabilitiesAdd + deadlockTransitions; - - template - storm::dd::Bdd AbstractProgram::getReachableStates() { - return lastReachableStates; + std::set usedPlayer2Variables; + for (uint_fast64_t index = 0; index < ddInformation.optionDdVariables.size(); ++index) { + usedPlayer2Variables.insert(usedPlayer2Variables.end(), ddInformation.optionDdVariables[index].first); + } + + std::set allNondeterminismVariables = usedPlayer2Variables; + allNondeterminismVariables.insert(ddInformation.commandDdVariable); + + return std::unique_ptr>(new storm::models::symbolic::StochasticTwoPlayerGame(ddInformation.manager, reachableStates, initialStates, transitionMatrix, ddInformation.sourceVariables, nullptr, ddInformation.successorVariables, nullptr, ddInformation.predicateDdVariables, {ddInformation.commandDdVariable}, usedPlayer2Variables, allNondeterminismVariables)); } template diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index c1f54b585..6bb86b2c5 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -10,6 +10,8 @@ #include "src/storage/expressions/Expression.h" +#include "src/models/symbolic/StochasticTwoPlayerGame.h" + namespace storm { namespace utility { namespace solver { @@ -17,6 +19,13 @@ namespace storm { } } + namespace models { + namespace symbolic { + template + class StochasticTwoPlayerGame; + } + } + namespace prism { // Forward-declare concrete Program class. class Program; @@ -40,17 +49,18 @@ namespace storm { /*! * Uses the current set of predicates to derive the abstract menu game in the form of an ADD. * - * @return The ADD representing the game. + * @return The abstract stochastic two player game. */ - storm::dd::Add getAbstractAdd(); + storm::models::symbolic::StochasticTwoPlayerGame getAbstractGame(); /*! - * Retrieves the reachable state space of the abstract game (that was previously retrieved via the - * appropriate method. + * Retrieves the set of states (represented by a BDD) satisfying the given predicate, assuming that it + * was either given as an initial predicate or used as a refining predicate later. * - * @return The reachable state space in the form of a BDD. + * @param predicate The predicate for which to retrieve the states. + * @return The BDD representing the set of states. */ - storm::dd::Bdd getReachableStates(); + storm::dd::Bdd getStates(storm::expressions::Expression const& predicate); /*! * Refines the abstract module with the given predicates. @@ -70,6 +80,13 @@ namespace storm { */ storm::dd::Bdd getReachableStates(storm::dd::Bdd const& initialStates, storm::dd::Bdd const& transitionRelation); + /*! + * Builds the stochastic game representing the abstraction of the program. + * + * @return The stochastic game. + */ + std::unique_ptr> buildGame(); + // A factory that can be used to create new SMT solvers. std::unique_ptr smtSolverFactory; @@ -91,14 +108,8 @@ namespace storm { // An ADD characterizing the probabilities of commands and their updates. storm::dd::Add commandUpdateProbabilitiesAdd; - // A BDD that is the result of the last abstraction of the system. - storm::dd::Bdd lastAbstractBdd; - - // An ADD that is the result of the last abstraction of the system. - storm::dd::Add lastAbstractAdd; - - // A BDD that is the result of the reachability analysis on the abstraction of the system. - storm::dd::Bdd lastReachableStates; + // The current game-based abstraction. + std::unique_ptr> currentGame; }; } } diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 0fcf721dc..8d5792cf1 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -13,6 +13,8 @@ #include "src/storage/dd/CuddAdd.h" #include "src/storage/dd/CuddBdd.h" +#include "src/models/symbolic/StandardRewardModel.h" + #include "src/utility/solver.h" TEST(PrismMenuGame, DieAbstractionTest) { @@ -25,11 +27,10 @@ TEST(PrismMenuGame, DieAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(15, abstraction.getNonZeroCount()); - EXPECT_EQ(2, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(15, game.getNumberOfTransitions()); + EXPECT_EQ(2, game.getNumberOfStates()); } TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { @@ -42,14 +43,12 @@ TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("s") == manager.integer(7)})); - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(15, abstraction.getNonZeroCount()); - EXPECT_EQ(3, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(15, game.getNumberOfTransitions()); + EXPECT_EQ(3, game.getNumberOfStates()); } TEST(PrismMenuGame, DieFullAbstractionTest) { @@ -77,11 +76,10 @@ TEST(PrismMenuGame, DieFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(20, abstraction.getNonZeroCount()); - EXPECT_EQ(13, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(20, game.getNumberOfTransitions()); + EXPECT_EQ(13, game.getNumberOfStates()); } TEST(PrismMenuGame, CrowdsAbstractionTest) { @@ -95,11 +93,10 @@ TEST(PrismMenuGame, CrowdsAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(16, abstraction.getNonZeroCount()); - EXPECT_EQ(2, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(16, game.getNumberOfTransitions()); + EXPECT_EQ(2, game.getNumberOfStates()); } TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { @@ -113,14 +110,12 @@ TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("observe0") + manager.getVariableExpression("observe1") + manager.getVariableExpression("observe2") + manager.getVariableExpression("observe3") + manager.getVariableExpression("observe4") <= manager.getVariableExpression("runCount")})); - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - EXPECT_EQ(38, abstraction.getNonZeroCount()); - EXPECT_EQ(4, abstractProgram.getReachableStates().getNonZeroCount()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + + EXPECT_EQ(38, game.getNumberOfTransitions()); + EXPECT_EQ(4, game.getNumberOfStates()); } TEST(PrismMenuGame, CrowdsFullAbstractionTest) { @@ -188,11 +183,10 @@ TEST(PrismMenuGame, CrowdsFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(15113, abstraction.getNonZeroCount()); - EXPECT_EQ(8607, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(15113, game.getNumberOfTransitions()); + EXPECT_EQ(8607, game.getNumberOfStates()); } TEST(PrismMenuGame, TwoDiceAbstractionTest) { @@ -208,11 +202,10 @@ TEST(PrismMenuGame, TwoDiceAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(58, abstraction.getNonZeroCount()); - EXPECT_EQ(4, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(58, game.getNumberOfTransitions()); + EXPECT_EQ(4, game.getNumberOfStates()); } TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { @@ -228,14 +221,12 @@ TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("d1") + manager.getVariableExpression("d2") == manager.integer(7)})); - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - - EXPECT_EQ(212, abstraction.getNonZeroCount()); - EXPECT_EQ(8, abstractProgram.getReachableStates().getNonZeroCount()); + + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + + EXPECT_EQ(212, game.getNumberOfTransitions()); + EXPECT_EQ(8, game.getNumberOfStates()); } TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { @@ -282,11 +273,10 @@ TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(436, abstraction.getNonZeroCount()); - EXPECT_EQ(169, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(436, game.getNumberOfTransitions()); + EXPECT_EQ(169, game.getNumberOfStates()); } TEST(PrismMenuGame, WlanAbstractionTest) { @@ -303,11 +293,10 @@ TEST(PrismMenuGame, WlanAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(307, abstraction.getNonZeroCount()); - EXPECT_EQ(4, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(307, game.getNumberOfTransitions()); + EXPECT_EQ(4, game.getNumberOfStates()); } TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { @@ -324,14 +313,12 @@ TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("backoff1") < manager.integer(7)})); - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); - - EXPECT_EQ(612, abstraction.getNonZeroCount()); - EXPECT_EQ(8, abstractProgram.getReachableStates().getNonZeroCount()); + + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + + EXPECT_EQ(612, game.getNumberOfTransitions()); + EXPECT_EQ(8, game.getNumberOfStates()); } TEST(PrismMenuGame, WlanFullAbstractionTest) { @@ -446,11 +433,10 @@ TEST(PrismMenuGame, WlanFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::dd::Add abstraction; - ASSERT_NO_THROW(abstraction = abstractProgram.getAbstractAdd()); + storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(59, abstraction.getNonZeroCount()); - EXPECT_EQ(37, abstractProgram.getReachableStates().getNonZeroCount()); + EXPECT_EQ(59, game.getNumberOfTransitions()); + EXPECT_EQ(37, game.getNumberOfStates()); } #endif \ No newline at end of file From 0bd0b963d72005aa6af34bd7626c34ad5a2b614b Mon Sep 17 00:00:00 2001 From: dehnert Date: Sat, 19 Sep 2015 19:38:36 +0200 Subject: [PATCH 017/400] introduced new menu game class Former-commit-id: f27691f9d69eed78a52f05e2f12692a7d0964758 --- src/models/symbolic/Model.h | 6 +- src/storage/expressions/Expression.cpp | 2 +- src/storage/expressions/Expression.h | 16 +++- .../prism/menu_games/AbstractProgram.cpp | 8 +- .../prism/menu_games/AbstractProgram.h | 9 +-- .../menu_games/AbstractionDdInformation.cpp | 1 + .../menu_games/AbstractionDdInformation.h | 4 + src/storage/prism/menu_games/MenuGame.cpp | 53 +++++++++++++ src/storage/prism/menu_games/MenuGame.h | 78 +++++++++++++++++++ .../abstraction/PrismMenuGameTest.cpp | 24 +++--- 10 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 src/storage/prism/menu_games/MenuGame.cpp create mode 100644 src/storage/prism/menu_games/MenuGame.h diff --git a/src/models/symbolic/Model.h b/src/models/symbolic/Model.h index b70c39b97..7c0b49118 100644 --- a/src/models/symbolic/Model.h +++ b/src/models/symbolic/Model.h @@ -117,7 +117,7 @@ namespace storm { * @param label The label for which to get the labeled states. * @return The set of states labeled with the requested label in the form of a bit vector. */ - storm::dd::Bdd getStates(std::string const& label) const; + virtual storm::dd::Bdd getStates(std::string const& label) const; /*! * Returns the set of states labeled satisfying the given expression (that must be of boolean type). @@ -125,7 +125,7 @@ namespace storm { * @param expression The expression that needs to hold in the states. * @return The set of states labeled satisfying the given expression. */ - storm::dd::Bdd getStates(storm::expressions::Expression const& expression) const; + virtual storm::dd::Bdd getStates(storm::expressions::Expression const& expression) const; /*! * Retrieves whether the given label is a valid label in this model. @@ -133,7 +133,7 @@ namespace storm { * @param label The label to be checked for validity. * @return True if the given label is valid in this model. */ - bool hasLabel(std::string const& label) const; + virtual bool hasLabel(std::string const& label) const; /*! * Retrieves the matrix representing the transitions of the model. diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index 7e3c6f43f..0f408c9b4 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -95,7 +95,7 @@ namespace storm { return this->getBaseExpression().isFalse(); } - bool Expression::isSame(storm::expressions::Expression const& other) const { + bool Expression::areSame(storm::expressions::Expression const& other) const { return this->expressionPtr == other.expressionPtr; } diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 109439948..9455f2acb 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -196,7 +196,7 @@ namespace storm { * * @return True iff the two expressions are the same. */ - bool isSame(storm::expressions::Expression const& other) const; + bool areSame(storm::expressions::Expression const& other) const; /*! * Retrieves whether this expression is a relation expression, i.e., an expression that has a relation @@ -340,14 +340,22 @@ namespace storm { } } -//specialize namespace std { - template<> - struct less < storm::expressions::Expression > { + template <> + struct less { bool operator()(const storm::expressions::Expression& lhs, const storm::expressions::Expression& rhs) const { return lhs.getBaseExpressionPointer() < rhs.getBaseExpressionPointer(); } }; } +namespace std { + template <> + struct hash { + size_t operator()(const storm::expressions::Expression& expr) const { + return reinterpret_cast(expr.getBaseExpressionPointer().get()); + } + }; +} + #endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSION_H_ */ \ No newline at end of file diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 3d57f0bdd..e0d79e23b 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -105,7 +105,7 @@ namespace storm { } template - storm::models::symbolic::StochasticTwoPlayerGame AbstractProgram::getAbstractGame() { + MenuGame AbstractProgram::getAbstractGame() { STORM_LOG_ASSERT(currentGame != nullptr, "Game was not properly created."); return *currentGame; } @@ -115,7 +115,7 @@ namespace storm { STORM_LOG_ASSERT(currentGame != nullptr, "Game was not properly created."); uint_fast64_t index = 0; for (auto const& knownPredicate : expressionInformation.predicates) { - if (knownPredicate.isSame(predicate)) { + if (knownPredicate.areSame(predicate)) { return currentGame->getReachableStates() && ddInformation.predicateBdds[index].first; } ++index; @@ -124,7 +124,7 @@ namespace storm { } template - std::unique_ptr> AbstractProgram::buildGame() { + std::unique_ptr> AbstractProgram::buildGame() { // As long as there is only one module, we only build its game representation. std::pair, uint_fast64_t> gameBdd = modules.front().getAbstractBdd(); @@ -160,7 +160,7 @@ namespace storm { std::set allNondeterminismVariables = usedPlayer2Variables; allNondeterminismVariables.insert(ddInformation.commandDdVariable); - return std::unique_ptr>(new storm::models::symbolic::StochasticTwoPlayerGame(ddInformation.manager, reachableStates, initialStates, transitionMatrix, ddInformation.sourceVariables, nullptr, ddInformation.successorVariables, nullptr, ddInformation.predicateDdVariables, {ddInformation.commandDdVariable}, usedPlayer2Variables, allNondeterminismVariables)); + return std::unique_ptr>(new MenuGame(ddInformation.manager, reachableStates, initialStates, transitionMatrix, ddInformation.sourceVariables, ddInformation.successorVariables, ddInformation.predicateDdVariables, {ddInformation.commandDdVariable}, usedPlayer2Variables, allNondeterminismVariables, ddInformation.updateDdVariable, ddInformation.expressionToBddMap)); } template diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 6bb86b2c5..807d5f17e 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -7,11 +7,10 @@ #include "src/storage/prism/menu_games/AbstractionExpressionInformation.h" #include "src/storage/prism/menu_games/StateSetAbstractor.h" #include "src/storage/prism/menu_games/AbstractModule.h" +#include "src/storage/prism/menu_games/MenuGame.h" #include "src/storage/expressions/Expression.h" -#include "src/models/symbolic/StochasticTwoPlayerGame.h" - namespace storm { namespace utility { namespace solver { @@ -51,7 +50,7 @@ namespace storm { * * @return The abstract stochastic two player game. */ - storm::models::symbolic::StochasticTwoPlayerGame getAbstractGame(); + MenuGame getAbstractGame(); /*! * Retrieves the set of states (represented by a BDD) satisfying the given predicate, assuming that it @@ -85,7 +84,7 @@ namespace storm { * * @return The stochastic game. */ - std::unique_ptr> buildGame(); + std::unique_ptr> buildGame(); // A factory that can be used to create new SMT solvers. std::unique_ptr smtSolverFactory; @@ -109,7 +108,7 @@ namespace storm { storm::dd::Add commandUpdateProbabilitiesAdd; // The current game-based abstraction. - std::unique_ptr> currentGame; + std::unique_ptr> currentGame; }; } } diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.cpp b/src/storage/prism/menu_games/AbstractionDdInformation.cpp index bb98b5a0b..28cfcf886 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.cpp +++ b/src/storage/prism/menu_games/AbstractionDdInformation.cpp @@ -55,6 +55,7 @@ namespace storm { allPredicateIdentities &= predicateIdentities.back(); sourceVariables.insert(newMetaVariable.first); successorVariables.insert(newMetaVariable.second); + expressionToBddMap[predicate] = predicateBdds.back().first; } template diff --git a/src/storage/prism/menu_games/AbstractionDdInformation.h b/src/storage/prism/menu_games/AbstractionDdInformation.h index ef8e08267..80cb914df 100644 --- a/src/storage/prism/menu_games/AbstractionDdInformation.h +++ b/src/storage/prism/menu_games/AbstractionDdInformation.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "src/storage/dd/DdType.h" #include "src/storage/expressions/Variable.h" @@ -100,6 +101,9 @@ namespace storm { // The DD variables encoding the nondeterministic choices of player 2. std::vector>> optionDdVariables; + + // A mapping from the predicates to the BDDs. + std::map> expressionToBddMap; }; } diff --git a/src/storage/prism/menu_games/MenuGame.cpp b/src/storage/prism/menu_games/MenuGame.cpp new file mode 100644 index 000000000..d5e0b98f4 --- /dev/null +++ b/src/storage/prism/menu_games/MenuGame.cpp @@ -0,0 +1,53 @@ +#include "src/storage/prism/menu_games/MenuGame.h" + +#include "src/exceptions/InvalidOperationException.h" +#include "src/exceptions/InvalidArgumentException.h" + +#include "src/storage/dd/CuddBdd.h" +#include "src/storage/dd/CuddAdd.h" + +#include "src/models/symbolic/StandardRewardModel.h" + +namespace storm { + namespace prism { + namespace menu_games { + + template + MenuGame::MenuGame(std::shared_ptr> manager, + storm::dd::Bdd reachableStates, + storm::dd::Bdd initialStates, + storm::dd::Add transitionMatrix, + std::set const& rowVariables, + std::set const& columnVariables, + std::vector> const& rowColumnMetaVariablePairs, + std::set const& player1Variables, + std::set const& player2Variables, + std::set const& allNondeterminismVariables, + storm::expressions::Variable const& updateVariable, + std::map> const& expressionToBddMap) : storm::models::symbolic::StochasticTwoPlayerGame(manager, reachableStates, initialStates, transitionMatrix, rowVariables, nullptr, columnVariables, nullptr, rowColumnMetaVariablePairs, player1Variables, player2Variables, allNondeterminismVariables), updateVariable(updateVariable), expressionToBddMap(expressionToBddMap) { + // Intentionally left empty. + } + + template + storm::dd::Bdd MenuGame::getStates(std::string const& label) const { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Menu games do not provide labels."); + } + + template + storm::dd::Bdd MenuGame::getStates(storm::expressions::Expression const& expression) const { + auto it = expressionToBddMap.find(expression); + STORM_LOG_THROW(it != expressionToBddMap.end(), storm::exceptions::InvalidArgumentException, "The given expression was not used in the abstraction process and can therefore not be retrieved."); + return it->second && this->getReachableStates(); + } + + template + bool MenuGame::hasLabel(std::string const& label) const { + return false; + } + + template class MenuGame; + + } // namespace menu_games + } // namespace prism +} // namespace storm + diff --git a/src/storage/prism/menu_games/MenuGame.h b/src/storage/prism/menu_games/MenuGame.h new file mode 100644 index 000000000..6c7416068 --- /dev/null +++ b/src/storage/prism/menu_games/MenuGame.h @@ -0,0 +1,78 @@ +#ifndef STORM_PRISM_MENU_GAMES_MENUGAME_H_ +#define STORM_PRISM_MENU_GAMES_MENUGAME_H_ + +#include + +#include "src/models/symbolic/StochasticTwoPlayerGame.h" + +#include "src/utility/OsDetection.h" + +namespace storm { + namespace prism { + namespace menu_games { + + /*! + * This class represents a discrete-time stochastic two-player game. + */ + template + class MenuGame : public storm::models::symbolic::StochasticTwoPlayerGame { + public: + typedef typename storm::models::symbolic::StochasticTwoPlayerGame::RewardModelType RewardModelType; + + MenuGame(MenuGame const& other) = default; + MenuGame& operator=(MenuGame const& other) = default; + +#ifndef WINDOWS + MenuGame(MenuGame&& other) = default; + MenuGame& operator=(MenuGame&& other) = default; +#endif + + /*! + * Constructs a model from the given data. + * + * @param manager The manager responsible for the decision diagrams. + * @param reachableStates A DD representing the reachable states. + * @param initialStates A DD representing the initial states of the model. + * @param transitionMatrix The matrix representing the transitions in the model. + * @param rowVariables The set of row meta variables used in the DDs. + * @param columVariables The set of column meta variables used in the DDs. + * @param rowColumnMetaVariablePairs All pairs of row/column meta variables. + * @param player1Variables The meta variables used to encode the nondeterministic choices of player 1. + * @param player2Variables The meta variables used to encode the nondeterministic choices of player 2. + * @param allNondeterminismVariables The meta variables used to encode the nondeterminism in the model. + * @param updateVariable The variable used to encode the different updates of the probabilistic program. + * @param expressionToBddMap A mapping from expressions (used) in the abstraction to the BDDs encoding + * them. + */ + MenuGame(std::shared_ptr> manager, + storm::dd::Bdd reachableStates, + storm::dd::Bdd initialStates, + storm::dd::Add transitionMatrix, + std::set const& rowVariables, + std::set const& columnVariables, + std::vector> const& rowColumnMetaVariablePairs, + std::set const& player1Variables, + std::set const& player2Variables, + std::set const& allNondeterminismVariables, + storm::expressions::Variable const& updateVariable, + std::map> const& expressionToBddMap); + + virtual storm::dd::Bdd getStates(std::string const& label) const override; + + virtual storm::dd::Bdd getStates(storm::expressions::Expression const& expression) const override; + + virtual bool hasLabel(std::string const& label) const override; + + private: + // The meta variable used to encode the updates. + storm::expressions::Variable updateVariable; + + // A mapping from expressions that were used in the abstraction process to the the BDDs representing them. + std::map> expressionToBddMap; + }; + + } // namespace menu_games + } // namespace prism +} // namespace storm + +#endif /* STORM_PRISM_MENU_GAMES_MENUGAME_H_ */ diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 8d5792cf1..8eb25f9ed 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -27,7 +27,7 @@ TEST(PrismMenuGame, DieAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(15, game.getNumberOfTransitions()); EXPECT_EQ(2, game.getNumberOfStates()); @@ -45,7 +45,7 @@ TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("s") == manager.integer(7)})); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(15, game.getNumberOfTransitions()); EXPECT_EQ(3, game.getNumberOfStates()); @@ -76,7 +76,7 @@ TEST(PrismMenuGame, DieFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(20, game.getNumberOfTransitions()); EXPECT_EQ(13, game.getNumberOfStates()); @@ -93,7 +93,7 @@ TEST(PrismMenuGame, CrowdsAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(16, game.getNumberOfTransitions()); EXPECT_EQ(2, game.getNumberOfStates()); @@ -112,7 +112,7 @@ TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("observe0") + manager.getVariableExpression("observe1") + manager.getVariableExpression("observe2") + manager.getVariableExpression("observe3") + manager.getVariableExpression("observe4") <= manager.getVariableExpression("runCount")})); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(38, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); @@ -183,7 +183,7 @@ TEST(PrismMenuGame, CrowdsFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(15113, game.getNumberOfTransitions()); EXPECT_EQ(8607, game.getNumberOfStates()); @@ -202,7 +202,7 @@ TEST(PrismMenuGame, TwoDiceAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(58, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); @@ -223,7 +223,7 @@ TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("d1") + manager.getVariableExpression("d2") == manager.integer(7)})); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(212, game.getNumberOfTransitions()); EXPECT_EQ(8, game.getNumberOfStates()); @@ -273,7 +273,7 @@ TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(436, game.getNumberOfTransitions()); EXPECT_EQ(169, game.getNumberOfStates()); @@ -293,7 +293,7 @@ TEST(PrismMenuGame, WlanAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(307, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); @@ -315,7 +315,7 @@ TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { ASSERT_NO_THROW(abstractProgram.refine({manager.getVariableExpression("backoff1") < manager.integer(7)})); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(612, game.getNumberOfTransitions()); EXPECT_EQ(8, game.getNumberOfStates()); @@ -433,7 +433,7 @@ TEST(PrismMenuGame, WlanFullAbstractionTest) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); - storm::models::symbolic::StochasticTwoPlayerGame game = abstractProgram.getAbstractGame(); + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); EXPECT_EQ(59, game.getNumberOfTransitions()); EXPECT_EQ(37, game.getNumberOfStates()); From 0cfc4dfd4d51ef7eb1e6424a9683133e44002f1d Mon Sep 17 00:00:00 2001 From: dehnert Date: Mon, 21 Sep 2015 09:53:50 +0200 Subject: [PATCH 018/400] (re)introduced min/maxAbstractRepresentative for ADDs Former-commit-id: 5a5d269339656fbf3b64fcb766f870e16cc43570 --- resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h | 2 + .../3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c | 440 ++++++++++++++++++ .../3rdparty/cudd-2.5.0/src/cudd/cuddInt.h | 2 + .../3rdparty/cudd-2.5.0/src/obj/cuddObj.cc | 18 + .../3rdparty/cudd-2.5.0/src/obj/cuddObj.hh | 2 + src/storage/dd/CuddAdd.cpp | 32 ++ src/storage/dd/CuddAdd.h | 22 + 7 files changed, 518 insertions(+) diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h b/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h index bba6713d2..542dd8fdf 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h @@ -769,6 +769,8 @@ extern DdNode * Cudd_addUnivAbstract (DdManager *manager, DdNode *f, DdNode *cub extern DdNode * Cudd_addOrAbstract (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * Cudd_addMinAbstract(DdManager * manager, DdNode * f, DdNode * cube); extern DdNode * Cudd_addMaxAbstract(DdManager * manager, DdNode * f, DdNode * cube); +extern DdNode * Cudd_addMinAbstractRepresentative(DdManager * manager, DdNode * f, DdNode * cube); +extern DdNode * Cudd_addMaxAbstractRepresentative(DdManager * manager, DdNode * f, DdNode * cube); extern DdNode * Cudd_addApply (DdManager *dd, DdNode * (*)(DdManager *, DdNode **, DdNode **), DdNode *f, DdNode *g); extern DdNode * Cudd_addPlus (DdManager *dd, DdNode **f, DdNode **g); extern DdNode * Cudd_addTimes (DdManager *dd, DdNode **f, DdNode **g); diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c index 7d774586c..11e95d8b5 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c @@ -310,6 +310,78 @@ Cudd_addMaxAbstract( } /* end of Cudd_addMaxAbstract */ +/**Function******************************************************************** + + Synopsis [Just like Cudd_addMinAbstract, but instead of abstracting the + variables in the given cube, picks a unique representative that realizes th + minimal function value.] + + Description [Returns the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addMaxAbstractRepresentative] + + Note: Added by Christian Dehnert 8/5/14 + + ******************************************************************************/ +DdNode * +Cudd_addMinAbstractRepresentative( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddMinAbstractRepresentativeRecur(manager, f, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_addMinRepresentative */ + +/**Function******************************************************************** + + Synopsis [Just like Cudd_addMaxAbstract, but instead of abstracting the + variables in the given cube, picks a unique representative that realizes th + maximal function value.] + + Description [Returns the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addMinAbstractRepresentative] + + Note: Added by Christian Dehnert 8/5/14 + + ******************************************************************************/ +DdNode * +Cudd_addMaxAbstractRepresentative( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (addCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract cubes"); + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddAddMaxAbstractRepresentativeRecur(manager, f, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_addMaxRepresentative */ + /*---------------------------------------------------------------------------*/ /* Definition of internal functions */ /*---------------------------------------------------------------------------*/ @@ -809,6 +881,374 @@ cuddAddMaxAbstractRecur( /* Definition of static functions */ /*---------------------------------------------------------------------------*/ +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addMinAbstractRepresentative.] + + Description [Performs the recursive step of Cudd_addMinAbstractRepresentative. + Returns the ADD obtained by picking a representative over the variables in + the given cube for all other valuations. Returns the resulting ADD if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + + ******************************************************************************/ +DdNode * +cuddAddMinAbstractRepresentativeRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *zero, *one, *res1Inf, *res2Inf, *left, *right, *tmp, *tmp2; + + zero = DD_ZERO(manager); + one = DD_ONE(manager); + + /* Cube is guaranteed to be a cube at this point. */ + if (cuddIsConstant(f)) { + if (cuddIsConstant(cube)) { + return(one); + } else { + return(cube); + } + } + + /* Abstract a variable that does not appear in f. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res = cuddAddMinAbstractRepresentativeRecur(manager, f, cuddT(cube)); + if (res == NULL) { + return(NULL); + } + + // Fill in the missing variables to make representative unique. Set the "non-used-branch" to infinity, otherwise + // this may interfere with the minimum calculation. + cuddRef(res); + cuddRef(zero); + res1 = cuddUniqueInter(manager, (int) cube->index, zero, res); + if (res1 == NULL) { + Cudd_RecursiveDeref(manager,res); + Cudd_RecursiveDeref(manager,zero); + return(NULL); + } + Cudd_RecursiveDeref(manager, res); + cuddDeref(zero); + return(res1); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addMinAbstractRepresentative, f, cube)) != NULL) { + return(res); + } + + + E = cuddE(f); + T = cuddT(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddMinAbstractRepresentativeRecur(manager, E, cuddT(cube)); + if (res1 == NULL) { + return(NULL); + } + cuddRef(res1); + + res2 = cuddAddMinAbstractRepresentativeRecur(manager, T, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager, res1); + return(NULL); + } + cuddRef(res2); + + left = cuddAddMinAbstractRecur(manager, E, cuddT(cube)); + if (left == NULL) { + Cudd_RecursiveDeref(manager, res1); + Cudd_RecursiveDeref(manager, res2); + return(NULL); + } + cuddRef(left); + right = cuddAddMinAbstractRecur(manager, T, cuddT(cube)); + if (right == NULL) { + Cudd_RecursiveDeref(manager, res1); + Cudd_RecursiveDeref(manager, res2); + Cudd_RecursiveDeref(manager, left); + return(NULL); + } + cuddRef(right); + + tmp = cuddAddApplyRecur(manager, Cudd_addLessThanEquals, left, right); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,left); + Cudd_RecursiveDeref(manager,right); + return(NULL); + } + cuddRef(tmp); + + Cudd_RecursiveDeref(manager, left); + Cudd_RecursiveDeref(manager, right); + + cuddRef(zero); + res1Inf = cuddAddIteRecur(manager, tmp, res1, zero); + if (res1Inf == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,tmp); + cuddDeref(zero); + return(NULL); + } + cuddRef(res1Inf); + Cudd_RecursiveDeref(manager,res1); + cuddDeref(zero); + + tmp2 = cuddAddCmplRecur(manager,tmp); + if (tmp2 == NULL) { + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,left); + Cudd_RecursiveDeref(manager,right); + Cudd_RecursiveDeref(manager,tmp); + return(NULL); + } + cuddRef(tmp2); + Cudd_RecursiveDeref(manager,tmp); + + cuddRef(zero); + res2Inf = cuddAddIteRecur(manager, tmp2, res2, zero); + if (res2Inf == NULL) { + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,res1Inf); + Cudd_RecursiveDeref(manager,tmp2); + cuddDeref(zero); + return(NULL); + } + cuddRef(res2Inf); + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,tmp2); + cuddDeref(zero); + + res = (res1Inf == res2Inf) ? res1Inf : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1Inf); + Cudd_RecursiveDeref(manager,res2Inf); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1Inf); + Cudd_RecursiveDeref(manager,res2Inf); + cuddCacheInsert2(manager, Cudd_addMinAbstractRepresentative, f, cube, res); + cuddDeref(res); + return(res); + } + else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddMinAbstractRepresentativeRecur(manager, E, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddMinAbstractRepresentativeRecur(manager, T, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : cuddUniqueInter(manager, (int) f->index, res2, res1); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addMinAbstractRepresentative, f, cube, res); + return(res); + } + +} /* end of cuddAddMinAbstractRepresentativeRecur */ + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addMaxAbstractRepresentative.] + + Description [Performs the recursive step of Cudd_addMaxAbstractRepresentative. + Returns the ADD obtained by picking a representative over the variables in + the given cube for all other valuations. Returns the resulting ADD if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + + ******************************************************************************/ +DdNode * +cuddAddMaxAbstractRepresentativeRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *T, *E, *res, *res1, *res2, *zero, *one, *res1Inf, *res2Inf, *left, *right, *tmp, *tmp2; + + zero = DD_ZERO(manager); + one = DD_ONE(manager); + + /* Cube is guaranteed to be a cube at this point. */ + if (cuddIsConstant(f)) { + if (cuddIsConstant(cube)) { + return(one); + } else { + return(cube); + } + } + + /* Abstract a variable that does not appear in f. */ + if (cuddI(manager,f->index) > cuddI(manager,cube->index)) { + res = cuddAddMaxAbstractRepresentativeRecur(manager, f, cuddT(cube)); + + if (res == NULL) { + return(NULL); + } + + // Fill in the missing variables to make representative unique. + cuddRef(res); + cuddRef(zero); + res1 = cuddUniqueInter(manager, (int) cube->index, zero, res); + if (res1 == NULL) { + Cudd_RecursiveDeref(manager, res); + Cudd_RecursiveDeref(manager,zero); + return(NULL); + } + Cudd_RecursiveDeref(manager,res); + Cudd_RecursiveDeref(manager,zero); + return(res1); + } + + if ((res = cuddCacheLookup2(manager, Cudd_addMaxAbstractRepresentative, f, cube)) != NULL) { + return(res); + } + + + E = cuddE(f); + T = cuddT(f); + + /* If the two indices are the same, so are their levels. */ + if (f->index == cube->index) { + res1 = cuddAddMaxAbstractRepresentativeRecur(manager, E, cuddT(cube)); + if (res1 == NULL) { + return(NULL); + } + cuddRef(res1); + + res2 = cuddAddMaxAbstractRepresentativeRecur(manager, T, cuddT(cube)); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager, res1); + return(NULL); + } + cuddRef(res2); + + left = cuddAddMaxAbstractRecur(manager, E, cuddT(cube)); + if (left == NULL) { + Cudd_RecursiveDeref(manager, res1); + Cudd_RecursiveDeref(manager, res2); + return(NULL); + } + cuddRef(left); + right = cuddAddMaxAbstractRecur(manager, T, cuddT(cube)); + if (right == NULL) { + Cudd_RecursiveDeref(manager, res1); + Cudd_RecursiveDeref(manager, res2); + Cudd_RecursiveDeref(manager, left); + return(NULL); + } + cuddRef(right); + + tmp = cuddAddApplyRecur(manager, Cudd_addGreaterThanEquals, left, right); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,left); + Cudd_RecursiveDeref(manager,right); + return(NULL); + } + cuddRef(tmp); + + Cudd_RecursiveDeref(manager, left); + Cudd_RecursiveDeref(manager, right); + + cuddRef(zero); + res1Inf = cuddAddIteRecur(manager, tmp, res1, zero); + if (res1Inf == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,tmp); + cuddDeref(zero); + return(NULL); + } + cuddRef(res1Inf); + Cudd_RecursiveDeref(manager,res1); + cuddDeref(zero); + + tmp2 = cuddAddCmplRecur(manager,tmp); + if (tmp2 == NULL) { + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,left); + Cudd_RecursiveDeref(manager,right); + Cudd_RecursiveDeref(manager,tmp); + return(NULL); + } + cuddRef(tmp2); + Cudd_RecursiveDeref(manager,tmp); + + cuddRef(zero); + res2Inf = cuddAddIteRecur(manager, tmp2, res2, zero); + if (res2Inf == NULL) { + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,res1Inf); + Cudd_RecursiveDeref(manager,tmp2); + cuddDeref(zero); + return(NULL); + } + cuddRef(res2Inf); + Cudd_RecursiveDeref(manager,res2); + Cudd_RecursiveDeref(manager,tmp2); + cuddDeref(zero); + + res = (res1Inf == res2Inf) ? res1Inf : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1Inf); + Cudd_RecursiveDeref(manager,res2Inf); + return(NULL); + } + cuddRef(res); + Cudd_RecursiveDeref(manager,res1Inf); + Cudd_RecursiveDeref(manager,res2Inf); + cuddCacheInsert2(manager, Cudd_addMaxAbstractRepresentative, f, cube, res); + cuddDeref(res); + return(res); + } + else { /* if (cuddI(manager,f->index) < cuddI(manager,cube->index)) */ + res1 = cuddAddMaxAbstractRepresentativeRecur(manager, E, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddAddMaxAbstractRepresentativeRecur(manager, T, cube); + if (res2 == NULL) { + Cudd_RecursiveDeref(manager,res1); + return(NULL); + } + cuddRef(res2); + res = (res1 == res2) ? res1 : cuddUniqueInter(manager, (int) f->index, res2, res1); + if (res == NULL) { + Cudd_RecursiveDeref(manager,res1); + Cudd_RecursiveDeref(manager,res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, Cudd_addMaxAbstractRepresentative, f, cube, res); + return(res); + } +} /* end of cuddAddMaxAbstractRepresentativeRecur */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ /**Function******************************************************************** diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h index 313a56586..26bdce294 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h @@ -1002,6 +1002,8 @@ extern DdNode * cuddAddUnivAbstractRecur (DdManager *manager, DdNode *f, DdNode extern DdNode * cuddAddOrAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddAddMinAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddAddMaxAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * cuddAddMinAbstractRepresentativeRecur (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * cuddAddMaxAbstractRepresentativeRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddAddApplyRecur (DdManager *dd, DdNode * (*)(DdManager *, DdNode **, DdNode **), DdNode *f, DdNode *g); extern DdNode * cuddAddMonadicApplyRecur (DdManager * dd, DdNode * (*op)(DdManager *, DdNode *), DdNode * f); extern DdNode * cuddAddScalarInverseRecur (DdManager *dd, DdNode *f, DdNode *epsilon); diff --git a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc index b8f7d3983..c9d0c03bc 100644 --- a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc +++ b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc @@ -2423,6 +2423,24 @@ ADD::MaxAbstract(const ADD& cube) const return ADD(p, result); } // ADD::MaxAbstract +ADD +ADD::MinAbstractRepresentative(const ADD& cube) const +{ + DdManager *mgr = checkSameManager(cube); + DdNode *result = Cudd_addMinAbstractRepresentative(mgr, node, cube.node); + checkReturnValue(result); + return ADD(p, result); +} // ADD::MinRepresentative + +ADD +ADD::MaxAbstractRepresentative(const ADD& cube) const +{ + DdManager *mgr = checkSameManager(cube); + DdNode *result = Cudd_addMaxAbstractRepresentative(mgr, node, cube.node); + checkReturnValue(result); + return ADD(p, result); +} // ADD::MaxRepresentative + ADD ADD::Plus( const ADD& g) const diff --git a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh index 0b5acf051..97ee1680e 100644 --- a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh +++ b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh @@ -376,6 +376,8 @@ public: ADD OrAbstract(const ADD& cube) const; ADD MinAbstract(const ADD& cube) const; ADD MaxAbstract(const ADD& cube) const; + ADD MinAbstractRepresentative(const ADD& cube) const; + ADD MaxAbstractRepresentative(const ADD& cube) const; ADD Plus(const ADD& g) const; ADD Times(const ADD& g) const; ADD Threshold(const ADD& g) const; diff --git a/src/storage/dd/CuddAdd.cpp b/src/storage/dd/CuddAdd.cpp index 58f17137b..eddc26e28 100644 --- a/src/storage/dd/CuddAdd.cpp +++ b/src/storage/dd/CuddAdd.cpp @@ -242,6 +242,38 @@ namespace storm { return Add(this->getDdManager(), this->getCuddAdd().MaxAbstract(cubeDd.toAdd().getCuddAdd()), newMetaVariables); } + Add Add::minAbstractRepresentative(std::set const& metaVariables) const { + Bdd cubeDd = this->getDdManager()->getBddOne(); + + std::set newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + STORM_LOG_THROW(this->containsMetaVariable(metaVariable), storm::exceptions::InvalidArgumentException, "Cannot abstract from meta variable '" << metaVariable.getName() << "' that is not present in the DD."); + newMetaVariables.erase(metaVariable); + + DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + return Add(this->getDdManager(), this->getCuddAdd().MinAbstractRepresentative(cubeDd.toAdd().getCuddAdd()), newMetaVariables); + } + + Add Add::maxAbstractRepresentative(std::set const& metaVariables) const { + Bdd cubeDd = this->getDdManager()->getBddOne(); + + std::set newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the DD contains the meta variable and erase it, if this is the case. + STORM_LOG_THROW(this->containsMetaVariable(metaVariable), storm::exceptions::InvalidArgumentException, "Cannot abstract from meta variable '" << metaVariable.getName() << "' that is not present in the DD."); + newMetaVariables.erase(metaVariable); + + DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeDd &= ddMetaVariable.getCube(); + } + + return Add(this->getDdManager(), this->getCuddAdd().MaxAbstractRepresentative(cubeDd.toAdd().getCuddAdd()), newMetaVariables); + } + bool Add::equalModuloPrecision(Add const& other, double precision, bool relative) const { if (relative) { return this->getCuddAdd().EqualSupNormRel(other.getCuddAdd(), precision); diff --git a/src/storage/dd/CuddAdd.h b/src/storage/dd/CuddAdd.h index f80422493..312096b63 100644 --- a/src/storage/dd/CuddAdd.h +++ b/src/storage/dd/CuddAdd.h @@ -309,6 +309,28 @@ namespace storm { */ Add maxAbstract(std::set const& metaVariables) const; + /*! + * Computes a DD in which each concrete valuation of the meta variables not in the given set is associated + * with exactly one valuation of the meta variables in the given set that realizes the minimal function + * value (for the valuation of meta variables not in the given set). + * + * @param metaVariables The set of meta variables for which to pick unique representatives (that achieve the + * minimal function value). + * @return The resulting DD. + */ + Add minAbstractRepresentative(std::set const& metaVariables) const; + + /*! + * Computes a DD in which each concrete valuation of the meta variables not in the given set is associated + * with exactly one valuation of the meta variables in the given set that realizes the maximal function + * value (for the valuation of meta variables not in the given set). + * + * @param metaVariables The set of meta variables for which to pick unique representatives (that achieve the + * maximal function value). + * @return The resulting DD. + */ + Add maxAbstractRepresentative(std::set const& metaVariables) const; + /*! * Checks whether the current and the given ADD represent the same function modulo some given precision. * From 6c804732e1de7f3cf84864cd4b637b17346843b5 Mon Sep 17 00:00:00 2001 From: dehnert Date: Mon, 21 Sep 2015 16:32:31 +0200 Subject: [PATCH 019/400] introduced (probably buggy) versions of existsAbstractRepresentative on BDDs and prob0 for games Former-commit-id: 5e7225fe29d1a58b5c22812f3f3d6459ffbb8db7 --- resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h | 1 + .../3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c | 27 ++- .../3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c | 224 ++++++++++++++++++ .../3rdparty/cudd-2.5.0/src/cudd/cuddInt.h | 1 + .../3rdparty/cudd-2.5.0/src/obj/cuddObj.cc | 11 + .../3rdparty/cudd-2.5.0/src/obj/cuddObj.hh | 1 + src/models/symbolic/NondeterministicModel.cpp | 9 +- src/models/symbolic/NondeterministicModel.h | 12 +- .../symbolic/StochasticTwoPlayerGame.cpp | 23 +- src/models/symbolic/StochasticTwoPlayerGame.h | 18 ++ src/storage/dd/CuddBdd.cpp | 16 ++ src/storage/dd/CuddBdd.h | 9 + src/utility/graph.cpp | 62 ++++- src/utility/graph.h | 22 +- 14 files changed, 419 insertions(+), 17 deletions(-) diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h b/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h index 542dd8fdf..3169c25e9 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cudd.h @@ -848,6 +848,7 @@ extern DdNode * Cudd_RemapOverApprox (DdManager *dd, DdNode *f, int numVars, int extern DdNode * Cudd_BiasedUnderApprox (DdManager *dd, DdNode *f, DdNode *b, int numVars, int threshold, double quality1, double quality0); extern DdNode * Cudd_BiasedOverApprox (DdManager *dd, DdNode *f, DdNode *b, int numVars, int threshold, double quality1, double quality0); extern DdNode * Cudd_bddExistAbstract (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * Cudd_bddExistAbstractRepresentative (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * Cudd_bddExistAbstractLimit(DdManager * manager, DdNode * f, DdNode * cube, unsigned int limit); extern DdNode * Cudd_bddXorExistAbstract (DdManager *manager, DdNode *f, DdNode *g, DdNode *cube); extern DdNode * Cudd_bddUnivAbstract (DdManager *manager, DdNode *f, DdNode *cube); diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c index 11e95d8b5..2711d3ecd 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddAddAbs.c @@ -922,8 +922,7 @@ cuddAddMinAbstractRepresentativeRecur( return(NULL); } - // Fill in the missing variables to make representative unique. Set the "non-used-branch" to infinity, otherwise - // this may interfere with the minimum calculation. + // Fill in the missing variables to make representative unique. cuddRef(res); cuddRef(zero); res1 = cuddUniqueInter(manager, (int) cube->index, zero, res); @@ -1026,8 +1025,13 @@ cuddAddMinAbstractRepresentativeRecur( Cudd_RecursiveDeref(manager,res2); Cudd_RecursiveDeref(manager,tmp2); cuddDeref(zero); + +// Originally: +// res = (res1Inf == res2Inf) ? res1Inf : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + cuddRef(zero); + res = (res1Inf == res2Inf) ? cuddUniqueInter(manager, (int) f->index, zero, res1Inf) : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + cuddDeref(zero); - res = (res1Inf == res2Inf) ? res1Inf : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); if (res == NULL) { Cudd_RecursiveDeref(manager,res1Inf); Cudd_RecursiveDeref(manager,res2Inf); @@ -1050,7 +1054,10 @@ cuddAddMinAbstractRepresentativeRecur( return(NULL); } cuddRef(res2); - res = (res1 == res2) ? res1 : cuddUniqueInter(manager, (int) f->index, res2, res1); + + cuddRef(zero); + res = (res1 == res2) ? cuddUniqueInter(manager, (int) f->index, zero, res1) : cuddUniqueInter(manager, (int) f->index, res2, res1); + cuddDeref(zero); if (res == NULL) { Cudd_RecursiveDeref(manager,res1); Cudd_RecursiveDeref(manager,res2); @@ -1209,8 +1216,14 @@ cuddAddMaxAbstractRepresentativeRecur( Cudd_RecursiveDeref(manager,res2); Cudd_RecursiveDeref(manager,tmp2); cuddDeref(zero); + +// Orignally +// res = (res1Inf == res2Inf) ? res1Inf : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); - res = (res1Inf == res2Inf) ? res1Inf : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + cuddRef(zero); + res = (res1Inf == res2Inf) ? cuddUniqueInter(manager, (int) f->index, zero, res1Inf) : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + cuddDeref(zero); + if (res == NULL) { Cudd_RecursiveDeref(manager,res1Inf); Cudd_RecursiveDeref(manager,res2Inf); @@ -1233,7 +1246,9 @@ cuddAddMaxAbstractRepresentativeRecur( return(NULL); } cuddRef(res2); - res = (res1 == res2) ? res1 : cuddUniqueInter(manager, (int) f->index, res2, res1); + cuddRef(zero); + res = (res1 == res2) ? cuddUniqueInter(manager, (int) f->index, zero, res1) : cuddUniqueInter(manager, (int) f->index, res2, res1); + cuddDeref(zero); if (res == NULL) { Cudd_RecursiveDeref(manager,res1); Cudd_RecursiveDeref(manager,res2); diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c index a806078c5..6aa60fee2 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c @@ -147,6 +147,42 @@ Cudd_bddExistAbstract( } /* end of Cudd_bddExistAbstract */ +/**Function******************************************************************** + + Synopsis [Just like Cudd_bddExistAbstract, but instead of abstracting the + variables in the given cube, picks a unique representative that realizes the + existential truth value.] + + Description [Returns the resulting BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + + Note: Added by Christian Dehnert 9/21/15 + + ******************************************************************************/ +DdNode * +Cudd_bddExistAbstractRepresentative( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (bddCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err,"Error: Can only abstract positive cubes\n"); + manager->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddBddExistAbstractRepresentativeRecur(manager, f, cube); + } while (manager->reordered == 1); + return(res); + +} /* end of Cudd_bddExistAbstractRepresentative */ /**Function******************************************************************** @@ -484,6 +520,194 @@ cuddBddExistAbstractRecur( } /* end of cuddBddExistAbstractRecur */ +/**Function******************************************************************** + + Synopsis [Performs the recursive steps of Cudd_bddExistAbstractRepresentative.] + + Description [Performs the recursive steps of Cudd_bddExistAbstractRepresentative. + Returns the BDD obtained by picking a representative over the variables in + the given cube for all other valuations. Returns the resulting BDD if successful; + NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + + ******************************************************************************/ +DdNode * +cuddBddExistAbstractRepresentativeRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *F, *T, *E, *res, *res1, *res2, *one, *zero, *left, *right, *tmp, *res1Inf, *res2Inf; + + statLine(manager); + one = DD_ONE(manager); + zero = DD_ZERO(manager); + F = Cudd_Regular(f); + + // Store whether f is negated. + int fIsNegated = f != F; + + /* Cube is guaranteed to be a cube at this point. */ + if (F == one) { + if (fIsNegated) { + return f; + } + + if (cube == one) { + return one; + } else { + return cube; + } + } + /* From now on, f and cube are non-constant. */ + + /* Abstract a variable that does not appear in f. */ + if (manager->perm[F->index] > manager->perm[cube->index]) { + res = cuddBddExistAbstractRepresentativeRecur(manager, f, cuddT(cube)); + if (res == NULL) { + return(NULL); + } + + cuddRef(res); + cuddRef(zero); + res1 = cuddUniqueInter(manager, (int) cube->index, zero, res); + if (res1 == NULL) { + Cudd_RecursiveDeref(manager,res); + Cudd_RecursiveDeref(manager,zero); + return(NULL); + } + Cudd_IterDerefBdd(manager, res); + cuddDeref(zero); + return(res1); + } + + /* Check the cache. */ + if (F->ref != 1 && (res = cuddCacheLookup2(manager, Cudd_bddExistAbstractRepresentative, f, cube)) != NULL) { + return(res); + } + + /* Compute the cofactors of f. */ + T = cuddT(F); E = cuddE(F); + if (f != F) { + T = Cudd_Not(T); E = Cudd_Not(E); + } + + /* If the two indices are the same, so are their levels. */ + if (F->index == cube->index) { +// if (E == one) { +// cuddRef(zero); +// cuddRef(cuddT(cube)); +// res1 = cuddUniqueInter(manager, (int) F->index, zero, cuddT(cube)); +// cuddDeref(zero); +// cuddDeref(cuddT(cube)); +// return res1; +// } else if (T == one) { +// cuddRef(zero); +// cuddRef(cuddT(cube)); +// res1 = cuddUniqueInter(manager, (int) F->index, cuddT(cube), zero); +// cuddDeref(zero); +// cuddDeref(cuddT(cube)); +// return res1; +// } + + res1 = cuddBddExistAbstractRepresentativeRecur(manager, E, cuddT(cube)); + if (res1 == NULL) { + return(NULL); + } + if (res1 == one) { + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstractRepresentative, f, cube, one); + return(one); + } + cuddRef(res1); + + res2 = cuddBddExistAbstractRepresentativeRecur(manager, T, cuddT(cube)); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager,res1); + return(NULL); + } + cuddRef(res2); + + left = cuddBddExistAbstractRecur(manager, E, cuddT(cube)); + if (left == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + cuddRef(left); + + cuddRef(zero); + res1Inf = cuddBddIteRecur(manager, left, res1, zero); + if (res1Inf == NULL) { + Cudd_IterDerefBdd(manager,res1); + Cudd_IterDerefBdd(manager,res2); + Cudd_IterDerefBdd(manager,left); + return(NULL); + } + cuddRef(res1Inf); + cuddDeref(zero); + + Cudd_IterDerefBdd(manager,res1); + + cuddRef(zero); + res2Inf = cuddBddIteRecur(manager, left, zero, res2); + if (res2Inf == NULL) { + Cudd_IterDerefBdd(manager,res1); + Cudd_IterDerefBdd(manager,res2); + Cudd_IterDerefBdd(manager,left); + Cudd_IterDerefBdd(manager,res1Inf); + return(NULL); + } + cuddRef(res2Inf); + cuddDeref(zero); + + Cudd_IterDerefBdd(manager,res2); + Cudd_IterDerefBdd(manager,left); + + cuddRef(zero); + res = (res1Inf == res2Inf) ? cuddUniqueInter(manager, (int) f->index, zero, res1Inf) : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + cuddRef(res); + cuddDeref(zero); + + Cudd_IterDerefBdd(manager,res1Inf); + Cudd_IterDerefBdd(manager,res2Inf); + + if (res == NULL) { + Cudd_IterDerefBdd(manager,res); + return(NULL); + } + cuddCacheInsert2(manager, Cudd_bddExistAbstractRepresentative, f, cube, res); + cuddDeref(res); + return(res); + } else { /* if (cuddI(manager,F->index) < cuddI(manager,cube->index)) */ + res1 = cuddBddExistAbstractRepresentativeRecur(manager, E, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddBddExistAbstractRepresentativeRecur(manager, T, cube); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager, res1); + return(NULL); + } + cuddRef(res2); + /* ITE takes care of possible complementation of res1 and of the + ** case in which res1 == res2. */ + res = cuddBddIteRecur(manager, manager->vars[F->index], res2, res1); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstractRepresentative, f, cube, res); + return(res); + } + +} /* end of cuddBddExistAbstractRepresentativeRecur */ /**Function******************************************************************** diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h index 26bdce294..6d3a8da10 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddInt.h @@ -1017,6 +1017,7 @@ extern DdNode * cuddBiasedUnderApprox (DdManager *dd, DdNode *f, DdNode *b, int extern DdNode * cuddBddAndAbstractRecur (DdManager *manager, DdNode *f, DdNode *g, DdNode *cube); extern int cuddAnnealing (DdManager *table, int lower, int upper); extern DdNode * cuddBddExistAbstractRecur (DdManager *manager, DdNode *f, DdNode *cube); +extern DdNode * cuddBddExistAbstractRepresentativeRecur (DdManager *manager, DdNode *f, DdNode *cube); extern DdNode * cuddBddXorExistAbstractRecur (DdManager *manager, DdNode *f, DdNode *g, DdNode *cube); extern DdNode * cuddBddBooleanDiffRecur (DdManager *manager, DdNode *f, DdNode *var); extern DdNode * cuddBddIteRecur (DdManager *dd, DdNode *f, DdNode *g, DdNode *h); diff --git a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc index c9d0c03bc..4da1b911b 100644 --- a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc +++ b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.cc @@ -3214,6 +3214,17 @@ BDD::ExistAbstract( } // BDD::ExistAbstract +BDD +BDD::ExistAbstractRepresentative( + const BDD& cube) const +{ + DdManager *mgr = checkSameManager(cube); + DdNode *result; + result = Cudd_bddExistAbstractRepresentative(mgr, node, cube.node); + checkReturnValue(result); + return BDD(p, result); + +} // BDD::ExistAbstractRepresentative BDD BDD::XorExistAbstract( diff --git a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh index 97ee1680e..509f6178c 100644 --- a/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh +++ b/resources/3rdparty/cudd-2.5.0/src/obj/cuddObj.hh @@ -255,6 +255,7 @@ public: BDD BiasedOverApprox(const BDD& bias, int numVars, int threshold = 0, double quality1 = 1.0, double quality0 = 1.0) const; BDD ExistAbstract(const BDD& cube, unsigned int limit = 0) const; + BDD ExistAbstractRepresentative(const BDD& cube) const; BDD XorExistAbstract(const BDD& g, const BDD& cube) const; BDD UnivAbstract(const BDD& cube) const; BDD BooleanDiff(int x) const; diff --git a/src/models/symbolic/NondeterministicModel.cpp b/src/models/symbolic/NondeterministicModel.cpp index f5decd7d5..140811ddd 100644 --- a/src/models/symbolic/NondeterministicModel.cpp +++ b/src/models/symbolic/NondeterministicModel.cpp @@ -27,8 +27,7 @@ namespace storm { : Model(modelType, manager, reachableStates, initialStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, columnExpressionAdapter, rowColumnMetaVariablePairs, labelToExpressionMap, rewardModels), nondeterminismVariables(nondeterminismVariables) { // Prepare the mask of illegal nondeterministic choices. - illegalMask = transitionMatrix.notZero().existsAbstract(this->getColumnVariables()); - illegalMask = !illegalMask && reachableStates; + illegalMask = !(transitionMatrix.notZero().existsAbstract(this->getColumnVariables())) && reachableStates; } template @@ -50,6 +49,12 @@ namespace storm { return illegalMask; } + template + storm::dd::Bdd NondeterministicModel::getIllegalSuccessorMask() const { + storm::dd::Bdd transitionMatrixBdd = this->getTransitionMatrix().notZero(); + return !transitionMatrixBdd && transitionMatrixBdd.existsAbstract(this->getColumnVariables()); + } + template void NondeterministicModel::printModelInformationToStream(std::ostream& out) const { this->printModelInformationHeaderToStream(out); diff --git a/src/models/symbolic/NondeterministicModel.h b/src/models/symbolic/NondeterministicModel.h index dc5194320..d5c7175a4 100644 --- a/src/models/symbolic/NondeterministicModel.h +++ b/src/models/symbolic/NondeterministicModel.h @@ -78,19 +78,27 @@ namespace storm { */ storm::dd::Bdd const& getIllegalMask() const; + /*! + * Retrieves a BDD characterizing the illegal successors for each choice. + * + * @return A BDD characterizing the illegal successors for each choice. + */ + storm::dd::Bdd getIllegalSuccessorMask() const; + virtual void printModelInformationToStream(std::ostream& out) const override; protected: virtual void printDdVariableInformationToStream(std::ostream& out) const override; + + // A mask that characterizes all illegal nondeterministic choices. + storm::dd::Bdd illegalMask; private: // The meta variables encoding the nondeterminism in the model. std::set nondeterminismVariables; - // A mask that characterizes all legal nondeterministic choices. - storm::dd::Bdd illegalMask; }; } // namespace symbolic diff --git a/src/models/symbolic/StochasticTwoPlayerGame.cpp b/src/models/symbolic/StochasticTwoPlayerGame.cpp index 370df9ffb..d8f1df1f6 100644 --- a/src/models/symbolic/StochasticTwoPlayerGame.cpp +++ b/src/models/symbolic/StochasticTwoPlayerGame.cpp @@ -25,8 +25,27 @@ namespace storm { std::set const& nondeterminismVariables, std::map labelToExpressionMap, std::unordered_map const& rewardModels) - : NondeterministicModel(storm::models::ModelType::S2pg, manager, reachableStates, initialStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, columnExpressionAdapter, rowColumnMetaVariablePairs, nondeterminismVariables, labelToExpressionMap, rewardModels), player1Variables(player1Variables), player2Variables(player2Variables) { - // Intentionally left empty. + : NondeterministicModel(storm::models::ModelType::S2pg, manager, reachableStates, initialStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, columnExpressionAdapter, rowColumnMetaVariablePairs, nondeterminismVariables, labelToExpressionMap, rewardModels), player1Variables(player1Variables), player2Variables(player2Variables) { + + // Compute legal player 1 mask. + illegalPlayer1Mask = transitionMatrix.notZero().existsAbstract(this->getColumnVariables()).existsAbstract(this->getPlayer2Variables()); + + // Correct the mask for player 2. This is necessary, because it is not yet restricted to the legal choices of player 1. + this->illegalMask &= illegalPlayer1Mask; + + // Then set the illegal mask for player 1 correctly. + illegalPlayer1Mask = !illegalPlayer1Mask && reachableStates; + } + + template + storm::dd::Bdd StochasticTwoPlayerGame::getIllegalPlayer1Mask() const { + return illegalPlayer1Mask; + } + + template + storm::dd::Bdd StochasticTwoPlayerGame::getIllegalPlayer2Mask() const { + // For player 2, we can simply return the mask of the superclass. + return this->getIllegalMask(); } template diff --git a/src/models/symbolic/StochasticTwoPlayerGame.h b/src/models/symbolic/StochasticTwoPlayerGame.h index ae295ba6c..aba4ee561 100644 --- a/src/models/symbolic/StochasticTwoPlayerGame.h +++ b/src/models/symbolic/StochasticTwoPlayerGame.h @@ -73,7 +73,25 @@ namespace storm { */ std::set const& getPlayer2Variables() const; + /*! + * Retrieves a BDD characterizing all illegal player 1 choice encodings in the model. + * + * @return A BDD characterizing all illegal player 1 choice encodings in the model. + */ + storm::dd::Bdd getIllegalPlayer1Mask() const; + + /*! + * Retrieves a BDD characterizing all illegal player 2 choice encodings in the model. + * + * @return A BDD characterizing all illegal player 2 choice encodings in the model. + */ + storm::dd::Bdd getIllegalPlayer2Mask() const; + private: + // A mask that characterizes all illegal player 1 choices. The mask for player 2 is given by the mask + // of the superclass (nondeterminstic model). + storm::dd::Bdd illegalPlayer1Mask; + // The meta variables used to encode the nondeterministic choices of player 1. std::set player1Variables; diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp index 09e59f390..bdcccd515 100644 --- a/src/storage/dd/CuddBdd.cpp +++ b/src/storage/dd/CuddBdd.cpp @@ -134,6 +134,22 @@ namespace storm { return Bdd(this->getDdManager(), this->getCuddBdd().ExistAbstract(cubeBdd.getCuddBdd()), newMetaVariables); } + Bdd Bdd::existsAbstractRepresentative(std::set const& metaVariables) const { + Bdd cubeBdd = this->getDdManager()->getBddOne(); + + std::set newMetaVariables = this->getContainedMetaVariables(); + for (auto const& metaVariable : metaVariables) { + // First check whether the BDD contains the meta variable and erase it, if this is the case. + STORM_LOG_THROW(this->containsMetaVariable(metaVariable), storm::exceptions::InvalidArgumentException, "Cannot abstract from meta variable '" << metaVariable.getName() << "' that is not present in the DD."); + newMetaVariables.erase(metaVariable); + + DdMetaVariable const& ddMetaVariable = this->getDdManager()->getMetaVariable(metaVariable); + cubeBdd &= ddMetaVariable.getCube(); + } + + return Bdd(this->getDdManager(), this->getCuddBdd().ExistAbstractRepresentative(cubeBdd.getCuddBdd()), newMetaVariables); + } + Bdd Bdd::universalAbstract(std::set const& metaVariables) const { Bdd cubeBdd = this->getDdManager()->getBddOne(); diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h index a21402c86..b7015b3a3 100644 --- a/src/storage/dd/CuddBdd.h +++ b/src/storage/dd/CuddBdd.h @@ -166,6 +166,15 @@ namespace storm { * @param metaVariables The meta variables from which to abstract. */ Bdd existsAbstract(std::set const& metaVariables) const; + + /*! + * Similar to existsAbstract, but does not abstract from the variables but rather picks a + * valuation of each of the meta variables "to abstract from" such that for this valuation, there exists a + * valuation (of the other variables) that that make the function evaluate to true. + * + * @param metaVariables The meta variables from which to abstract. + */ + Bdd existsAbstractRepresentative(std::set const& metaVariables) const; /*! * Universally abstracts from the given meta variables. diff --git a/src/utility/graph.cpp b/src/utility/graph.cpp index 3e12abcd2..e377c4f09 100644 --- a/src/utility/graph.cpp +++ b/src/utility/graph.cpp @@ -8,6 +8,7 @@ #include "src/models/symbolic/DeterministicModel.h" #include "src/models/symbolic/NondeterministicModel.h" #include "src/models/symbolic/StandardRewardModel.h" +#include "src/models/symbolic/StochasticTwoPlayerGame.h" #include "src/models/sparse/DeterministicModel.h" #include "src/models/sparse/NondeterministicModel.h" #include "src/models/sparse/StandardRewardModel.h" @@ -688,6 +689,64 @@ namespace storm { return result; } + template + void performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy) { + + // The solution set. + storm::dd::Bdd solution = psiStates; + + bool done = false; + uint_fast64_t iterations = 0; + while (!done) { + storm::dd::Bdd tmp = (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())).existsAbstract(model.getColumnVariables()) && phiStates; + + if (player2Strategy == OptimizationDirection::Minimize) { + tmp = (tmp || model.getIllegalPlayer2Mask()).universalAbstract(model.getPlayer2Variables()); + } else { + tmp = tmp.existsAbstract(model.getPlayer2Variables()); + } + + if (player1Strategy == OptimizationDirection::Minimize) { + tmp = (tmp || model.getIllegalPlayer1Mask()).universalAbstract(model.getPlayer1Variables()); + } else { + tmp = tmp.existsAbstract(model.getPlayer1Variables()); + } + + tmp |= solution; + + if (tmp == solution) { + done = true; + } + + solution = tmp; + ++iterations; + } + + // Since we have determined the inverse of the desired set, we need to complement it now. + solution = !solution && model.getReachableStates(); + + // Determine all transitions between prob0 states. + storm::dd::Bdd transitionsBetweenProb0States = solution && (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())); + + // Determine the distributions that have only successors that are prob0 states. + storm::dd::Bdd onlyProb0Successors = (transitionsBetweenProb0States || model.getIllegalSuccessorMask()).universalAbstract(model.getColumnVariables()); + + boost::optional> player2StrategyBdd; + if (player2Strategy == OptimizationDirection::Minimize) { + // Pick a distribution that has only prob0 successors. + player2StrategyBdd = onlyProb0Successors.existsAbstractRepresentative(model.getPlayer2Variables()); + } + + boost::optional> player1StrategyBdd; + if (player1Strategy == OptimizationDirection::Minimize) { + // Move from player 2 choices with only prob0 successors to player 1 choices with only prob 0 successors. + onlyProb0Successors = onlyProb0Successors.existsAbstract(model.getPlayer2Variables()); + + // Pick a prob0 player 2 state. + onlyProb0Successors.existsAbstractRepresentative(model.getPlayer1Variables()); + } + } + template std::vector getTopologicalSort(storm::storage::SparseMatrix const& matrix) { if (matrix.getRowCount() != matrix.getColumnCount()) { @@ -997,7 +1056,7 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01(storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); - + template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); @@ -1020,6 +1079,7 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01Min(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; + template void performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); } // namespace graph } // namespace utility diff --git a/src/utility/graph.h b/src/utility/graph.h index 3ae26d34f..5118ef726 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -11,6 +11,8 @@ #include "src/models/sparse/DeterministicModel.h" #include "src/storage/dd/DdType.h" +#include "src/solver/OptimizationDirection.h" + namespace storm { namespace storage { class BitVector; @@ -23,6 +25,7 @@ namespace storm { template class Model; template class DeterministicModel; template class NondeterministicModel; + template class StochasticTwoPlayerGame; } } @@ -393,7 +396,7 @@ namespace storm { * @return A BDD representing all such states. */ template - storm::dd::Bdd performProb0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; + storm::dd::Bdd performProb0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); /*! * Computes the set of states for which all schedulers achieve probability one of satisfying phi until psi. @@ -425,10 +428,21 @@ namespace storm { storm::dd::Bdd performProb1E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::dd::Bdd const& statesWithProbabilityGreater0E) ; template - std::pair, storm::dd::Bdd> performProb01Max(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; + std::pair, storm::dd::Bdd> performProb01Max(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + + template + std::pair, storm::dd::Bdd> performProb01Min(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + /*! + * Computes the set of states that have probability 0 given the strategies of the two players. + * + * @param model The (symbolic) model for which to compute the set of states. + * @param transitionMatrix The transition matrix of the model as a BDD. + * @param phiStates The BDD containing all phi states of the model. + * @param psiStates The BDD containing all psi states of the model. + */ template - std::pair, storm::dd::Bdd> performProb01Min(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; + void performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); /*! * Performs a topological sort of the states of the system according to the given transitions. @@ -437,7 +451,7 @@ namespace storm { * @return A vector of indices that is a topological sort of the states. */ template - std::vector getTopologicalSort(storm::storage::SparseMatrix const& matrix) ; + std::vector getTopologicalSort(storm::storage::SparseMatrix const& matrix); /*! * A class needed to compare the distances for two states in the Dijkstra search. From 972795912a5e48c00bd658a5aa2558f4ea2e7f52 Mon Sep 17 00:00:00 2001 From: dehnert Date: Mon, 21 Sep 2015 21:43:04 +0200 Subject: [PATCH 020/400] added some convenience accessor methods in symbolic model/games. added return type for prob01 for games that can also store strategies. added tests for prob0 for games Former-commit-id: f0a8b156caf9014c94841a3cc11102c898f31a12 --- src/models/symbolic/Model.cpp | 5 +++ src/models/symbolic/Model.h | 10 ++++- .../symbolic/StochasticTwoPlayerGame.cpp | 1 + .../prism/menu_games/AbstractProgram.cpp | 2 +- src/storage/prism/menu_games/MenuGame.cpp | 13 +++++- src/storage/prism/menu_games/MenuGame.h | 17 +++++++ src/utility/graph.cpp | 44 ++++++++++--------- src/utility/graph.h | 13 +++++- test/functional/utility/GraphTest.cpp | 35 +++++++++++++++ 9 files changed, 114 insertions(+), 26 deletions(-) diff --git a/src/models/symbolic/Model.cpp b/src/models/symbolic/Model.cpp index d61d51115..c30c75b67 100644 --- a/src/models/symbolic/Model.cpp +++ b/src/models/symbolic/Model.cpp @@ -90,6 +90,11 @@ namespace storm { return transitionMatrix; } + template + storm::dd::Bdd Model::getQualitativeTransitionMatrix() const { + return this->getTransitionMatrix().notZero(); + } + template std::size_t Model::getSizeInBytes() const { return sizeof(*this) + sizeof(DdNode) * (reachableStates.getNodeCount() + initialStates.getNodeCount() + transitionMatrix.getNodeCount()); diff --git a/src/models/symbolic/Model.h b/src/models/symbolic/Model.h index 7c0b49118..9ced28001 100644 --- a/src/models/symbolic/Model.h +++ b/src/models/symbolic/Model.h @@ -123,7 +123,7 @@ namespace storm { * Returns the set of states labeled satisfying the given expression (that must be of boolean type). * * @param expression The expression that needs to hold in the states. - * @return The set of states labeled satisfying the given expression. + * @return The set of states satisfying the given expression. */ virtual storm::dd::Bdd getStates(storm::expressions::Expression const& expression) const; @@ -148,6 +148,14 @@ namespace storm { * @return A matrix representing the transitions of the model. */ storm::dd::Add& getTransitionMatrix(); + + /*! + * Retrieves the matrix qualitatively (i.e. without probabilities) representing the transitions of the + * model. + * + * @return A matrix representing the qualitative transitions of the model. + */ + storm::dd::Bdd getQualitativeTransitionMatrix() const; /*! * Retrieves the meta variables used to encode the rows of the transition matrix and the vector indices. diff --git a/src/models/symbolic/StochasticTwoPlayerGame.cpp b/src/models/symbolic/StochasticTwoPlayerGame.cpp index d8f1df1f6..6b9f72a14 100644 --- a/src/models/symbolic/StochasticTwoPlayerGame.cpp +++ b/src/models/symbolic/StochasticTwoPlayerGame.cpp @@ -28,6 +28,7 @@ namespace storm { : NondeterministicModel(storm::models::ModelType::S2pg, manager, reachableStates, initialStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, columnExpressionAdapter, rowColumnMetaVariablePairs, nondeterminismVariables, labelToExpressionMap, rewardModels), player1Variables(player1Variables), player2Variables(player2Variables) { // Compute legal player 1 mask. + transitionMatrix.exportToDot("trans.dot"); illegalPlayer1Mask = transitionMatrix.notZero().existsAbstract(this->getColumnVariables()).existsAbstract(this->getPlayer2Variables()); // Correct the mask for player 2. This is necessary, because it is not yet restricted to the legal choices of player 1. diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index e0d79e23b..53cafc69e 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -153,7 +153,7 @@ namespace storm { storm::dd::Add transitionMatrix = (gameBdd.first && reachableStates).toAdd() * commandUpdateProbabilitiesAdd + deadlockTransitions; std::set usedPlayer2Variables; - for (uint_fast64_t index = 0; index < ddInformation.optionDdVariables.size(); ++index) { + for (uint_fast64_t index = 0; index < gameBdd.second; ++index) { usedPlayer2Variables.insert(usedPlayer2Variables.end(), ddInformation.optionDdVariables[index].first); } diff --git a/src/storage/prism/menu_games/MenuGame.cpp b/src/storage/prism/menu_games/MenuGame.cpp index d5e0b98f4..22f25e251 100644 --- a/src/storage/prism/menu_games/MenuGame.cpp +++ b/src/storage/prism/menu_games/MenuGame.cpp @@ -24,7 +24,7 @@ namespace storm { std::set const& player2Variables, std::set const& allNondeterminismVariables, storm::expressions::Variable const& updateVariable, - std::map> const& expressionToBddMap) : storm::models::symbolic::StochasticTwoPlayerGame(manager, reachableStates, initialStates, transitionMatrix, rowVariables, nullptr, columnVariables, nullptr, rowColumnMetaVariablePairs, player1Variables, player2Variables, allNondeterminismVariables), updateVariable(updateVariable), expressionToBddMap(expressionToBddMap) { + std::map> const& expressionToBddMap) : storm::models::symbolic::StochasticTwoPlayerGame(manager, reachableStates, initialStates, transitionMatrix.sumAbstract({updateVariable}), rowVariables, nullptr, columnVariables, nullptr, rowColumnMetaVariablePairs, player1Variables, player2Variables, allNondeterminismVariables), updateVariable(updateVariable), expressionToBddMap(expressionToBddMap) { // Intentionally left empty. } @@ -35,9 +35,18 @@ namespace storm { template storm::dd::Bdd MenuGame::getStates(storm::expressions::Expression const& expression) const { + return this->getStates(expression, false); + } + + template + storm::dd::Bdd MenuGame::getStates(storm::expressions::Expression const& expression, bool negated) const { auto it = expressionToBddMap.find(expression); STORM_LOG_THROW(it != expressionToBddMap.end(), storm::exceptions::InvalidArgumentException, "The given expression was not used in the abstraction process and can therefore not be retrieved."); - return it->second && this->getReachableStates(); + if (negated) { + return !it->second && this->getReachableStates(); + } else { + return it->second && this->getReachableStates(); + } } template diff --git a/src/storage/prism/menu_games/MenuGame.h b/src/storage/prism/menu_games/MenuGame.h index 6c7416068..854dbe5f6 100644 --- a/src/storage/prism/menu_games/MenuGame.h +++ b/src/storage/prism/menu_games/MenuGame.h @@ -59,7 +59,24 @@ namespace storm { virtual storm::dd::Bdd getStates(std::string const& label) const override; + /*! + * Returns the set of states satisfying the given expression (that must be of boolean type). Note that + * for menu games, the given expression must be a predicate that was used to build the abstract game. + * + * @param expression The expression that needs to hold in the states. + * @return The set of states satisfying the given expression. + */ virtual storm::dd::Bdd getStates(storm::expressions::Expression const& expression) const override; + + /*! + * Returns the set of states satisfying the given expression (that must be of boolean type). Note that + * for menu games, the given expression must be a predicate that was used to build the abstract game. + * + * @param expression The expression that needs to hold in the states. + * @param negated If set to true, the result is the set of states not satisfying the expression. + * @return The set of states labeled satisfying the given expression. + */ + storm::dd::Bdd getStates(storm::expressions::Expression const& expression, bool negated) const; virtual bool hasLabel(std::string const& label) const override; diff --git a/src/utility/graph.cpp b/src/utility/graph.cpp index e377c4f09..e78408a1b 100644 --- a/src/utility/graph.cpp +++ b/src/utility/graph.cpp @@ -690,7 +690,7 @@ namespace storm { } template - void performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy) { + GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy) { // The solution set. storm::dd::Bdd solution = psiStates; @@ -725,26 +725,28 @@ namespace storm { // Since we have determined the inverse of the desired set, we need to complement it now. solution = !solution && model.getReachableStates(); - // Determine all transitions between prob0 states. - storm::dd::Bdd transitionsBetweenProb0States = solution && (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())); + return GameProb01Result(solution); - // Determine the distributions that have only successors that are prob0 states. - storm::dd::Bdd onlyProb0Successors = (transitionsBetweenProb0States || model.getIllegalSuccessorMask()).universalAbstract(model.getColumnVariables()); - - boost::optional> player2StrategyBdd; - if (player2Strategy == OptimizationDirection::Minimize) { - // Pick a distribution that has only prob0 successors. - player2StrategyBdd = onlyProb0Successors.existsAbstractRepresentative(model.getPlayer2Variables()); - } - - boost::optional> player1StrategyBdd; - if (player1Strategy == OptimizationDirection::Minimize) { - // Move from player 2 choices with only prob0 successors to player 1 choices with only prob 0 successors. - onlyProb0Successors = onlyProb0Successors.existsAbstract(model.getPlayer2Variables()); - - // Pick a prob0 player 2 state. - onlyProb0Successors.existsAbstractRepresentative(model.getPlayer1Variables()); - } +// // Determine all transitions between prob0 states. +// storm::dd::Bdd transitionsBetweenProb0States = solution && (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())); +// +// // Determine the distributions that have only successors that are prob0 states. +// storm::dd::Bdd onlyProb0Successors = (transitionsBetweenProb0States || model.getIllegalSuccessorMask()).universalAbstract(model.getColumnVariables()); +// +// boost::optional> player2StrategyBdd; +// if (player2Strategy == OptimizationDirection::Minimize) { +// // Pick a distribution that has only prob0 successors. +// player2StrategyBdd = onlyProb0Successors.existsAbstractRepresentative(model.getPlayer2Variables()); +// } +// +// boost::optional> player1StrategyBdd; +// if (player1Strategy == OptimizationDirection::Minimize) { +// // Move from player 2 choices with only prob0 successors to player 1 choices with only prob 0 successors. +// onlyProb0Successors = onlyProb0Successors.existsAbstract(model.getPlayer2Variables()); +// +// // Pick a prob0 player 2 state. +// onlyProb0Successors.existsAbstractRepresentative(model.getPlayer1Variables()); +// } } template @@ -1079,7 +1081,7 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01Min(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; - template void performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); + template GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); } // namespace graph } // namespace utility diff --git a/src/utility/graph.h b/src/utility/graph.h index 5118ef726..ccc7cc784 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -433,6 +433,17 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01Min(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template + struct GameProb01Result { + GameProb01Result(storm::dd::Bdd const& states, boost::optional> const& player1Strategy = boost::none, boost::optional> const& player2Strategy = boost::none) : states(states), player1Strategy(player1Strategy), player2Strategy(player2Strategy) { + // Intentionally left empty. + } + + storm::dd::Bdd states; + boost::optional> player1Strategy; + boost::optional> player2Strategy; + }; + /*! * Computes the set of states that have probability 0 given the strategies of the two players. * @@ -442,7 +453,7 @@ namespace storm { * @param psiStates The BDD containing all psi states of the model. */ template - void performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); + GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); /*! * Performs a topological sort of the states of the system according to the given transitions. diff --git a/test/functional/utility/GraphTest.cpp b/test/functional/utility/GraphTest.cpp index d58de5ca9..6dba4e688 100644 --- a/test/functional/utility/GraphTest.cpp +++ b/test/functional/utility/GraphTest.cpp @@ -88,6 +88,41 @@ TEST(GraphTest, SymbolicProb01MinMax) { EXPECT_EQ(16ul, statesWithProbability01.second.getNonZeroCount()); } +#ifdef STORM_HAVE_MSAT + +#include "src/storage/prism/menu_games/AbstractProgram.h" + +#include "src/storage/expressions/Expression.h" + +#include "src/utility/solver.h" + +TEST(GraphTest, SymbolicProb0StochasticGame) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s") < manager.integer(3)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); + + storm::utility::graph::GameProb01Result result1 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize); + EXPECT_EQ(1, result1.states.getNonZeroCount()); + + storm::utility::graph::GameProb01Result result2 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize); + EXPECT_EQ(1, result2.states.getNonZeroCount()); + + storm::utility::graph::GameProb01Result result3 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize); + EXPECT_EQ(0, result3.states.getNonZeroCount()); + + storm::utility::graph::GameProb01Result result4 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize); + EXPECT_EQ(0, result4.states.getNonZeroCount()); +} + +#endif + TEST(GraphTest, ExplicitProb01) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/crowds-5-5.pm"); std::shared_ptr> model = storm::builder::ExplicitPrismModelBuilder::translateProgram(program); From 1c42ed792b82c78a4f4fc71185aa8a3ff66e330e Mon Sep 17 00:00:00 2001 From: dehnert Date: Tue, 22 Sep 2015 16:58:56 +0200 Subject: [PATCH 021/400] fixed some bugs, added some test, added some prob1 algorithm, and did some stuff, you know? Former-commit-id: 00fa21d1fe67756959fc210b3d630d813b749c88 --- .../3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c | 63 ++++--- .../symbolic/StochasticTwoPlayerGame.cpp | 1 - src/storage/dd/CuddAdd.cpp | 6 +- src/storage/dd/CuddAdd.h | 4 +- src/storage/dd/CuddBdd.cpp | 12 +- src/storage/dd/CuddBdd.h | 4 +- src/storage/dd/CuddDd.cpp | 4 +- src/storage/dd/CuddDd.h | 4 +- src/storage/dd/CuddDdManager.cpp | 13 +- src/storage/dd/CuddDdManager.h | 13 +- src/storage/dd/CuddDdMetaVariable.cpp | 6 +- src/storage/dd/CuddDdMetaVariable.h | 6 +- src/utility/graph.cpp | 157 +++++++++++++++--- src/utility/graph.h | 17 +- .../abstraction/PrismMenuGameTest.cpp | 16 +- test/functional/builder/die.pm | 2 +- test/functional/utility/GraphTest.cpp | 148 ++++++++++++++++- 17 files changed, 381 insertions(+), 95 deletions(-) diff --git a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c index 6aa60fee2..eb041793c 100644 --- a/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c +++ b/resources/3rdparty/cudd-2.5.0/src/cudd/cuddBddAbs.c @@ -540,11 +540,12 @@ cuddBddExistAbstractRepresentativeRecur( DdNode * f, DdNode * cube) { +// printf("entering exists abstract...\n"); DdNode *F, *T, *E, *res, *res1, *res2, *one, *zero, *left, *right, *tmp, *res1Inf, *res2Inf; statLine(manager); one = DD_ONE(manager); - zero = DD_ZERO(manager); + zero = Cudd_Not(one); F = Cudd_Regular(f); // Store whether f is negated. @@ -553,45 +554,57 @@ cuddBddExistAbstractRepresentativeRecur( /* Cube is guaranteed to be a cube at this point. */ if (F == one) { if (fIsNegated) { +// printf("return in preprocessing...\n"); return f; } if (cube == one) { +// printf("return in preprocessing...\n"); return one; } else { +// printf("return in preprocessing...\n"); return cube; } + } else if (cube == one) { +// printf("return in preprocessing...\n"); + return f; } /* From now on, f and cube are non-constant. */ +// printf("F perm %i and cube perm %i\n", manager->perm[F->index], manager->perm[cube->index]); + /* Abstract a variable that does not appear in f. */ if (manager->perm[F->index] > manager->perm[cube->index]) { res = cuddBddExistAbstractRepresentativeRecur(manager, f, cuddT(cube)); if (res == NULL) { return(NULL); } - cuddRef(res); - cuddRef(zero); - res1 = cuddUniqueInter(manager, (int) cube->index, zero, res); + +// res1 = cuddUniqueInter(manager, (int) cube->index, zero, res); + res1 = cuddBddIteRecur(manager, manager->vars[cube->index], zero, res); + if (res1 == NULL) { - Cudd_RecursiveDeref(manager,res); - Cudd_RecursiveDeref(manager,zero); + Cudd_IterDerefBdd(manager,res); + Cudd_IterDerefBdd(manager,zero); return(NULL); } - Cudd_IterDerefBdd(manager, res); - cuddDeref(zero); + cuddDeref(res); + +// printf("return after abstr. var that does not appear in f...\n"); return(res1); } /* Check the cache. */ if (F->ref != 1 && (res = cuddCacheLookup2(manager, Cudd_bddExistAbstractRepresentative, f, cube)) != NULL) { +// printf("return because of cache hit...\n"); return(res); } /* Compute the cofactors of f. */ T = cuddT(F); E = cuddE(F); if (f != F) { +// printf("negating T and E\n"); T = Cudd_Not(T); E = Cudd_Not(E); } @@ -618,8 +631,9 @@ cuddBddExistAbstractRepresentativeRecur( return(NULL); } if (res1 == one) { - if (F->ref != 1) + if (F->ref != 1) { cuddCacheInsert2(manager, Cudd_bddExistAbstractRepresentative, f, cube, one); + } return(one); } cuddRef(res1); @@ -639,7 +653,6 @@ cuddBddExistAbstractRepresentativeRecur( } cuddRef(left); - cuddRef(zero); res1Inf = cuddBddIteRecur(manager, left, res1, zero); if (res1Inf == NULL) { Cudd_IterDerefBdd(manager,res1); @@ -648,11 +661,9 @@ cuddBddExistAbstractRepresentativeRecur( return(NULL); } cuddRef(res1Inf); - cuddDeref(zero); Cudd_IterDerefBdd(manager,res1); - cuddRef(zero); res2Inf = cuddBddIteRecur(manager, left, zero, res2); if (res2Inf == NULL) { Cudd_IterDerefBdd(manager,res1); @@ -662,36 +673,42 @@ cuddBddExistAbstractRepresentativeRecur( return(NULL); } cuddRef(res2Inf); - cuddDeref(zero); Cudd_IterDerefBdd(manager,res2); Cudd_IterDerefBdd(manager,left); - cuddRef(zero); - res = (res1Inf == res2Inf) ? cuddUniqueInter(manager, (int) f->index, zero, res1Inf) : cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + assert(res1Inf != res2Inf); +// res = cuddUniqueInter(manager, (int) f->index, res2Inf, res1Inf); + res = cuddBddIteRecur(manager, manager->vars[F->index], res2Inf, res1Inf); + + if (res == NULL) { + Cudd_IterDerefBdd(manager,res1Inf); + Cudd_IterDerefBdd(manager,res2Inf); + return(NULL); + } cuddRef(res); - cuddDeref(zero); Cudd_IterDerefBdd(manager,res1Inf); Cudd_IterDerefBdd(manager,res2Inf); - if (res == NULL) { - Cudd_IterDerefBdd(manager,res); - return(NULL); - } cuddCacheInsert2(manager, Cudd_bddExistAbstractRepresentative, f, cube, res); cuddDeref(res); +// printf("return properly computed result...\n"); return(res); } else { /* if (cuddI(manager,F->index) < cuddI(manager,cube->index)) */ res1 = cuddBddExistAbstractRepresentativeRecur(manager, E, cube); - if (res1 == NULL) return(NULL); + if (res1 == NULL){ + return(NULL); + } cuddRef(res1); + res2 = cuddBddExistAbstractRepresentativeRecur(manager, T, cube); if (res2 == NULL) { Cudd_IterDerefBdd(manager, res1); return(NULL); } cuddRef(res2); + /* ITE takes care of possible complementation of res1 and of the ** case in which res1 == res2. */ res = cuddBddIteRecur(manager, manager->vars[F->index], res2, res1); @@ -702,8 +719,10 @@ cuddBddExistAbstractRepresentativeRecur( } cuddDeref(res1); cuddDeref(res2); - if (F->ref != 1) + if (F->ref != 1) { cuddCacheInsert2(manager, Cudd_bddExistAbstractRepresentative, f, cube, res); + } +// printf("return of last case...\n"); return(res); } diff --git a/src/models/symbolic/StochasticTwoPlayerGame.cpp b/src/models/symbolic/StochasticTwoPlayerGame.cpp index 6b9f72a14..d8f1df1f6 100644 --- a/src/models/symbolic/StochasticTwoPlayerGame.cpp +++ b/src/models/symbolic/StochasticTwoPlayerGame.cpp @@ -28,7 +28,6 @@ namespace storm { : NondeterministicModel(storm::models::ModelType::S2pg, manager, reachableStates, initialStates, transitionMatrix, rowVariables, rowExpressionAdapter, columnVariables, columnExpressionAdapter, rowColumnMetaVariablePairs, nondeterminismVariables, labelToExpressionMap, rewardModels), player1Variables(player1Variables), player2Variables(player2Variables) { // Compute legal player 1 mask. - transitionMatrix.exportToDot("trans.dot"); illegalPlayer1Mask = transitionMatrix.notZero().existsAbstract(this->getColumnVariables()).existsAbstract(this->getPlayer2Variables()); // Correct the mask for player 2. This is necessary, because it is not yet restricted to the legal choices of player 1. diff --git a/src/storage/dd/CuddAdd.cpp b/src/storage/dd/CuddAdd.cpp index eddc26e28..627527e79 100644 --- a/src/storage/dd/CuddAdd.cpp +++ b/src/storage/dd/CuddAdd.cpp @@ -19,12 +19,12 @@ namespace storm { namespace dd { - Add::Add(std::shared_ptr const> ddManager, ADD cuddAdd, std::set const& containedMetaVariables) : Dd(ddManager, containedMetaVariables), cuddAdd(cuddAdd) { + Add::Add(std::weak_ptr const> ddManager, ADD cuddAdd, std::set const& containedMetaVariables) : Dd(ddManager, containedMetaVariables), cuddAdd(cuddAdd) { // Intentionally left empty. } - Add::Add(std::shared_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables) : Dd(ddManager, metaVariables) { - cuddAdd = fromVector(ddManager, values, odd, metaVariables); + Add::Add(std::weak_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables) : Dd(ddManager, metaVariables) { + cuddAdd = fromVector(ddManager.lock(), values, odd, metaVariables); } Bdd Add::toBdd() const { diff --git a/src/storage/dd/CuddAdd.h b/src/storage/dd/CuddAdd.h index 312096b63..47ed3bcb3 100644 --- a/src/storage/dd/CuddAdd.h +++ b/src/storage/dd/CuddAdd.h @@ -46,7 +46,7 @@ namespace storm { * @param odd An ODD that is used to do the translation. * @param metaVariables The meta variables to use to encode the vector. */ - Add(std::shared_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables); + Add(std::weak_ptr const> ddManager, std::vector const& values, Odd const& odd, std::set const& metaVariables); // Instantiate all copy/move constructors/assignments with the default implementation. Add() = default; @@ -695,7 +695,7 @@ namespace storm { * @param cuddAdd The CUDD ADD to store. * @param containedMetaVariables The meta variables that appear in the DD. */ - Add(std::shared_ptr const> ddManager, ADD cuddAdd, std::set const& containedMetaVariables = std::set()); + Add(std::weak_ptr const> ddManager, ADD cuddAdd, std::set const& containedMetaVariables = std::set()); /*! * Converts the ADD to a row-grouped (sparse) double matrix. If the optional vector is given, it is also diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp index bdcccd515..476cd0880 100644 --- a/src/storage/dd/CuddBdd.cpp +++ b/src/storage/dd/CuddBdd.cpp @@ -16,23 +16,23 @@ namespace storm { namespace dd { - Bdd::Bdd(std::shared_ptr const> ddManager, BDD cuddBdd, std::set const& containedMetaVariables) : Dd(ddManager, containedMetaVariables), cuddBdd(cuddBdd) { + Bdd::Bdd(std::weak_ptr const> ddManager, BDD cuddBdd, std::set const& containedMetaVariables) : Dd(ddManager, containedMetaVariables), cuddBdd(cuddBdd) { // Intentionally left empty. } - Bdd::Bdd(std::shared_ptr const> ddManager, std::vector const& explicitValues, storm::dd::Odd const& odd, std::set const& metaVariables, storm::logic::ComparisonType comparisonType, double value) : Dd(ddManager, metaVariables) { + Bdd::Bdd(std::weak_ptr const> ddManager, std::vector const& explicitValues, storm::dd::Odd const& odd, std::set const& metaVariables, storm::logic::ComparisonType comparisonType, double value) : Dd(ddManager, metaVariables) { switch (comparisonType) { case storm::logic::ComparisonType::Less: - this->cuddBdd = fromVector(ddManager, explicitValues, odd, metaVariables, std::bind(std::greater(), value, std::placeholders::_1)); + this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::greater(), value, std::placeholders::_1)); break; case storm::logic::ComparisonType::LessEqual: - this->cuddBdd = fromVector(ddManager, explicitValues, odd, metaVariables, std::bind(std::greater_equal(), value, std::placeholders::_1)); + this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::greater_equal(), value, std::placeholders::_1)); break; case storm::logic::ComparisonType::Greater: - this->cuddBdd = fromVector(ddManager, explicitValues, odd, metaVariables, std::bind(std::less(), value, std::placeholders::_1)); + this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::less(), value, std::placeholders::_1)); break; case storm::logic::ComparisonType::GreaterEqual: - this->cuddBdd = fromVector(ddManager, explicitValues, odd, metaVariables, std::bind(std::less_equal(), value, std::placeholders::_1)); + this->cuddBdd = fromVector(ddManager.lock(), explicitValues, odd, metaVariables, std::bind(std::less_equal(), value, std::placeholders::_1)); break; } } diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h index b7015b3a3..6ad869428 100644 --- a/src/storage/dd/CuddBdd.h +++ b/src/storage/dd/CuddBdd.h @@ -43,7 +43,7 @@ namespace storm { * @param comparisonType The relation that needs to hold for the values (wrt. to the given value). * @param value The value to compare with. */ - Bdd(std::shared_ptr const> ddManager, std::vector const& explicitValues, storm::dd::Odd const& odd, std::set const& metaVariables, storm::logic::ComparisonType comparisonType, double value); + Bdd(std::weak_ptr const> ddManager, std::vector const& explicitValues, storm::dd::Odd const& odd, std::set const& metaVariables, storm::logic::ComparisonType comparisonType, double value); // Declare the DdManager and DdIterator class as friend so it can access the internals of a DD. friend class DdManager; @@ -325,7 +325,7 @@ namespace storm { * @param cuddBdd The CUDD BDD to store. * @param containedMetaVariables The meta variables that appear in the DD. */ - Bdd(std::shared_ptr const> ddManager, BDD cuddBdd, std::set const& containedMetaVariables = std::set()); + Bdd(std::weak_ptr const> ddManager, BDD cuddBdd, std::set const& containedMetaVariables = std::set()); /*! * Builds a BDD representing the values that make the given filter function evaluate to true. diff --git a/src/storage/dd/CuddDd.cpp b/src/storage/dd/CuddDd.cpp index d56589824..de0a9f22d 100644 --- a/src/storage/dd/CuddDd.cpp +++ b/src/storage/dd/CuddDd.cpp @@ -5,7 +5,7 @@ namespace storm { namespace dd { - Dd::Dd(std::shared_ptr const> ddManager, std::set const& containedMetaVariables) : ddManager(ddManager), containedMetaVariables(containedMetaVariables) { + Dd::Dd(std::weak_ptr const> ddManager, std::set const& containedMetaVariables) : ddManager(ddManager), containedMetaVariables(containedMetaVariables) { // Intentionally left empty. } @@ -26,7 +26,7 @@ namespace storm { } std::shared_ptr const> Dd::getDdManager() const { - return this->ddManager; + return this->ddManager.lock(); } void Dd::addMetaVariables(std::set const& metaVariables) { diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h index dbfa5ddae..7f61c530a 100644 --- a/src/storage/dd/CuddDd.h +++ b/src/storage/dd/CuddDd.h @@ -173,12 +173,12 @@ namespace storm { * @param ddManager The manager responsible for this DD. * @param containedMetaVariables The meta variables that appear in the DD. */ - Dd(std::shared_ptr const> ddManager, std::set const& containedMetaVariables = std::set()); + Dd(std::weak_ptr const> ddManager, std::set const& containedMetaVariables = std::set()); private: // A pointer to the manager responsible for this DD. - std::shared_ptr const> ddManager; + std::weak_ptr const> ddManager; // The meta variables that appear in this DD. std::set containedMetaVariables; diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index b01b12470..375c255d8 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -2,6 +2,8 @@ #include #include +#include "storm-config.h" + #include "src/storage/dd/CuddDdManager.h" #include "src/utility/macros.h" #include "src/storage/expressions/Variable.h" @@ -10,12 +12,11 @@ #include "src/settings/modules/CuddSettings.h" #include "src/storage/expressions/ExpressionManager.h" #include "src/storage/dd/CuddAdd.h" -#include "CuddBdd.h" - +#include "src/storage/dd/CuddBdd.h" namespace storm { namespace dd { - DdManager::DdManager() : metaVariableMap(), cuddManager(), reorderingTechnique(CUDD_REORDER_NONE), manager(new storm::expressions::ExpressionManager()) { + DdManager::DdManager() : cuddManager(), metaVariableMap(), reorderingTechnique(CUDD_REORDER_NONE), manager(new storm::expressions::ExpressionManager()) { this->cuddManager.SetMaxMemory(static_cast(storm::settings::cuddSettings().getMaximalMemory() * 1024ul * 1024ul)); this->cuddManager.SetEpsilon(storm::settings::cuddSettings().getConstantPrecision()); @@ -43,6 +44,12 @@ namespace storm { } } + DdManager::~DdManager() { +#ifndef NDEBUG + cuddManager.DebugCheck(); +#endif + } + Bdd DdManager::getBddOne() const { return Bdd(this->shared_from_this(), cuddManager.bddOne()); } diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h index e70ea0144..b6e5d836e 100644 --- a/src/storage/dd/CuddDdManager.h +++ b/src/storage/dd/CuddDdManager.h @@ -34,6 +34,11 @@ namespace storm { */ DdManager(); + /*! + * Destroys the manager. In debug mode, this will check for errors with CUDD. + */ + ~DdManager(); + // Explictly forbid copying a DdManager, but allow moving it. DdManager(DdManager const& other) = delete; DdManager& operator=(DdManager const& other) = delete; @@ -225,12 +230,14 @@ namespace storm { */ storm::expressions::ExpressionManager& getExpressionManager(); + // The manager responsible for the DDs created/modified with this DdManager. This member strictly needs to + // the first member of the class: upon destruction, the meta variables still destruct DDs that are managed + // by this manager, so we have to make sure it still exists at this point and is destructed later. + Cudd cuddManager; + // A mapping from variables to the meta variable information. std::unordered_map> metaVariableMap; - // The manager responsible for the DDs created/modified with this DdManager. - Cudd cuddManager; - // The technique that is used for dynamic reordering. Cudd_ReorderingType reorderingTechnique; diff --git a/src/storage/dd/CuddDdMetaVariable.cpp b/src/storage/dd/CuddDdMetaVariable.cpp index 4784f7235..2af279216 100644 --- a/src/storage/dd/CuddDdMetaVariable.cpp +++ b/src/storage/dd/CuddDdMetaVariable.cpp @@ -3,14 +3,14 @@ namespace storm { namespace dd { - DdMetaVariable::DdMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, std::vector> const& ddVariables, std::shared_ptr> manager) : name(name), type(MetaVariableType::Int), low(low), high(high), ddVariables(ddVariables), cube(manager->getBddOne()), manager(manager) { + DdMetaVariable::DdMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, std::vector> const& ddVariables, std::weak_ptr> manager) : name(name), type(MetaVariableType::Int), low(low), high(high), ddVariables(ddVariables), cube(manager.lock()->getBddOne()), manager(manager) { // Create the cube of all variables of this meta variable. for (auto const& ddVariable : this->ddVariables) { this->cube &= ddVariable; } } - DdMetaVariable::DdMetaVariable(std::string const& name, std::vector> const& ddVariables, std::shared_ptr> manager) : name(name), type(MetaVariableType::Bool), low(0), high(1), ddVariables(ddVariables), cube(manager->getBddOne()), manager(manager) { + DdMetaVariable::DdMetaVariable(std::string const& name, std::vector> const& ddVariables, std::weak_ptr> manager) : name(name), type(MetaVariableType::Bool), low(0), high(1), ddVariables(ddVariables), cube(manager.lock()->getBddOne()), manager(manager) { // Create the cube of all variables of this meta variable. for (auto const& ddVariable : this->ddVariables) { this->cube &= ddVariable; @@ -38,7 +38,7 @@ namespace storm { } std::shared_ptr> DdMetaVariable::getDdManager() const { - return this->manager; + return this->manager.lock(); } std::vector> const& DdMetaVariable::getDdVariables() const { diff --git a/src/storage/dd/CuddDdMetaVariable.h b/src/storage/dd/CuddDdMetaVariable.h index 54673c83d..216f24060 100644 --- a/src/storage/dd/CuddDdMetaVariable.h +++ b/src/storage/dd/CuddDdMetaVariable.h @@ -42,7 +42,7 @@ namespace storm { * @param ddVariables The vector of variables used to encode this variable. * @param manager A pointer to the manager that is responsible for this meta variable. */ - DdMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, std::vector> const& ddVariables, std::shared_ptr> manager); + DdMetaVariable(std::string const& name, int_fast64_t low, int_fast64_t high, std::vector> const& ddVariables, std::weak_ptr> manager); /*! * Creates a boolean meta variable with the given name. @@ -50,7 +50,7 @@ namespace storm { * @param ddVariables The vector of variables used to encode this variable. * @param manager A pointer to the manager that is responsible for this meta variable. */ - DdMetaVariable(std::string const& name, std::vector> const& ddVariables, std::shared_ptr> manager); + DdMetaVariable(std::string const& name, std::vector> const& ddVariables, std::weak_ptr> manager); // Explictly generate all default versions of copy/move constructors/assignments. DdMetaVariable(DdMetaVariable const& other) = default; @@ -136,7 +136,7 @@ namespace storm { Bdd cube; // A pointer to the manager responsible for this meta variable. - std::shared_ptr> manager; + std::weak_ptr> manager; }; } } diff --git a/src/utility/graph.cpp b/src/utility/graph.cpp index e78408a1b..9ea5be105 100644 --- a/src/utility/graph.cpp +++ b/src/utility/graph.cpp @@ -690,7 +690,7 @@ namespace storm { } template - GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy) { + GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy, bool produceStrategies) { // The solution set. storm::dd::Bdd solution = psiStates; @@ -724,29 +724,136 @@ namespace storm { // Since we have determined the inverse of the desired set, we need to complement it now. solution = !solution && model.getReachableStates(); + + // Determine all transitions between prob0 states. + storm::dd::Bdd transitionsBetweenProb0States = solution && (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())); + + // Determine the distributions that have only successors that are prob0 states. + storm::dd::Bdd onlyProb0Successors = (transitionsBetweenProb0States || model.getIllegalSuccessorMask()).universalAbstract(model.getColumnVariables()); + + boost::optional> player2StrategyBdd; + if (produceStrategies && player2Strategy == OptimizationDirection::Minimize) { + // Pick a distribution that has only prob0 successors. + player2StrategyBdd = onlyProb0Successors.existsAbstractRepresentative(model.getPlayer2Variables()); + } + + boost::optional> player1StrategyBdd; + if (produceStrategies && player1Strategy == OptimizationDirection::Minimize) { + // Move from player 2 choices with only prob0 successors to player 1 choices with only prob 0 successors. + onlyProb0Successors = onlyProb0Successors.existsAbstract(model.getPlayer2Variables()); + + // Pick a prob0 player 2 state. + player1StrategyBdd = onlyProb0Successors.existsAbstractRepresentative(model.getPlayer1Variables()); + } + + return GameProb01Result(solution, player1StrategyBdd, player2StrategyBdd); + } + + template + GameProb01Result performProb1(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy, bool produceStrategies) { - return GameProb01Result(solution); + // Create two sets of states. Those states for which we definitely know that their probability is 1 and + // those states that potentially have a probability of 1. + storm::dd::Bdd maybeStates = model.getReachableStates(); + storm::dd::Bdd solution = psiStates; + + // A flag that governs whether strategies are produced in the current iteration. + bool produceStrategiesInIteration = false; + boost::optional> player1StrategyBdd; + boost::optional> consideredPlayer1States; + boost::optional> player2StrategyBdd; + boost::optional> consideredPlayer2States; + + bool maybeStatesDone = false; + uint_fast64_t maybeStateIterations = 0; + while (!maybeStatesDone || produceStrategiesInIteration) { + bool solutionStatesDone = false; + uint_fast64_t solutionStateIterations = 0; + + // If we are to produce strategies in this iteration, we prepare some storage. + if (produceStrategiesInIteration) { + player1StrategyBdd = model.getManager().getBddZero(); + consideredPlayer1States = model.getManager().getBddZero(); + player2StrategyBdd = model.getManager().getBddZero(); + consideredPlayer2States = model.getManager().getBddZero(); + } + + while (!solutionStatesDone) { + // Start by computing the transitions that have only maybe states as successors. Note that at + // this point, there may be illegal transitions. + storm::dd::Bdd distributionsStayingInMaybe = (!transitionMatrix || maybeStates.swapVariables(model.getRowColumnMetaVariablePairs())).universalAbstract(model.getColumnVariables()); + + // Then, determine all distributions that have at least one successor in the states that have + // probability 1. + storm::dd::Bdd distributionsWithProb1Successor = (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())).existsAbstract(model.getColumnVariables()); + + // The valid distributions are then those that emanate from phi states, stay completely in the + // maybe states and have at least one successor with probability 1. + storm::dd::Bdd valid = phiStates && distributionsStayingInMaybe && distributionsWithProb1Successor; + + // Depending on the strategy of player 2, we need to check whether all choices are valid or + // there exists a valid choice. + if (player2Strategy == OptimizationDirection::Minimize) { + valid = (valid || model.getIllegalPlayer2Mask()).universalAbstract(model.getPlayer2Variables()); + } else { + if (produceStrategiesInIteration) { + storm::dd::Bdd newValidDistributions = valid && !consideredPlayer2States.get(); + player2StrategyBdd.get() = player2StrategyBdd.get() || newValidDistributions.existsAbstractRepresentative(model.getPlayer2Variables()); + } + + valid = valid.existsAbstract(model.getPlayer2Variables()); + + if (produceStrategiesInIteration) { + consideredPlayer2States.get() |= valid; + } + } + + // And do the same for player 1. + if (player1Strategy == OptimizationDirection::Minimize) { + valid = (valid || model.getIllegalPlayer1Mask()).universalAbstract(model.getPlayer1Variables()); + } else { + if (produceStrategiesInIteration) { + storm::dd::Bdd newValidDistributions = valid && !consideredPlayer1States.get(); + player1StrategyBdd.get() = player1StrategyBdd.get() || newValidDistributions.existsAbstractRepresentative(model.getPlayer1Variables()); + } + + valid = valid.existsAbstract(model.getPlayer1Variables()); + + if (produceStrategiesInIteration) { + consideredPlayer1States.get() |= valid; + } + } + + // Add psi states to result. + valid |= psiStates; + + // If no new states were added, we have found the current hypothesis for the states with + // probability 1. + if (valid == solution) { + solutionStatesDone = true; + } else { + solution = valid; + } + ++solutionStateIterations; + } + + // If the states with probability 1 and the potential probability 1 states coincide, we have found + // the solution. + if (solution == maybeStates) { + maybeStatesDone = true; + + // If we were asked to produce strategies, we propagate that by triggering another iteration. + // We only do this if at least one strategy will be produced. + produceStrategiesInIteration = produceStrategies && (player1Strategy == OptimizationDirection::Maximize || player2Strategy == OptimizationDirection::Maximize); + } else { + // Otherwise, we use the current hypothesis for the states with probability 1 as the new maybe + // state set. + maybeStates = solution; + } + ++maybeStateIterations; + } -// // Determine all transitions between prob0 states. -// storm::dd::Bdd transitionsBetweenProb0States = solution && (transitionMatrix && solution.swapVariables(model.getRowColumnMetaVariablePairs())); -// -// // Determine the distributions that have only successors that are prob0 states. -// storm::dd::Bdd onlyProb0Successors = (transitionsBetweenProb0States || model.getIllegalSuccessorMask()).universalAbstract(model.getColumnVariables()); -// -// boost::optional> player2StrategyBdd; -// if (player2Strategy == OptimizationDirection::Minimize) { -// // Pick a distribution that has only prob0 successors. -// player2StrategyBdd = onlyProb0Successors.existsAbstractRepresentative(model.getPlayer2Variables()); -// } -// -// boost::optional> player1StrategyBdd; -// if (player1Strategy == OptimizationDirection::Minimize) { -// // Move from player 2 choices with only prob0 successors to player 1 choices with only prob 0 successors. -// onlyProb0Successors = onlyProb0Successors.existsAbstract(model.getPlayer2Variables()); -// -// // Pick a prob0 player 2 state. -// onlyProb0Successors.existsAbstractRepresentative(model.getPlayer1Variables()); -// } + return GameProb01Result(solution, player1StrategyBdd, player2StrategyBdd); } template @@ -1081,8 +1188,10 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01Min(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; - template GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); - + template GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy, bool produceStrategies); + + template GameProb01Result performProb1(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy, bool produceStrategies); + } // namespace graph } // namespace utility } // namespace storm diff --git a/src/utility/graph.h b/src/utility/graph.h index ccc7cc784..7475d0570 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -451,9 +451,24 @@ namespace storm { * @param transitionMatrix The transition matrix of the model as a BDD. * @param phiStates The BDD containing all phi states of the model. * @param psiStates The BDD containing all psi states of the model. + * @param produceStrategies A flag indicating whether strategies should be produced. Note that the strategies + * are only produced in case the choices of the player are not irrelevant. */ template - GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy); + GameProb01Result performProb0(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategy, bool produceStrategies = false); + + /*! + * Computes the set of states that have probability 1 given the strategies of the two players. + * + * @param model The (symbolic) model for which to compute the set of states. + * @param transitionMatrix The transition matrix of the model as a BDD. + * @param phiStates The BDD containing all phi states of the model. + * @param psiStates The BDD containing all psi states of the model. + * @param produceStrategies A flag indicating whether strategies should be produced. Note that the strategies + * are only produced in case the choices of the player are not irrelevant. + */ + template + GameProb01Result performProb1(storm::models::symbolic::StochasticTwoPlayerGame const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, storm::OptimizationDirection const& player1Strategy, storm::OptimizationDirection const& player2Strategybool, bool produceStrategies = false); /*! * Performs a topological sort of the states of the system according to the given transitions. diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 8eb25f9ed..30b55e863 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -29,7 +29,7 @@ TEST(PrismMenuGame, DieAbstractionTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(15, game.getNumberOfTransitions()); + EXPECT_EQ(10, game.getNumberOfTransitions()); EXPECT_EQ(2, game.getNumberOfStates()); } @@ -47,7 +47,7 @@ TEST(PrismMenuGame, DieAbstractionAndRefinementTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(15, game.getNumberOfTransitions()); + EXPECT_EQ(10, game.getNumberOfTransitions()); EXPECT_EQ(3, game.getNumberOfStates()); } @@ -95,7 +95,7 @@ TEST(PrismMenuGame, CrowdsAbstractionTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(16, game.getNumberOfTransitions()); + EXPECT_EQ(11, game.getNumberOfTransitions()); EXPECT_EQ(2, game.getNumberOfStates()); } @@ -114,7 +114,7 @@ TEST(PrismMenuGame, CrowdsAbstractionAndRefinementTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(38, game.getNumberOfTransitions()); + EXPECT_EQ(28, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); } @@ -204,7 +204,7 @@ TEST(PrismMenuGame, TwoDiceAbstractionTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(58, game.getNumberOfTransitions()); + EXPECT_EQ(34, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); } @@ -225,7 +225,7 @@ TEST(PrismMenuGame, TwoDiceAbstractionAndRefinementTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(212, game.getNumberOfTransitions()); + EXPECT_EQ(164, game.getNumberOfTransitions()); EXPECT_EQ(8, game.getNumberOfStates()); } @@ -295,7 +295,7 @@ TEST(PrismMenuGame, WlanAbstractionTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(307, game.getNumberOfTransitions()); + EXPECT_EQ(277, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); } @@ -317,7 +317,7 @@ TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(612, game.getNumberOfTransitions()); + EXPECT_EQ(556, game.getNumberOfTransitions()); EXPECT_EQ(8, game.getNumberOfStates()); } diff --git a/test/functional/builder/die.pm b/test/functional/builder/die.pm index 6fd3f8231..af0797cff 100644 --- a/test/functional/builder/die.pm +++ b/test/functional/builder/die.pm @@ -29,4 +29,4 @@ label "three" = s=7&d=3; label "four" = s=7&d=4; label "five" = s=7&d=5; label "six" = s=7&d=6; -label "done" = s=7; \ No newline at end of file +label "done" = s=7; diff --git a/test/functional/utility/GraphTest.cpp b/test/functional/utility/GraphTest.cpp index 6dba4e688..4f4814dbd 100644 --- a/test/functional/utility/GraphTest.cpp +++ b/test/functional/utility/GraphTest.cpp @@ -96,7 +96,7 @@ TEST(GraphTest, SymbolicProb01MinMax) { #include "src/utility/solver.h" -TEST(GraphTest, SymbolicProb0StochasticGame) { +TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); std::vector initialPredicates; @@ -107,18 +107,148 @@ TEST(GraphTest, SymbolicProb0StochasticGame) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); + + game.getQualitativeTransitionMatrix().toAdd().exportToDot("trans.dot"); + + // The target states are those states where !(s < 3). + storm::dd::Bdd targetStates = game.getStates(initialPredicates[0], true); + + storm::utility::graph::GameProb01Result result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); - storm::utility::graph::GameProb01Result result1 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize); - EXPECT_EQ(1, result1.states.getNonZeroCount()); + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); - storm::utility::graph::GameProb01Result result2 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize); - EXPECT_EQ(1, result2.states.getNonZeroCount()); + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(0, result.states.getNonZeroCount()); - storm::utility::graph::GameProb01Result result3 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize); - EXPECT_EQ(0, result3.states.getNonZeroCount()); + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(2, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(0, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(2, result.states.getNonZeroCount()); + + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); + + result.player1Strategy.get().toAdd().exportToDot("player1.dot"); + result.player2Strategy.get().toAdd().exportToDot("player2.dot"); + exit(-1); + + abstractProgram.refine({manager.getVariableExpression("s") < manager.integer(2)}); + game = abstractProgram.getAbstractGame(); - storm::utility::graph::GameProb01Result result4 = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), game.getStates(initialPredicates.front(), true), storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize); - EXPECT_EQ(0, result4.states.getNonZeroCount()); + // We need to create a new BDD for the target states since the reachable states might have changed. + targetStates = game.getStates(initialPredicates[0], true); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(0, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(3, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(0, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(3, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(0, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(3, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(0, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(3, result.states.getNonZeroCount()); +} + +TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/two_dice.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("d1") == manager.integer(6)); + + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("d2") == manager.integer(6)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); + + // The target states are those states where s1 == 7 & s2 == 7 & d1 + d2 == 1. + storm::dd::Bdd targetStates = game.getStates(initialPredicates[7], false) && game.getStates(initialPredicates[22], false) && game.getStates(initialPredicates[9], false) && game.getStates(initialPredicates[24], false); + + storm::utility::graph::GameProb01Result result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize); + EXPECT_EQ(153, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(153, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(153, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(153, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(1, result.states.getNonZeroCount()); } #endif From e8b7928831d7d559e88b9470525904940b0a6080 Mon Sep 17 00:00:00 2001 From: dehnert Date: Tue, 22 Sep 2015 17:53:27 +0200 Subject: [PATCH 022/400] fixed minor bug Former-commit-id: 6d208b877ab1c844fd34684693b9975772beb2cd --- src/utility/graph.cpp | 2 +- test/functional/utility/GraphTest.cpp | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/utility/graph.cpp b/src/utility/graph.cpp index 9ea5be105..34e10467d 100644 --- a/src/utility/graph.cpp +++ b/src/utility/graph.cpp @@ -844,7 +844,7 @@ namespace storm { // If we were asked to produce strategies, we propagate that by triggering another iteration. // We only do this if at least one strategy will be produced. - produceStrategiesInIteration = produceStrategies && (player1Strategy == OptimizationDirection::Maximize || player2Strategy == OptimizationDirection::Maximize); + produceStrategiesInIteration = !produceStrategiesInIteration && produceStrategies && (player1Strategy == OptimizationDirection::Maximize || player2Strategy == OptimizationDirection::Maximize); } else { // Otherwise, we use the current hypothesis for the states with probability 1 as the new maybe // state set. diff --git a/test/functional/utility/GraphTest.cpp b/test/functional/utility/GraphTest.cpp index 4f4814dbd..6ac99e129 100644 --- a/test/functional/utility/GraphTest.cpp +++ b/test/functional/utility/GraphTest.cpp @@ -107,8 +107,6 @@ TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - - game.getQualitativeTransitionMatrix().toAdd().exportToDot("trans.dot"); // The target states are those states where !(s < 3). storm::dd::Bdd targetStates = game.getStates(initialPredicates[0], true); @@ -138,14 +136,9 @@ TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); EXPECT_EQ(2, result.states.getNonZeroCount()); - EXPECT_TRUE(static_cast(result.player1Strategy)); EXPECT_TRUE(static_cast(result.player2Strategy)); - - result.player1Strategy.get().toAdd().exportToDot("player1.dot"); - result.player2Strategy.get().toAdd().exportToDot("player2.dot"); - exit(-1); - + abstractProgram.refine({manager.getVariableExpression("s") < manager.integer(2)}); game = abstractProgram.getAbstractGame(); @@ -154,6 +147,8 @@ TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(0, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(3, result.states.getNonZeroCount()); @@ -175,6 +170,8 @@ TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); EXPECT_EQ(3, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); } TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { @@ -226,8 +223,10 @@ TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { // The target states are those states where s1 == 7 & s2 == 7 & d1 + d2 == 1. storm::dd::Bdd targetStates = game.getStates(initialPredicates[7], false) && game.getStates(initialPredicates[22], false) && game.getStates(initialPredicates[9], false) && game.getStates(initialPredicates[24], false); - storm::utility::graph::GameProb01Result result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize); + storm::utility::graph::GameProb01Result result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(153, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(1, result.states.getNonZeroCount()); @@ -249,6 +248,8 @@ TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); EXPECT_EQ(1, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); } #endif From c624b194271545906f3e1d4a90c0e6429dc05b96 Mon Sep 17 00:00:00 2001 From: dehnert Date: Tue, 22 Sep 2015 19:02:05 +0200 Subject: [PATCH 023/400] added no-cuts option. prob1 tests for game now passing. Former-commit-id: 3806747948377378e393f2a78a05b3692b93e7dc --- src/builder/DdPrismModelBuilder.cpp | 5 + src/builder/ExplicitPrismModelBuilder.cpp | 6 + src/settings/Option.cpp | 4 +- src/settings/modules/GeneralSettings.cpp | 10 +- src/settings/modules/GeneralSettings.h | 8 + src/utility/graph.cpp | 3 +- .../abstraction/PrismMenuGameTest.cpp | 14 +- test/functional/builder/wlan0-2-4.nm | 114 ++++++++++++++ test/functional/utility/GraphTest.cpp | 146 ++++++++++++++++++ 9 files changed, 298 insertions(+), 12 deletions(-) create mode 100644 test/functional/builder/wlan0-2-4.nm diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp index 4f6ab3871..721a92480 100644 --- a/src/builder/DdPrismModelBuilder.cpp +++ b/src/builder/DdPrismModelBuilder.cpp @@ -254,6 +254,11 @@ namespace storm { template void DdPrismModelBuilder::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) { + // If cutting the model was disabled, we do not set anything. + if (storm::settings::generalSettings().isNoCutsSet()) { + return; + } + if (formula.isAtomicExpressionFormula()) { terminalStates = formula.asAtomicExpressionFormula().getExpression(); } else if (formula.isAtomicLabelFormula()) { diff --git a/src/builder/ExplicitPrismModelBuilder.cpp b/src/builder/ExplicitPrismModelBuilder.cpp index cfc7d8577..aa873f618 100644 --- a/src/builder/ExplicitPrismModelBuilder.cpp +++ b/src/builder/ExplicitPrismModelBuilder.cpp @@ -113,6 +113,7 @@ namespace storm { template ExplicitPrismModelBuilder::Options::Options(storm::logic::Formula const& formula) : buildCommandLabels(false), buildAllRewardModels(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(false), labelsToBuild(std::set()), expressionLabels(std::vector()), terminalStates(), negatedTerminalStates() { this->preserveFormula(formula); + this->setTerminalStatesFromFormula(formula); } template @@ -132,6 +133,11 @@ namespace storm { template void ExplicitPrismModelBuilder::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) { + // If cutting the model was disabled, we do not set anything. + if (storm::settings::generalSettings().isNoCutsSet()) { + return; + } + if (formula.isAtomicExpressionFormula()) { terminalStates = formula.asAtomicExpressionFormula().getExpression(); } else if (formula.isAtomicLabelFormula()) { diff --git a/src/settings/Option.cpp b/src/settings/Option.cpp index b5cba7759..d011de507 100644 --- a/src/settings/Option.cpp +++ b/src/settings/Option.cpp @@ -115,10 +115,10 @@ namespace storm { STORM_LOG_THROW(!longName.empty(), storm::exceptions::IllegalArgumentException, "Unable to construct option with empty name."); STORM_LOG_THROW(!moduleName.empty(), storm::exceptions::IllegalArgumentException, "Unable to construct option with empty module name."); - bool longNameContainsNonAlpha = std::find_if(longName.begin(), longName.end(), [](char c) { return !(std::isalpha(c) || std::isdigit(c)); }) != longName.end(); + bool longNameContainsNonAlpha = std::find_if(longName.begin(), longName.end(), [](char c) { return !(std::isalpha(c) || std::isdigit(c) || c == '-'); }) != longName.end(); STORM_LOG_THROW(!longNameContainsNonAlpha, storm::exceptions::IllegalArgumentException, "Unable to construct option with illegal long name '" << longName << "'."); - bool shortNameContainsNonAlpha = std::find_if(shortName.begin(), shortName.end(), [](char c) { return !(std::isalpha(c) || std::isdigit(c)); }) != shortName.end(); + bool shortNameContainsNonAlpha = std::find_if(shortName.begin(), shortName.end(), [](char c) { return !(std::isalpha(c) || std::isdigit(c) || c == '-'); }) != shortName.end(); STORM_LOG_THROW(!shortNameContainsNonAlpha, storm::exceptions::IllegalArgumentException, "Unable to construct option with illegal short name '" << shortName << "'."); // Then index all arguments. diff --git a/src/settings/modules/GeneralSettings.cpp b/src/settings/modules/GeneralSettings.cpp index 893dd4717..5faea4ed2 100644 --- a/src/settings/modules/GeneralSettings.cpp +++ b/src/settings/modules/GeneralSettings.cpp @@ -36,7 +36,8 @@ namespace storm { const std::string GeneralSettings::choiceLabelingOptionName = "choicelab"; const std::string GeneralSettings::counterexampleOptionName = "counterexample"; const std::string GeneralSettings::counterexampleOptionShortName = "cex"; - const std::string GeneralSettings::dontFixDeadlockOptionName = "nofixdl"; + const std::string GeneralSettings::noCutsOptionName = "no-cuts"; + const std::string GeneralSettings::dontFixDeadlockOptionName = "no-fixdl"; const std::string GeneralSettings::dontFixDeadlockOptionShortName = "ndl"; const std::string GeneralSettings::timeoutOptionName = "timeout"; const std::string GeneralSettings::timeoutOptionShortName = "t"; @@ -88,7 +89,8 @@ namespace storm { .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The file from which to read the state rewards.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, choiceLabelingOptionName, false, "If given, the choice labels are read from this file and added to the explicit model. Note that this requires the model to be given as an explicit model (i.e., via --" + explicitOptionName + ").") .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The file from which to read the choice labels.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, dontFixDeadlockOptionName, false, "If the model contains deadlock states, they need to be fixed by setting this option.").setShortName(dontFixDeadlockOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, noCutsOptionName, false, "Do not perform cuts when buildings the state space.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, dontFixDeadlockOptionName, false, "If the model contains deadlock states, this flag disables automatically fixing them.").setShortName(dontFixDeadlockOptionShortName).build()); std::vector engines = {"sparse", "hybrid", "dd"}; this->addOption(storm::settings::OptionBuilder(moduleName, engineOptionName, false, "Sets which engine is used for model building and model checking.").setShortName(engineOptionShortName) @@ -220,6 +222,10 @@ namespace storm { return this->getOption(dontFixDeadlockOptionName).getHasOptionBeenSet(); } + bool GeneralSettings::isNoCutsSet() const { + return this->getOption(noCutsOptionName).getHasOptionBeenSet(); + } + std::unique_ptr GeneralSettings::overrideDontFixDeadlocksSet(bool stateToSet) { return this->overrideOption(dontFixDeadlockOptionName, stateToSet); } diff --git a/src/settings/modules/GeneralSettings.h b/src/settings/modules/GeneralSettings.h index 1c7abdc98..39ba1e4f6 100644 --- a/src/settings/modules/GeneralSettings.h +++ b/src/settings/modules/GeneralSettings.h @@ -215,6 +215,13 @@ namespace storm { * @return True if the dont-fix-deadlocks option was set. */ bool isDontFixDeadlocksSet() const; + + /*! + * Retrieves whether the no-cuts option was set. + * + * @return True if the no-cuts option was set. + */ + bool isNoCutsSet() const; /*! * Overrides the option to not fix deadlocks by setting it to the specified value. As soon as the @@ -384,6 +391,7 @@ namespace storm { static const std::string choiceLabelingOptionName; static const std::string counterexampleOptionName; static const std::string counterexampleOptionShortName; + static const std::string noCutsOptionName; static const std::string dontFixDeadlockOptionName; static const std::string dontFixDeadlockOptionShortName; static const std::string timeoutOptionName; diff --git a/src/utility/graph.cpp b/src/utility/graph.cpp index 34e10467d..1d0e850da 100644 --- a/src/utility/graph.cpp +++ b/src/utility/graph.cpp @@ -824,7 +824,8 @@ namespace storm { } } - // Add psi states to result. + // Explicitly add psi states to result since they may have transitions going to some state that + // does not have a reachability probability of 1. valid |= psiStates; // If no new states were added, we have found the current hypothesis for the states with diff --git a/test/functional/abstraction/PrismMenuGameTest.cpp b/test/functional/abstraction/PrismMenuGameTest.cpp index 30b55e863..6425c05b7 100644 --- a/test/functional/abstraction/PrismMenuGameTest.cpp +++ b/test/functional/abstraction/PrismMenuGameTest.cpp @@ -280,7 +280,7 @@ TEST(PrismMenuGame, TwoDiceFullAbstractionTest) { } TEST(PrismMenuGame, WlanAbstractionTest) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-4.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); @@ -295,12 +295,12 @@ TEST(PrismMenuGame, WlanAbstractionTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(277, game.getNumberOfTransitions()); + EXPECT_EQ(279, game.getNumberOfTransitions()); EXPECT_EQ(4, game.getNumberOfStates()); } TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-4.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); @@ -317,12 +317,12 @@ TEST(PrismMenuGame, WlanAbstractionAndRefinementTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(556, game.getNumberOfTransitions()); + EXPECT_EQ(560, game.getNumberOfTransitions()); EXPECT_EQ(8, game.getNumberOfStates()); } TEST(PrismMenuGame, WlanFullAbstractionTest) { - storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm"); + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-4.nm"); program = program.substituteConstants(); program = program.flattenModules(std::make_unique()); @@ -435,8 +435,8 @@ TEST(PrismMenuGame, WlanFullAbstractionTest) { storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); - EXPECT_EQ(59, game.getNumberOfTransitions()); - EXPECT_EQ(37, game.getNumberOfStates()); + EXPECT_EQ(9503, game.getNumberOfTransitions()); + EXPECT_EQ(5523, game.getNumberOfStates()); } #endif \ No newline at end of file diff --git a/test/functional/builder/wlan0-2-4.nm b/test/functional/builder/wlan0-2-4.nm new file mode 100644 index 000000000..136be18c5 --- /dev/null +++ b/test/functional/builder/wlan0-2-4.nm @@ -0,0 +1,114 @@ +mdp +const int COL = 2; +const int ASLOTTIME = 1; +const int DIFS = 3; +const int VULN = 1; +const int TRANS_TIME_MAX = 4; +const int TRANS_TIME_MIN = 4; +const int ACK_TO = 6; +const int ACK = 4; +const int SIFS = 1; +const int TIME_MAX = ((max(6, 4)) + 1); +const int MAX_BACKOFF = 0; + + +formula busy = ((c1 > 0) | (c2 > 0)); +formula free = ((c1 = 0) & (c2 = 0)); + +module medium + col: [0..2] init 0; + c1: [0..2] init 0; + c2: [0..2] init 0; + [send1] ((c1 = 0) & (c2 = 0)) -> 1 : (c1' = 1); + [send2] ((c2 = 0) & (c1 = 0)) -> 1 : (c2' = 1); + [send1] ((c1 = 0) & (c2 > 0)) -> 1 : (col' = (min((col + 1), 2))) & (c1' = 2) & (c2' = 2); + [send2] ((c2 = 0) & (c1 > 0)) -> 1 : (col' = (min((col + 1), 2))) & (c1' = 2) & (c2' = 2); + [finish1] (c1 > 0) -> 1 : (c1' = 0); + [finish2] (c2 > 0) -> 1 : (c2' = 0); +endmodule + +module station1 + x1: [0..((max(6, 4)) + 1)] init 0; + s1: [1..12] init 1; + slot1: [0..1] init 0; + backoff1: [0..15] init 0; + bc1: [0..1] init 0; + [time] (((s1 = 1) & (x1 < 3)) & ((c1 = 0) & (c2 = 0))) -> 1 : (x1' = (min((x1 + 1), 7))); + [] ((s1 = 1) & ((x1 = 3) | (x1 = 2))) -> 1 : (x1' = 0) & (s1' = 8); + [] ((s1 = 1) & ((c1 > 0) | (c2 > 0))) -> 1 : (x1' = 0) & (s1' = 2); + [time] ((s1 = 2) & ((c1 > 0) | (c2 > 0))) -> 1 : (s1' = 2); + [] ((s1 = 2) & ((c1 = 0) & (c2 = 0))) -> 1 : (s1' = 3); + [time] (((s1 = 3) & (x1 < 3)) & ((c1 = 0) & (c2 = 0))) -> 1 : (x1' = (min((x1 + 1), 7))); + [] ((s1 = 3) & ((c1 > 0) | (c2 > 0))) -> 1 : (x1' = 0) & (s1' = 2); + [] (((s1 = 3) & ((x1 = 3) | (x1 = 2))) & (bc1 = 0)) -> 1 : (x1' = 0) & (s1' = 4) & (slot1' = 0) & (bc1' = (min((bc1 + 1), 0))); + [] (s1 = 4) -> (1 / 16) : (s1' = 5) & (backoff1' = 0) + (1 / 16) : (s1' = 5) & (backoff1' = 1) + (1 / 16) : (s1' = 5) & (backoff1' = 2) + (1 / 16) : (s1' = 5) & (backoff1' = 3) + (1 / 16) : (s1' = 5) & (backoff1' = 4) + (1 / 16) : (s1' = 5) & (backoff1' = 5) + (1 / 16) : (s1' = 5) & (backoff1' = 6) + (1 / 16) : (s1' = 5) & (backoff1' = 7) + (1 / 16) : (s1' = 5) & (backoff1' = 8) + (1 / 16) : (s1' = 5) & (backoff1' = 9) + (1 / 16) : (s1' = 5) & (backoff1' = 10) + (1 / 16) : (s1' = 5) & (backoff1' = 11) + (1 / 16) : (s1' = 5) & (backoff1' = 12) + (1 / 16) : (s1' = 5) & (backoff1' = 13) + (1 / 16) : (s1' = 5) & (backoff1' = 14) + (1 / 16) : (s1' = 5) & (backoff1' = 15); + [time] (((s1 = 5) & (x1 < 1)) & ((c1 = 0) & (c2 = 0))) -> 1 : (x1' = (min((x1 + 1), 7))); + [] (((s1 = 5) & (x1 = 1)) & (backoff1 > 0)) -> 1 : (x1' = 0) & (s1' = 5) & (backoff1' = (backoff1 - 1)); + [] ((((s1 = 5) & (x1 = 1)) & (backoff1 = 0)) & (slot1 > 0)) -> 1 : (x1' = 0) & (s1' = 5) & (slot1' = (slot1 - 1)) & (backoff1' = 15); + [] ((((s1 = 5) & (x1 = 1)) & (backoff1 = 0)) & (slot1 = 0)) -> 1 : (x1' = 0) & (s1' = 8); + [] ((s1 = 5) & ((c1 > 0) | (c2 > 0))) -> 1 : (x1' = 0) & (s1' = 6); + [time] ((s1 = 6) & ((c1 > 0) | (c2 > 0))) -> 1 : (s1' = 6); + [] ((s1 = 6) & ((c1 = 0) & (c2 = 0))) -> 1 : (s1' = 7); + [time] (((s1 = 7) & (x1 < 3)) & ((c1 = 0) & (c2 = 0))) -> 1 : (x1' = (min((x1 + 1), 7))); + [] ((s1 = 7) & ((x1 = 3) | (x1 = 2))) -> 1 : (x1' = 0) & (s1' = 5); + [] ((s1 = 7) & ((c1 > 0) | (c2 > 0))) -> 1 : (x1' = 0) & (s1' = 6); + [time] ((s1 = 8) & (x1 < 1)) -> 1 : (x1' = (min((x1 + 1), 7))); + [send1] ((s1 = 8) & ((x1 = 1) | (x1 = 0))) -> 1 : (x1' = 0) & (s1' = 9); + [time] ((s1 = 9) & (x1 < 4)) -> 1 : (x1' = (min((x1 + 1), 7))); + [finish1] (((s1 = 9) & (x1 >= 4)) & (c1 = 1)) -> 1 : (x1' = 0) & (s1' = 10); + [finish1] (((s1 = 9) & (x1 >= 4)) & (c1 = 2)) -> 1 : (x1' = 0) & (s1' = 11); + [] ((((s1 = 10) & (c1 = 0)) & (x1 = 0)) & ((c1 > 0) | (c2 > 0))) -> 1 : (s1' = 2); + [time] ((((s1 = 10) & (c1 = 0)) & (x1 = 0)) & ((c1 = 0) & (c2 = 0))) -> 1 : (x1' = (min((x1 + 1), 7))); + [send1] (((s1 = 10) & (c1 = 0)) & ((x1 = 1) | ((x1 = 0) & ((c1 = 0) & (c2 = 0))))) -> 1 : (x1' = 0) & (s1' = 10); + [time] (((s1 = 10) & (c1 = 1)) & (x1 < 4)) -> 1 : (x1' = (min((x1 + 1), 7))); + [finish1] (((s1 = 10) & (c1 = 1)) & ((x1 = 4) | (x1 = 3))) -> 1 : (x1' = 0) & (s1' = 12) & (bc1' = 0); + [] (((s1 = 11) & (x1 = 0)) & ((c1 > 0) | (c2 > 0))) -> 1 : (s1' = 2); + [time] (((s1 = 11) & (x1 = 0)) & ((c1 = 0) & (c2 = 0))) -> 1 : (x1' = (min((x1 + 1), 7))); + [time] (((s1 = 11) & (x1 > 0)) & (x1 < 6)) -> 1 : (x1' = (min((x1 + 1), 7))); + [] ((s1 = 11) & (x1 = 6)) -> 1 : (x1' = 0) & (s1' = 3); + [time] (s1 = 12) -> 1 : (s1' = 12); +endmodule + +module station2 + x2: [0..((max(6, 4)) + 1)] init 0; + s2: [1..12] init 1; + slot2: [0..1] init 0; + backoff2: [0..15] init 0; + bc2: [0..1] init 0; + [time] (((s2 = 1) & (x2 < 3)) & ((c2 = 0) & (c1 = 0))) -> 1 : (x2' = (min((x2 + 1), 7))); + [] ((s2 = 1) & ((x2 = 3) | (x2 = 2))) -> 1 : (x2' = 0) & (s2' = 8); + [] ((s2 = 1) & ((c2 > 0) | (c1 > 0))) -> 1 : (x2' = 0) & (s2' = 2); + [time] ((s2 = 2) & ((c2 > 0) | (c1 > 0))) -> 1 : (s2' = 2); + [] ((s2 = 2) & ((c2 = 0) & (c1 = 0))) -> 1 : (s2' = 3); + [time] (((s2 = 3) & (x2 < 3)) & ((c2 = 0) & (c1 = 0))) -> 1 : (x2' = (min((x2 + 1), 7))); + [] ((s2 = 3) & ((c2 > 0) | (c1 > 0))) -> 1 : (x2' = 0) & (s2' = 2); + [] (((s2 = 3) & ((x2 = 3) | (x2 = 2))) & (bc2 = 0)) -> 1 : (x2' = 0) & (s2' = 4) & (slot2' = 0) & (bc2' = (min((bc2 + 1), 0))); + [] (s2 = 4) -> (1 / 16) : (s2' = 5) & (backoff2' = 0) + (1 / 16) : (s2' = 5) & (backoff2' = 1) + (1 / 16) : (s2' = 5) & (backoff2' = 2) + (1 / 16) : (s2' = 5) & (backoff2' = 3) + (1 / 16) : (s2' = 5) & (backoff2' = 4) + (1 / 16) : (s2' = 5) & (backoff2' = 5) + (1 / 16) : (s2' = 5) & (backoff2' = 6) + (1 / 16) : (s2' = 5) & (backoff2' = 7) + (1 / 16) : (s2' = 5) & (backoff2' = 8) + (1 / 16) : (s2' = 5) & (backoff2' = 9) + (1 / 16) : (s2' = 5) & (backoff2' = 10) + (1 / 16) : (s2' = 5) & (backoff2' = 11) + (1 / 16) : (s2' = 5) & (backoff2' = 12) + (1 / 16) : (s2' = 5) & (backoff2' = 13) + (1 / 16) : (s2' = 5) & (backoff2' = 14) + (1 / 16) : (s2' = 5) & (backoff2' = 15); + [time] (((s2 = 5) & (x2 < 1)) & ((c2 = 0) & (c1 = 0))) -> 1 : (x2' = (min((x2 + 1), 7))); + [] (((s2 = 5) & (x2 = 1)) & (backoff2 > 0)) -> 1 : (x2' = 0) & (s2' = 5) & (backoff2' = (backoff2 - 1)); + [] ((((s2 = 5) & (x2 = 1)) & (backoff2 = 0)) & (slot2 > 0)) -> 1 : (x2' = 0) & (s2' = 5) & (slot2' = (slot2 - 1)) & (backoff2' = 15); + [] ((((s2 = 5) & (x2 = 1)) & (backoff2 = 0)) & (slot2 = 0)) -> 1 : (x2' = 0) & (s2' = 8); + [] ((s2 = 5) & ((c2 > 0) | (c1 > 0))) -> 1 : (x2' = 0) & (s2' = 6); + [time] ((s2 = 6) & ((c2 > 0) | (c1 > 0))) -> 1 : (s2' = 6); + [] ((s2 = 6) & ((c2 = 0) & (c1 = 0))) -> 1 : (s2' = 7); + [time] (((s2 = 7) & (x2 < 3)) & ((c2 = 0) & (c1 = 0))) -> 1 : (x2' = (min((x2 + 1), 7))); + [] ((s2 = 7) & ((x2 = 3) | (x2 = 2))) -> 1 : (x2' = 0) & (s2' = 5); + [] ((s2 = 7) & ((c2 > 0) | (c1 > 0))) -> 1 : (x2' = 0) & (s2' = 6); + [time] ((s2 = 8) & (x2 < 1)) -> 1 : (x2' = (min((x2 + 1), 7))); + [send2] ((s2 = 8) & ((x2 = 1) | (x2 = 0))) -> 1 : (x2' = 0) & (s2' = 9); + [time] ((s2 = 9) & (x2 < 4)) -> 1 : (x2' = (min((x2 + 1), 7))); + [finish2] (((s2 = 9) & (x2 >= 4)) & (c2 = 1)) -> 1 : (x2' = 0) & (s2' = 10); + [finish2] (((s2 = 9) & (x2 >= 4)) & (c2 = 2)) -> 1 : (x2' = 0) & (s2' = 11); + [] ((((s2 = 10) & (c2 = 0)) & (x2 = 0)) & ((c2 > 0) | (c1 > 0))) -> 1 : (s2' = 2); + [time] ((((s2 = 10) & (c2 = 0)) & (x2 = 0)) & ((c2 = 0) & (c1 = 0))) -> 1 : (x2' = (min((x2 + 1), 7))); + [send2] (((s2 = 10) & (c2 = 0)) & ((x2 = 1) | ((x2 = 0) & ((c2 = 0) & (c1 = 0))))) -> 1 : (x2' = 0) & (s2' = 10); + [time] (((s2 = 10) & (c2 = 1)) & (x2 < 4)) -> 1 : (x2' = (min((x2 + 1), 7))); + [finish2] (((s2 = 10) & (c2 = 1)) & ((x2 = 4) | (x2 = 3))) -> 1 : (x2' = 0) & (s2' = 12) & (bc2' = 0); + [] (((s2 = 11) & (x2 = 0)) & ((c2 > 0) | (c1 > 0))) -> 1 : (s2' = 2); + [time] (((s2 = 11) & (x2 = 0)) & ((c2 = 0) & (c1 = 0))) -> 1 : (x2' = (min((x2 + 1), 7))); + [time] (((s2 = 11) & (x2 > 0)) & (x2 < 6)) -> 1 : (x2' = (min((x2 + 1), 7))); + [] ((s2 = 11) & (x2 = 6)) -> 1 : (x2' = 0) & (s2' = 3); + [time] (s2 = 12) -> 1 : (s2' = 12); +endmodule + +label "twoCollisions" = (col = 2); \ No newline at end of file diff --git a/test/functional/utility/GraphTest.cpp b/test/functional/utility/GraphTest.cpp index 6ac99e129..c402b06f5 100644 --- a/test/functional/utility/GraphTest.cpp +++ b/test/functional/utility/GraphTest.cpp @@ -252,6 +252,152 @@ TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { EXPECT_TRUE(static_cast(result.player2Strategy)); } +TEST(GraphTest, SymbolicProb01StochasticGameWlan) { + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-4.nm"); + program = program.substituteConstants(); + program = program.flattenModules(std::make_unique()); + + std::vector initialPredicates; + storm::expressions::ExpressionManager& manager = program.getManager(); + + initialPredicates.push_back(manager.getVariableExpression("col") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("col") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("col") == manager.integer(2)); + + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("c1") == manager.integer(2)); + + initialPredicates.push_back(manager.getVariableExpression("c2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("c2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("c2") == manager.integer(2)); + + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("x1") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("s1") == manager.integer(12)); + + initialPredicates.push_back(manager.getVariableExpression("slot1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("slot1") == manager.integer(1)); + + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(12)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(13)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(14)); + initialPredicates.push_back(manager.getVariableExpression("backoff1") == manager.integer(15)); + + initialPredicates.push_back(manager.getVariableExpression("bc1") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("bc1") == manager.integer(1)); + + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("x2") == manager.integer(7)); + + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("s2") == manager.integer(12)); + + initialPredicates.push_back(manager.getVariableExpression("slot2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("slot2") == manager.integer(1)); + + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(1)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(2)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(3)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(4)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(5)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(6)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(7)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(8)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(9)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(10)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(11)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(12)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(13)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(14)); + initialPredicates.push_back(manager.getVariableExpression("backoff2") == manager.integer(15)); + + initialPredicates.push_back(manager.getVariableExpression("bc2") == manager.integer(0)); + initialPredicates.push_back(manager.getVariableExpression("bc2") == manager.integer(1)); + + storm::prism::menu_games::AbstractProgram abstractProgram(program.getManager(), program, initialPredicates, std::make_unique(), false); + + storm::prism::menu_games::MenuGame game = abstractProgram.getAbstractGame(); + + // The target states are those states where col == 2. + storm::dd::Bdd targetStates = game.getStates(initialPredicates[2], false); + + storm::utility::graph::GameProb01Result result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(2831, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(2692, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(2831, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(2692, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(2064, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Minimize, true); + EXPECT_EQ(2884, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(2064, result.states.getNonZeroCount()); + + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Maximize, storm::OptimizationDirection::Maximize, true); + EXPECT_EQ(2884, result.states.getNonZeroCount()); + EXPECT_TRUE(static_cast(result.player1Strategy)); + EXPECT_TRUE(static_cast(result.player2Strategy)); +} + #endif TEST(GraphTest, ExplicitProb01) { From 781610b05d2f6f7cd3f58394f50f534f8d0cb632 Mon Sep 17 00:00:00 2001 From: dehnert Date: Wed, 23 Sep 2015 11:23:02 +0200 Subject: [PATCH 024/400] extended tests for validity of returned strategies Former-commit-id: fb6a1c23f0b3318fa366321326a0ab993ac69a28 --- src/storage/dd/CuddDdManager.cpp | 4 ++ src/storage/dd/CuddDdManager.h | 9 +++- src/utility/graph.h | 2 +- test/functional/utility/GraphTest.cpp | 77 +++++++++++++++++++++++++-- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index 375c255d8..622ecbb44 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -331,5 +331,9 @@ namespace storm { std::shared_ptr const> DdManager::asSharedPointer() const { return this->shared_from_this(); } + + std::shared_ptr> DdManager::asSharedPointer() { + return this->shared_from_this(); + } } } \ No newline at end of file diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h index b6e5d836e..411a64b55 100644 --- a/src/storage/dd/CuddDdManager.h +++ b/src/storage/dd/CuddDdManager.h @@ -186,7 +186,14 @@ namespace storm { * @return A shared pointer to the manager. */ std::shared_ptr const> asSharedPointer() const; - + + /*! + * Retrieves the manager as a shared pointer. + * + * @return A shared pointer to the manager. + */ + std::shared_ptr> asSharedPointer(); + private: /*! * Retrieves a list of names of the DD variables in the order of their index. diff --git a/src/utility/graph.h b/src/utility/graph.h index 7475d0570..a4f083aed 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -175,7 +175,7 @@ namespace storm { * @return All states with probability 1. */ template - storm::dd::Bdd performProb1(storm::models::symbolic::Model const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) ; + storm::dd::Bdd performProb1(storm::models::symbolic::Model const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); /*! * Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi in a diff --git a/test/functional/utility/GraphTest.cpp b/test/functional/utility/GraphTest.cpp index c402b06f5..7d8e3fc95 100644 --- a/test/functional/utility/GraphTest.cpp +++ b/test/functional/utility/GraphTest.cpp @@ -149,7 +149,19 @@ TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { EXPECT_EQ(0, result.states.getNonZeroCount()); EXPECT_TRUE(static_cast(result.player1Strategy)); EXPECT_TRUE(static_cast(result.player2Strategy)); - + + // Check the validity of the strategies. Start by checking whether only prob0 states have a strategy. + storm::dd::Bdd nonProb0StatesWithStrategy = !result.states && result.player1Strategy.get(); + EXPECT_TRUE(nonProb0StatesWithStrategy.isZero()); + + // Proceed by checking whether they select exactly one action in each state. + storm::dd::Add stateDistributionsUnderStrategies = (game.getTransitionMatrix() * result.player1Strategy.get().toAdd() * result.player2Strategy.get().toAdd()).sumAbstract(game.getColumnVariables());; + EXPECT_EQ(0, stateDistributionsUnderStrategies.getNonZeroCount()); + + // Check that the number of distributions per state is one (or zero in the case where there are no prob0 states). + storm::dd::Add stateDistributionCount = stateDistributionsUnderStrategies.sumAbstract(game.getNondeterminismVariables()); + EXPECT_EQ(0, stateDistributionCount.getMax()); + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(3, result.states.getNonZeroCount()); @@ -172,6 +184,18 @@ TEST(GraphTest, SymbolicProb01StochasticGameDieSmall) { EXPECT_EQ(3, result.states.getNonZeroCount()); EXPECT_TRUE(static_cast(result.player1Strategy)); EXPECT_TRUE(static_cast(result.player2Strategy)); + + // Check the validity of the strategies. Start by checking whether only prob1 states have a strategy. + storm::dd::Bdd nonProb1StatesWithStrategy = !result.states && result.player1Strategy.get(); + EXPECT_TRUE(nonProb1StatesWithStrategy.isZero()); + + // Proceed by checking whether they select exactly one action in each state. + stateDistributionsUnderStrategies = (game.getTransitionMatrix() * result.player1Strategy.get().toAdd() * result.player2Strategy.get().toAdd()).sumAbstract(game.getColumnVariables()); + EXPECT_EQ(3, stateDistributionsUnderStrategies.getNonZeroCount()); + + // Check that the number of distributions per state is one (or zero in the case where there are no prob1 states). + stateDistributionCount = stateDistributionsUnderStrategies.sumAbstract(game.getNondeterminismVariables()); + EXPECT_EQ(1, stateDistributionCount.getMax()); } TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { @@ -228,6 +252,17 @@ TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { EXPECT_TRUE(static_cast(result.player1Strategy)); EXPECT_TRUE(static_cast(result.player2Strategy)); + // Check the validity of the strategies. Start by checking whether only prob0 states have a strategy. + storm::dd::Bdd nonProb0StatesWithStrategy = !result.states && result.player1Strategy.get(); + EXPECT_TRUE(nonProb0StatesWithStrategy.isZero()); + + // Proceed by checking whether they select exactly one exaction in each state. + storm::dd::Add stateDistributionsUnderStrategies = (game.getTransitionMatrix() * result.player1Strategy.get().toAdd() * result.player2Strategy.get().toAdd()).sumAbstract(game.getColumnVariables()); + EXPECT_EQ(153, stateDistributionsUnderStrategies.getNonZeroCount()); + + storm::dd::Add stateDistributionCount = stateDistributionsUnderStrategies.sumAbstract(game.getNondeterminismVariables()); + EXPECT_EQ(1, stateDistributionCount.getMax()); + result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(1, result.states.getNonZeroCount()); @@ -250,6 +285,18 @@ TEST(GraphTest, SymbolicProb01StochasticGameTwoDice) { EXPECT_EQ(1, result.states.getNonZeroCount()); EXPECT_TRUE(static_cast(result.player1Strategy)); EXPECT_TRUE(static_cast(result.player2Strategy)); + + // Check the validity of the strategies. Start by checking whether only prob1 states have a strategy. + storm::dd::Bdd nonProb1StatesWithStrategy = !result.states && result.player1Strategy.get(); + EXPECT_TRUE(nonProb1StatesWithStrategy.isZero()); + + // Proceed by checking whether they select exactly one action in each state. + stateDistributionsUnderStrategies = (game.getTransitionMatrix() * result.player1Strategy.get().toAdd() * result.player2Strategy.get().toAdd()).sumAbstract(game.getColumnVariables()); + EXPECT_EQ(1, stateDistributionsUnderStrategies.getNonZeroCount()); + + // Check that the number of distributions per state is one (or zero in the case where there are no prob1 states). + stateDistributionCount = stateDistributionsUnderStrategies.sumAbstract(game.getNondeterminismVariables()); + EXPECT_EQ(1, stateDistributionCount.getMax()); } TEST(GraphTest, SymbolicProb01StochasticGameWlan) { @@ -371,8 +418,20 @@ TEST(GraphTest, SymbolicProb01StochasticGameWlan) { storm::utility::graph::GameProb01Result result = storm::utility::graph::performProb0(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(2831, result.states.getNonZeroCount()); - EXPECT_TRUE(static_cast(result.player1Strategy)); - EXPECT_TRUE(static_cast(result.player2Strategy)); + ASSERT_TRUE(static_cast(result.player1Strategy)); + ASSERT_TRUE(static_cast(result.player2Strategy)); + + // Check the validity of the strategies. Start by checking whether only prob0 states have a strategy. + storm::dd::Bdd nonProb0StatesWithStrategy = !result.states && result.player1Strategy.get(); + EXPECT_TRUE(nonProb0StatesWithStrategy.isZero()); + + // Proceed by checking whether they select exactly one action in each state. + storm::dd::Add stateDistributionsUnderStrategies = (game.getTransitionMatrix() * result.player1Strategy.get().toAdd() * result.player2Strategy.get().toAdd()).sumAbstract(game.getColumnVariables());; + EXPECT_EQ(2831, stateDistributionsUnderStrategies.getNonZeroCount()); + + // Check that the number of distributions per state is one (or zero in the case where there are no prob0 states). + storm::dd::Add stateDistributionCount = stateDistributionsUnderStrategies.sumAbstract(game.getNondeterminismVariables()); + EXPECT_EQ(1, stateDistributionCount.getMax()); result = storm::utility::graph::performProb1(game, game.getQualitativeTransitionMatrix(), game.getReachableStates(), targetStates, storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Minimize, true); EXPECT_EQ(2692, result.states.getNonZeroCount()); @@ -396,6 +455,18 @@ TEST(GraphTest, SymbolicProb01StochasticGameWlan) { EXPECT_EQ(2884, result.states.getNonZeroCount()); EXPECT_TRUE(static_cast(result.player1Strategy)); EXPECT_TRUE(static_cast(result.player2Strategy)); + + // Check the validity of the strategies. Start by checking whether only prob1 states have a strategy. + storm::dd::Bdd nonProb1StatesWithStrategy = !result.states && result.player1Strategy.get(); + EXPECT_TRUE(nonProb1StatesWithStrategy.isZero()); + + // Proceed by checking whether they select exactly one action in each state. + stateDistributionsUnderStrategies = (game.getTransitionMatrix() * result.player1Strategy.get().toAdd() * result.player2Strategy.get().toAdd()).sumAbstract(game.getColumnVariables()); + EXPECT_EQ(2884, stateDistributionsUnderStrategies.getNonZeroCount()); + + // Check that the number of distributions per state is one (or zero in the case where there are no prob1 states). + stateDistributionCount = stateDistributionsUnderStrategies.sumAbstract(game.getNondeterminismVariables()); + EXPECT_EQ(1, stateDistributionCount.getMax()); } #endif From 8574d474a436eb8a1efe0efd09939220b0c4bf17 Mon Sep 17 00:00:00 2001 From: dehnert Date: Wed, 23 Sep 2015 17:11:43 +0200 Subject: [PATCH 025/400] added support for computation of bottom states. not yet done Former-commit-id: 49c2a28b2862f574d549d6623ec03c4964967920 --- .../prism/menu_games/AbstractProgram.cpp | 25 ++++++++++++++++--- .../prism/menu_games/AbstractProgram.h | 6 +++++ src/storage/prism/menu_games/MenuGame.cpp | 8 +++++- src/storage/prism/menu_games/MenuGame.h | 16 ++++++++++-- .../prism/menu_games/StateSetAbstractor.cpp | 19 +++++++++----- .../prism/menu_games/StateSetAbstractor.h | 5 +++- 6 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/storage/prism/menu_games/AbstractProgram.cpp b/src/storage/prism/menu_games/AbstractProgram.cpp index 53cafc69e..3105e82a5 100644 --- a/src/storage/prism/menu_games/AbstractProgram.cpp +++ b/src/storage/prism/menu_games/AbstractProgram.cpp @@ -17,7 +17,7 @@ namespace storm { namespace menu_games { template - AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), currentGame(nullptr) { + AbstractProgram::AbstractProgram(storm::expressions::ExpressionManager& expressionManager, storm::prism::Program const& program, std::vector const& initialPredicates, std::unique_ptr&& smtSolverFactory, bool addAllGuards) : smtSolverFactory(std::move(smtSolverFactory)), ddInformation(std::make_shared>()), expressionInformation(expressionManager, initialPredicates, program.getAllExpressionVariables(), program.getAllRangeExpressions()), modules(), program(program), initialStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), addedAllGuards(addAllGuards), bottomStateAbstractor(expressionInformation, ddInformation, *this->smtSolverFactory), currentGame(nullptr) { // For now, we assume that there is a single module. If the program has more than one module, it needs // to be flattened before the procedure. @@ -30,6 +30,10 @@ namespace storm { for (auto const& command : module.getCommands()) { if (addAllGuards) { expressionInformation.predicates.push_back(command.getGuardExpression()); + } else { + // If not all guards were added, we also need to populate the bottom state abstractor. + std::cout << "adding " << !command.getGuardExpression() << std::endl; + bottomStateAbstractor.addPredicate(!command.getGuardExpression()); } maximalUpdateCount = std::max(maximalUpdateCount, static_cast(command.getNumberOfUpdates())); } @@ -100,7 +104,10 @@ namespace storm { // Refine initial state abstractor. initialStateAbstractor.refine(newPredicateIndices); - // Finally, we rebuild the game. + // Refine bottom state abstractor. + bottomStateAbstractor.refine(newPredicateIndices); + + // Finally, we rebuild the game.. currentGame = buildGame(); } @@ -122,7 +129,7 @@ namespace storm { } STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The given predicate is illegal, since it was neither used as an initial predicate nor used to refine the abstraction."); } - + template std::unique_ptr> AbstractProgram::buildGame() { // As long as there is only one module, we only build its game representation. @@ -138,6 +145,16 @@ namespace storm { storm::dd::Bdd transitionRelation = gameBdd.first.existsAbstract(variablesToAbstract); storm::dd::Bdd initialStates = initialStateAbstractor.getAbstractStates(); storm::dd::Bdd reachableStates = this->getReachableStates(initialStates, transitionRelation); + + // Determine the bottom states. + storm::dd::Bdd bottomStates; + if (addedAllGuards) { + bottomStates = ddInformation.manager->getBddZero(); + } else { + bottomStates = bottomStateAbstractor.getAbstractStates(); + } + + std::cout << "found " << (reachableStates && bottomStates).getNonZeroCount() << " reachable bottom states" << std::endl; // Find the deadlock states in the model. storm::dd::Bdd deadlockStates = transitionRelation.existsAbstract(ddInformation.successorVariables); @@ -160,7 +177,7 @@ namespace storm { std::set allNondeterminismVariables = usedPlayer2Variables; allNondeterminismVariables.insert(ddInformation.commandDdVariable); - return std::unique_ptr>(new MenuGame(ddInformation.manager, reachableStates, initialStates, transitionMatrix, ddInformation.sourceVariables, ddInformation.successorVariables, ddInformation.predicateDdVariables, {ddInformation.commandDdVariable}, usedPlayer2Variables, allNondeterminismVariables, ddInformation.updateDdVariable, ddInformation.expressionToBddMap)); + return std::unique_ptr>(new MenuGame(ddInformation.manager, reachableStates, initialStates, transitionMatrix, bottomStates, ddInformation.sourceVariables, ddInformation.successorVariables, ddInformation.predicateDdVariables, {ddInformation.commandDdVariable}, usedPlayer2Variables, allNondeterminismVariables, ddInformation.updateDdVariable, ddInformation.expressionToBddMap)); } template diff --git a/src/storage/prism/menu_games/AbstractProgram.h b/src/storage/prism/menu_games/AbstractProgram.h index 807d5f17e..996963ee0 100644 --- a/src/storage/prism/menu_games/AbstractProgram.h +++ b/src/storage/prism/menu_games/AbstractProgram.h @@ -104,6 +104,12 @@ namespace storm { // A state-set abstractor used to determine the initial states of the abstraction. StateSetAbstractor initialStateAbstractor; + // A flag that stores whether all guards were added (which is relevant for determining the bottom states). + bool addedAllGuards; + + // A state-set abstractor used to determine the bottom states if not all guards were added. + StateSetAbstractor bottomStateAbstractor; + // An ADD characterizing the probabilities of commands and their updates. storm::dd::Add commandUpdateProbabilitiesAdd; diff --git a/src/storage/prism/menu_games/MenuGame.cpp b/src/storage/prism/menu_games/MenuGame.cpp index 22f25e251..fb9269c5a 100644 --- a/src/storage/prism/menu_games/MenuGame.cpp +++ b/src/storage/prism/menu_games/MenuGame.cpp @@ -17,6 +17,7 @@ namespace storm { storm::dd::Bdd reachableStates, storm::dd::Bdd initialStates, storm::dd::Add transitionMatrix, + storm::dd::Bdd bottomStates, std::set const& rowVariables, std::set const& columnVariables, std::vector> const& rowColumnMetaVariablePairs, @@ -24,7 +25,7 @@ namespace storm { std::set const& player2Variables, std::set const& allNondeterminismVariables, storm::expressions::Variable const& updateVariable, - std::map> const& expressionToBddMap) : storm::models::symbolic::StochasticTwoPlayerGame(manager, reachableStates, initialStates, transitionMatrix.sumAbstract({updateVariable}), rowVariables, nullptr, columnVariables, nullptr, rowColumnMetaVariablePairs, player1Variables, player2Variables, allNondeterminismVariables), updateVariable(updateVariable), expressionToBddMap(expressionToBddMap) { + std::map> const& expressionToBddMap) : storm::models::symbolic::StochasticTwoPlayerGame(manager, reachableStates, initialStates, transitionMatrix.sumAbstract({updateVariable}), rowVariables, nullptr, columnVariables, nullptr, rowColumnMetaVariablePairs, player1Variables, player2Variables, allNondeterminismVariables), updateVariable(updateVariable), expressionToBddMap(expressionToBddMap), bottomStates(bottomStates) { // Intentionally left empty. } @@ -49,6 +50,11 @@ namespace storm { } } + template + storm::dd::Bdd MenuGame::getBottomStates() const { + return bottomStates; + } + template bool MenuGame::hasLabel(std::string const& label) const { return false; diff --git a/src/storage/prism/menu_games/MenuGame.h b/src/storage/prism/menu_games/MenuGame.h index 854dbe5f6..44d3c6e25 100644 --- a/src/storage/prism/menu_games/MenuGame.h +++ b/src/storage/prism/menu_games/MenuGame.h @@ -31,9 +31,10 @@ namespace storm { * Constructs a model from the given data. * * @param manager The manager responsible for the decision diagrams. - * @param reachableStates A DD representing the reachable states. - * @param initialStates A DD representing the initial states of the model. + * @param reachableStates The reachable states of the model. + * @param initialStates The initial states of the model. * @param transitionMatrix The matrix representing the transitions in the model. + * @param bottomStates The bottom states of the model. * @param rowVariables The set of row meta variables used in the DDs. * @param columVariables The set of column meta variables used in the DDs. * @param rowColumnMetaVariablePairs All pairs of row/column meta variables. @@ -48,6 +49,7 @@ namespace storm { storm::dd::Bdd reachableStates, storm::dd::Bdd initialStates, storm::dd::Add transitionMatrix, + storm::dd::Bdd bottomStates, std::set const& rowVariables, std::set const& columnVariables, std::vector> const& rowColumnMetaVariablePairs, @@ -78,6 +80,13 @@ namespace storm { */ storm::dd::Bdd getStates(storm::expressions::Expression const& expression, bool negated) const; + /*! + * Retrieves the bottom states of the model. + * + * @return The bottom states of the model. + */ + storm::dd::Bdd getBottomStates() const; + virtual bool hasLabel(std::string const& label) const override; private: @@ -86,6 +95,9 @@ namespace storm { // A mapping from expressions that were used in the abstraction process to the the BDDs representing them. std::map> expressionToBddMap; + + // The bottom states of the model. + storm::dd::Bdd bottomStates; }; } // namespace menu_games diff --git a/src/storage/prism/menu_games/StateSetAbstractor.cpp b/src/storage/prism/menu_games/StateSetAbstractor.cpp index da5219043..a1f7d8175 100644 --- a/src/storage/prism/menu_games/StateSetAbstractor.cpp +++ b/src/storage/prism/menu_games/StateSetAbstractor.cpp @@ -13,7 +13,7 @@ namespace storm { namespace menu_games { template - StateSetAbstractor::StateSetAbstractor(AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.manager)), expressionInformation(expressionInformation), ddInformation(ddInformation), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), concretePredicateVariables(), cachedBdd(ddInformation.manager->getBddZero()) { + StateSetAbstractor::StateSetAbstractor(AbstractionExpressionInformation const& expressionInformation, AbstractionDdInformation const& ddInformation, storm::utility::solver::SmtSolverFactory const& smtSolverFactory) : smtSolver(smtSolverFactory.create(expressionInformation.manager)), expressionInformation(expressionInformation), ddInformation(ddInformation), variablePartition(expressionInformation.variables), relevantPredicatesAndVariables(), concretePredicateVariables(), needsRecomputation(false), cachedBdd(ddInformation.manager->getBddZero()) { // Assert all range expressions to enforce legal variable values. for (auto const& rangeExpression : expressionInformation.rangeExpressions) { @@ -36,9 +36,9 @@ namespace storm { std::set usedVariables = predicate.getVariables(); concretePredicateVariables.insert(usedVariables.begin(), usedVariables.end()); variablePartition.relate(usedVariables); - - // Since the new predicate might have changed the abstractions, we need to recompute it. - this->refine(); + + // Remember that we have to recompute the BDD. + this->needsRecomputation = true; } template @@ -82,11 +82,14 @@ namespace storm { template storm::dd::Bdd StateSetAbstractor::getStateBdd(storm::solver::SmtSolver::ModelReference const& model) const { STORM_LOG_TRACE("Building source state BDD."); + std::cout << "new model: " << std::endl; storm::dd::Bdd result = ddInformation.manager->getBddOne(); for (auto const& variableIndexPair : relevantPredicatesAndVariables) { if (model.getBooleanValue(variableIndexPair.first)) { + std::cout << expressionInformation.predicates[variableIndexPair.second] << " is true" << std::endl; result &= ddInformation.predicateBdds[variableIndexPair.second].first; } else { + std::cout << expressionInformation.predicates[variableIndexPair.second] << " is false" << std::endl; result &= !ddInformation.predicateBdds[variableIndexPair.second].first; } } @@ -98,13 +101,17 @@ namespace storm { STORM_LOG_TRACE("Recomputing BDD for state set abstraction."); storm::dd::Bdd result = ddInformation.manager->getBddZero(); - smtSolver->allSat(decisionVariables, [&result,this] (storm::solver::SmtSolver::ModelReference const& model) { result |= getStateBdd(model); return true; } ); + uint_fast64_t modelCounter = 0; + smtSolver->allSat(decisionVariables, [&result,this,&modelCounter] (storm::solver::SmtSolver::ModelReference const& model) { result |= getStateBdd(model); ++modelCounter; std::cout << "found " << modelCounter << " models" << std::endl; return modelCounter < 10000 ? true : false; } ); cachedBdd = result; } template - storm::dd::Bdd StateSetAbstractor::getAbstractStates() const { + storm::dd::Bdd StateSetAbstractor::getAbstractStates() { + if (needsRecomputation) { + this->refine(); + } return cachedBdd; } diff --git a/src/storage/prism/menu_games/StateSetAbstractor.h b/src/storage/prism/menu_games/StateSetAbstractor.h index 7e5b17178..b6703022d 100644 --- a/src/storage/prism/menu_games/StateSetAbstractor.h +++ b/src/storage/prism/menu_games/StateSetAbstractor.h @@ -62,7 +62,7 @@ namespace storm { * * @return The set of matching abstract states in the form of a BDD */ - storm::dd::Bdd getAbstractStates() const; + storm::dd::Bdd getAbstractStates(); private: /*! @@ -106,6 +106,9 @@ namespace storm { // The set of all decision variables over which to perform the all-sat enumeration. std::vector decisionVariables; + // A flag indicating whether the cached BDD needs recomputation. + bool needsRecomputation; + // The cached BDD representing the abstraction. This variable is written to in refinement steps (if work // needed to be done). storm::dd::Bdd cachedBdd; From 4b24be7204a9cb5c4096390e7bc9379f11f09b40 Mon Sep 17 00:00:00 2001 From: dehnert Date: Wed, 23 Sep 2015 19:27:32 +0200 Subject: [PATCH 026/400] commit to switch workplace Former-commit-id: c50e423557b14078feaed04e2921274a5bfc731d --- src/storage/dd/CuddBdd.cpp | 24 ++++++++++++++++++++++++ src/storage/dd/CuddBdd.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp index 476cd0880..98d50132a 100644 --- a/src/storage/dd/CuddBdd.cpp +++ b/src/storage/dd/CuddBdd.cpp @@ -367,6 +367,30 @@ namespace storm { return result; } + std::pair, std::unordered_map, storm::expressions::Variable>> Bdd::toExpression(storm::expressions::ExpressionManager& manager, std::unordered_map const& indexToExpressionMap) const { + std::pair, std::unordered_map, storm::expressions::Variable>> result; + + // Create (and maintain) a mapping from the DD nodes to a counter that says the how-many-th node (within the + // nodes of equal index) the node was. + std::unordered_map nodeToCounterMap; + this->toExpressionRec(this->getCuddDdNode(), this->getDdManager()->getCuddManager(), manager, result.first, result.second, nodeToCounterMap); + + return result; + } + + void Bdd::toExpressionRec(DdNode const* f, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap) const { + DdNode const* F = Cudd_Regular(f); + + // Terminal cases. + if (F == Cudd_ReadOne(ddManager.getManager())) { + + } + + // Non-terminal cases. + // (1) Check if we have seen the node before. + auto nodeIt = nodeToCounterMap.find(); + } + void Bdd::toVectorRec(DdNode const* dd, Cudd const& manager, storm::storage::BitVector& result, Odd const& rowOdd, bool complement, uint_fast64_t currentRowLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, std::vector const& ddRowVariableIndices) const { // If there are no more values to select, we can directly return. if (dd == Cudd_ReadLogicZero(manager.getManager()) && !complement) { diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h index 6ad869428..a8e434958 100644 --- a/src/storage/dd/CuddBdd.h +++ b/src/storage/dd/CuddBdd.h @@ -1,6 +1,7 @@ #ifndef STORM_STORAGE_DD_CUDDBDD_H_ #define STORM_STORAGE_DD_CUDDBDD_H_ +#include #include #include "src/storage/dd/Bdd.h" @@ -303,6 +304,17 @@ namespace storm { */ storm::storage::BitVector toVector(storm::dd::Odd const& rowOdd) const; + /*! + * Translates the function the BDD is representing to a set of expressions that characterize the function. + * + * @param manager The manager that is used to build the expression and, in particular, create new variables in. + * @param indexToExpressionMap A mapping from indices (of DD variables) to expressions with which they are + * to be replaced. + * @return A pair consisting of the created expressions and a mapping from pairs (i, j) to variables such + * that the i-th variable of level j is represented by the mapped-to variable. + */ + std::pair, std::unordered_map, storm::expressions::Variable>> toExpression(storm::expressions::ExpressionManager& manager, std::unordered_map const& indexToExpressionMap) const; + private: /*! * Retrieves the CUDD BDD object associated with this DD. @@ -370,6 +382,8 @@ namespace storm { */ void toVectorRec(DdNode const* dd, Cudd const& manager, storm::storage::BitVector& result, Odd const& rowOdd, bool complement, uint_fast64_t currentRowLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, std::vector const& ddRowVariableIndices) const; + void toExpressionRec(DdNode const* dd, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap) const; + // The BDD created by CUDD. BDD cuddBdd; }; From 381fe6d9a866df57331a048e5c7353cfc4ea0c89 Mon Sep 17 00:00:00 2001 From: dehnert Date: Wed, 23 Sep 2015 21:01:42 +0200 Subject: [PATCH 027/400] more work on translating BDDs to expressions Former-commit-id: 0f361f76f59d33ad58f265fa6dd2168225c713b5 --- src/storage/PairHash.h | 17 +++++++ src/storage/dd/CuddBdd.cpp | 69 ++++++++++++++++++++++---- src/storage/dd/CuddBdd.h | 3 +- src/storage/dd/CuddDdManager.cpp | 8 ++- src/storage/dd/CuddDdManager.h | 10 ++++ test/functional/storage/CuddDdTest.cpp | 26 ++++++++++ 6 files changed, 122 insertions(+), 11 deletions(-) create mode 100644 src/storage/PairHash.h diff --git a/src/storage/PairHash.h b/src/storage/PairHash.h new file mode 100644 index 000000000..dc7b7e7f2 --- /dev/null +++ b/src/storage/PairHash.h @@ -0,0 +1,17 @@ +#ifndef STORM_STORAGE_PAIRHASH_H_ +#define STORM_STORAGE_PAIRHASH_H_ + +namespace std { + template <> + struct hash> + { + std::size_t operator()(std::pair const& key) const { + std::size_t seed = 0; + boost::hash_combine(seed, key.first); + boost::hash_combine(seed, key.second); + return seed; + } + }; +} + +#endif /* STORM_STORAGE_PAIRHASH_H_ */ diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp index 98d50132a..fc673e0f2 100644 --- a/src/storage/dd/CuddBdd.cpp +++ b/src/storage/dd/CuddBdd.cpp @@ -7,6 +7,9 @@ #include "src/storage/dd/CuddOdd.h" #include "src/storage/dd/CuddDdManager.h" +#include "src/storage/expressions/ExpressionManager.h" +#include "src/storage/expressions/Expression.h" + #include "src/storage/BitVector.h" #include "src/logic/ComparisonType.h" @@ -372,23 +375,71 @@ namespace storm { // Create (and maintain) a mapping from the DD nodes to a counter that says the how-many-th node (within the // nodes of equal index) the node was. - std::unordered_map nodeToCounterMap; - this->toExpressionRec(this->getCuddDdNode(), this->getDdManager()->getCuddManager(), manager, result.first, result.second, nodeToCounterMap); + std::unordered_map nodeToCounterMap; + std::vector nextCounterForIndex(this->getDdManager()->getNumberOfDdVariables(), 0); + bool negated = Cudd_Regular(this->getCuddDdNode()) != this->getCuddDdNode(); + + // Translate from the top node downwards. + storm::expressions::Variable topVariable = this->toExpressionRec(Cudd_Regular(this->getCuddDdNode()), this->getDdManager()->getCuddManager(), manager, result.first, result.second, nodeToCounterMap, nextCounterForIndex, indexToExpressionMap); + + // Create the final expression. + if (negated) { + result.first.push_back(!topVariable); + } else { + result.first.push_back(topVariable); + } return result; } - void Bdd::toExpressionRec(DdNode const* f, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap) const { - DdNode const* F = Cudd_Regular(f); + storm::expressions::Variable Bdd::toExpressionRec(DdNode const* dd, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap, std::vector& nextCounterForIndex, std::unordered_map const& indexToExpressionMap) const { + STORM_LOG_ASSERT(dd == Cudd_Regular(dd), "Expected non-negated BDD node."); + + // First, try to look up the current node. + auto nodeCounterIt = nodeToCounterMap.find(dd); + if (nodeCounterIt != nodeToCounterMap.end()) { + // If we have found the node, this means we can look up the counter-index pair and get the corresponding variable. + auto variableIt = countIndexToVariablePair.find(std::make_pair(nodeCounterIt->second, dd->index)); + STORM_LOG_ASSERT(variableIt != countIndexToVariablePair.end(), "Unable to find node."); + return variableIt->second; + } + + // If the node was not yet encountered, we create a variable and associate it with the appropriate expression. + storm::expressions::Variable newVariable = manager.declareFreshBooleanVariable(); + + // Since we want to reuse the variable whenever possible, we insert the appropriate entries in the hash table. + if (!Cudd_IsConstant(dd)) { + // If we are dealing with a non-terminal node, we count it as a new node with this index. + nodeToCounterMap[dd] = nextCounterForIndex[dd->index]; + countIndexToVariablePair[std::make_pair(nextCounterForIndex[dd->index], dd->index)] = newVariable; + ++nextCounterForIndex[dd->index]; + } else { + // If it's a terminal node, it is the one leaf and there's no need to keep track of a counter for this level. + nodeToCounterMap[dd] = 0; + countIndexToVariablePair[std::make_pair(0, dd->index)] = newVariable; + } - // Terminal cases. - if (F == Cudd_ReadOne(ddManager.getManager())) { + // In the terminal case, we can only have a one since we are considering non-negated nodes only. + if (dd == Cudd_ReadOne(ddManager.getManager())) { + // Push the expression that enforces that the new variable is true. + expressions.push_back(storm::expressions::implies(manager.boolean(true), newVariable)); + } else { + // In the non-terminal case, we recursively translate the children nodes and then construct and appropriate ite-expression. + DdNode const* t = Cudd_T(dd); + DdNode const* e = Cudd_E(dd); + DdNode const* T = Cudd_Regular(t); + DdNode const* E = Cudd_Regular(e); + storm::expressions::Variable thenVariable = toExpressionRec(T, ddManager, manager, expressions, countIndexToVariablePair, nodeToCounterMap, nextCounterForIndex, indexToExpressionMap); + storm::expressions::Variable elseVariable = toExpressionRec(E, ddManager, manager, expressions, countIndexToVariablePair, nodeToCounterMap, nextCounterForIndex, indexToExpressionMap); + // Create the appropriate expression. + auto expressionIt = indexToExpressionMap.find(dd->index); + STORM_LOG_ASSERT(expressionIt != indexToExpressionMap.end(), "Unable to find expression for variable index."); + expressions.push_back(storm::expressions::iff(newVariable, storm::expressions::ite(expressionIt->second, t == T ? thenVariable : !thenVariable, e == E ? elseVariable : !elseVariable))); } - // Non-terminal cases. - // (1) Check if we have seen the node before. - auto nodeIt = nodeToCounterMap.find(); + // Return the variable for this node. + return newVariable; } void Bdd::toVectorRec(DdNode const* dd, Cudd const& manager, storm::storage::BitVector& result, Odd const& rowOdd, bool complement, uint_fast64_t currentRowLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, std::vector const& ddRowVariableIndices) const { diff --git a/src/storage/dd/CuddBdd.h b/src/storage/dd/CuddBdd.h index a8e434958..91019d79f 100644 --- a/src/storage/dd/CuddBdd.h +++ b/src/storage/dd/CuddBdd.h @@ -6,6 +6,7 @@ #include "src/storage/dd/Bdd.h" #include "src/storage/dd/CuddDd.h" +#include "src/storage/PairHash.h" #include "src/utility/OsDetection.h" // Include the C++-interface of CUDD. @@ -382,7 +383,7 @@ namespace storm { */ void toVectorRec(DdNode const* dd, Cudd const& manager, storm::storage::BitVector& result, Odd const& rowOdd, bool complement, uint_fast64_t currentRowLevel, uint_fast64_t maxLevel, uint_fast64_t currentRowOffset, std::vector const& ddRowVariableIndices) const; - void toExpressionRec(DdNode const* dd, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap) const; + storm::expressions::Variable toExpressionRec(DdNode const* dd, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap, std::vector& nextCounterForIndex, std::unordered_map const& indexToExpressionMap) const; // The BDD created by CUDD. BDD cuddBdd; diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index 622ecbb44..c782033ae 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -16,7 +16,7 @@ namespace storm { namespace dd { - DdManager::DdManager() : cuddManager(), metaVariableMap(), reorderingTechnique(CUDD_REORDER_NONE), manager(new storm::expressions::ExpressionManager()) { + DdManager::DdManager() : cuddManager(), metaVariableMap(), reorderingTechnique(CUDD_REORDER_NONE), manager(new storm::expressions::ExpressionManager()), numberOfDdVariables(0) { this->cuddManager.SetMaxMemory(static_cast(storm::settings::cuddSettings().getMaximalMemory() * 1024ul * 1024ul)); this->cuddManager.SetEpsilon(storm::settings::cuddSettings().getConstantPrecision()); @@ -160,6 +160,7 @@ namespace storm { variablesPrime.emplace_back(Bdd(this->shared_from_this(), cuddManager.bddVar(), {primed})); } } + numberOfDdVariables += numberOfBits; // Now group the non-primed and primed variable. for (uint_fast64_t i = 0; i < numberOfBits; ++i) { @@ -191,6 +192,7 @@ namespace storm { ++level.get(); } } + numberOfDdVariables += 2; storm::expressions::Variable unprimed = manager->declareBooleanVariable(name); storm::expressions::Variable primed = manager->declareBooleanVariable(name + "'"); @@ -235,6 +237,10 @@ namespace storm { return this->metaVariableMap.size(); } + std::size_t DdManager::getNumberOfDdVariables() const { + return numberOfDdVariables; + } + bool DdManager::hasMetaVariable(std::string const& metaVariableName) const { return manager->hasVariable(metaVariableName); } diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h index 411a64b55..fe267c423 100644 --- a/src/storage/dd/CuddDdManager.h +++ b/src/storage/dd/CuddDdManager.h @@ -145,6 +145,13 @@ namespace storm { */ std::size_t getNumberOfMetaVariables() const; + /*! + * Retrieves the number of DD variables that are contained in this manager. + * + * @return The number of DD variables contained in this manager. + */ + std::size_t getNumberOfDdVariables() const; + /*! * Retrieves whether the given meta variable name is already in use. * @@ -250,6 +257,9 @@ namespace storm { // The manager responsible for the variables. std::shared_ptr manager; + + // Keep track of all DD variables of this manager. + uint_fast64_t numberOfDdVariables; }; } } diff --git a/test/functional/storage/CuddDdTest.cpp b/test/functional/storage/CuddDdTest.cpp index 33a67b197..b6130d297 100644 --- a/test/functional/storage/CuddDdTest.cpp +++ b/test/functional/storage/CuddDdTest.cpp @@ -5,6 +5,8 @@ #include "src/storage/dd/CuddAdd.h" #include "src/storage/dd/CuddOdd.h" #include "src/storage/dd/DdMetaVariable.h" +#include "src/storage/expressions/ExpressionManager.h" +#include "src/storage/expressions/Expression.h" #include "src/settings/SettingsManager.h" #include "src/storage/SparseMatrix.h" @@ -406,4 +408,28 @@ TEST(CuddDd, BddOddTest) { EXPECT_EQ(9ul, matrix.getRowGroupCount()); EXPECT_EQ(9ul, matrix.getColumnCount()); EXPECT_EQ(106ul, matrix.getNonzeroEntryCount()); +} + +TEST(CuddDd, BddToExpressionTest) { + std::shared_ptr> ddManager(new storm::dd::DdManager()); + std::pair a = ddManager->addMetaVariable("a"); + std::pair b = ddManager->addMetaVariable("b"); + + storm::dd::Bdd bdd = ddManager->getBddOne(); + bdd &= ddManager->getEncoding(a.first, 1); + bdd |= ddManager->getEncoding(b.first, 0); + + std::shared_ptr manager = std::make_shared(); + storm::expressions::Variable c = manager->declareBooleanVariable("c"); + storm::expressions::Variable d = manager->declareBooleanVariable("d"); + + std::unordered_map indexToExpressionMap; + indexToExpressionMap[0] = c; + indexToExpressionMap[2] = d; + auto result = bdd.toExpression(*manager, indexToExpressionMap); + + for (auto const& expression : result.first) { + std::cout << expression << std::endl; + } + } \ No newline at end of file From 237690581063d32573aa304ced44d9cd583c901f Mon Sep 17 00:00:00 2001 From: dehnert Date: Thu, 24 Sep 2015 17:44:26 +0200 Subject: [PATCH 028/400] more work Former-commit-id: 7182125a9eb7da7e54e149a6c3517a238940718e --- src/storage/dd/CuddBdd.cpp | 20 +++--- src/storage/prism/Program.cpp | 10 +++ src/storage/prism/Program.h | 7 ++ .../prism/menu_games/AbstractProgram.cpp | 25 ++------ .../menu_games/AbstractionDdInformation.cpp | 6 +- .../menu_games/AbstractionDdInformation.h | 3 +- .../prism/menu_games/StateSetAbstractor.cpp | 64 +++++++++---------- .../prism/menu_games/StateSetAbstractor.h | 29 ++++++--- .../prism/menu_games/VariablePartition.cpp | 9 +-- .../abstraction/PrismMenuGameTest.cpp | 4 +- 10 files changed, 99 insertions(+), 78 deletions(-) diff --git a/src/storage/dd/CuddBdd.cpp b/src/storage/dd/CuddBdd.cpp index fc673e0f2..b16421332 100644 --- a/src/storage/dd/CuddBdd.cpp +++ b/src/storage/dd/CuddBdd.cpp @@ -395,14 +395,18 @@ namespace storm { storm::expressions::Variable Bdd::toExpressionRec(DdNode const* dd, Cudd const& ddManager, storm::expressions::ExpressionManager& manager, std::vector& expressions, std::unordered_map, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map& nodeToCounterMap, std::vector& nextCounterForIndex, std::unordered_map const& indexToExpressionMap) const { STORM_LOG_ASSERT(dd == Cudd_Regular(dd), "Expected non-negated BDD node."); - // First, try to look up the current node. - auto nodeCounterIt = nodeToCounterMap.find(dd); - if (nodeCounterIt != nodeToCounterMap.end()) { - // If we have found the node, this means we can look up the counter-index pair and get the corresponding variable. - auto variableIt = countIndexToVariablePair.find(std::make_pair(nodeCounterIt->second, dd->index)); - STORM_LOG_ASSERT(variableIt != countIndexToVariablePair.end(), "Unable to find node."); - return variableIt->second; - } + // First, try to look up the current node if it's not a terminal node. The result of terminal nodes must not + // be reused, since we want to be able to incrementally refine the expression later and that requires + // different variables for the one-leaf. +// if (!Cudd_IsConstant(dd)) { + auto nodeCounterIt = nodeToCounterMap.find(dd); + if (nodeCounterIt != nodeToCounterMap.end()) { + // If we have found the node, this means we can look up the counter-index pair and get the corresponding variable. + auto variableIt = countIndexToVariablePair.find(std::make_pair(nodeCounterIt->second, dd->index)); + STORM_LOG_ASSERT(variableIt != countIndexToVariablePair.end(), "Unable to find node."); + return variableIt->second; + } +// } // If the node was not yet encountered, we create a variable and associate it with the appropriate expression. storm::expressions::Variable newVariable = manager.declareFreshBooleanVariable(); diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 7d1486501..06e6fead7 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -360,6 +360,16 @@ namespace storm { return this->labels; } + std::vector Program::getAllGuards() const { + std::vector allGuards; + for (auto const& module : modules) { + for (auto const& command : module.getCommands()) { + allGuards.push_back(command.getGuardExpression()); + } + } + return allGuards; + } + storm::expressions::Expression const& Program::getLabelExpression(std::string const& label) const { auto const& labelIndexPair = labelToIndexMap.find(label); STORM_LOG_THROW(labelIndexPair != labelToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Cannot retrieve expression for unknown label '" << label << "'."); diff --git a/src/storage/prism/Program.h b/src/storage/prism/Program.h index 8185472ee..188fbb58f 100644 --- a/src/storage/prism/Program.h +++ b/src/storage/prism/Program.h @@ -360,6 +360,13 @@ namespace storm { */ std::vector