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