dehnert
8 years ago
10 changed files with 567 additions and 650 deletions
-
349src/storm/abstraction/prism/AbstractProgram.cpp
-
151src/storm/abstraction/prism/AbstractProgram.h
-
48src/storm/abstraction/prism/CommandAbstractor.cpp
-
6src/storm/abstraction/prism/CommandAbstractor.h
-
32src/storm/abstraction/prism/ModuleAbstractor.cpp
-
22src/storm/abstraction/prism/ModuleAbstractor.h
-
326src/storm/abstraction/prism/PrismMenuGameAbstractor.cpp
-
147src/storm/abstraction/prism/PrismMenuGameAbstractor.h
-
118src/test/abstraction/PrismMenuGameTest.cpp
-
18src/test/utility/GraphTest.cpp
@ -1,349 +0,0 @@ |
|||
#include "storm/abstraction/prism/AbstractProgram.h"
|
|||
|
|||
#include "storm/abstraction/BottomStateResult.h"
|
|||
#include "storm/abstraction/GameBddResult.h"
|
|||
|
|||
#include "storm/storage/BitVector.h"
|
|||
|
|||
#include "storm/storage/prism/Program.h"
|
|||
|
|||
#include "storm/storage/dd/DdManager.h"
|
|||
#include "storm/storage/dd/Add.h"
|
|||
|
|||
#include "storm/models/symbolic/StandardRewardModel.h"
|
|||
|
|||
#include "storm/utility/dd.h"
|
|||
#include "storm/utility/macros.h"
|
|||
#include "storm/utility/solver.h"
|
|||
#include "storm/exceptions/WrongFormatException.h"
|
|||
#include "storm/exceptions/InvalidArgumentException.h"
|
|||
|
|||
#include "storm-config.h"
|
|||
#include "storm/adapters/CarlAdapter.h"
|
|||
|
|||
namespace storm { |
|||
namespace abstraction { |
|||
namespace prism { |
|||
|
|||
#undef LOCAL_DEBUG
|
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
AbstractProgram<DdType, ValueType>::AbstractProgram(storm::prism::Program const& program, |
|||
std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory, |
|||
bool addAllGuards) |
|||
: program(program), smtSolverFactory(smtSolverFactory), abstractionInformation(program.getManager()), modules(), initialStateAbstractor(abstractionInformation, program.getAllExpressionVariables(), {program.getInitialStatesExpression()}, this->smtSolverFactory), addedAllGuards(addAllGuards), currentGame(nullptr), refinementPerformed(false) { |
|||
|
|||
// 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."); |
|||
|
|||
// Add all variables and range expressions to the information object.
|
|||
for (auto const& variable : this->program.get().getAllExpressionVariables()) { |
|||
abstractionInformation.addExpressionVariable(variable); |
|||
} |
|||
for (auto const& range : this->program.get().getAllRangeExpressions()) { |
|||
abstractionInformation.addConstraint(range); |
|||
initialStateAbstractor.constrain(range); |
|||
} |
|||
|
|||
uint_fast64_t totalNumberOfCommands = 0; |
|||
uint_fast64_t maximalUpdateCount = 0; |
|||
std::vector<storm::expressions::Expression> allGuards; |
|||
for (auto const& module : program.getModules()) { |
|||
// If we were requested to add all guards to the set of predicates, we do so now.
|
|||
for (auto const& command : module.getCommands()) { |
|||
if (addAllGuards) { |
|||
allGuards.push_back(command.getGuardExpression()); |
|||
} |
|||
maximalUpdateCount = std::max(maximalUpdateCount, static_cast<uint_fast64_t>(command.getNumberOfUpdates())); |
|||
} |
|||
|
|||
totalNumberOfCommands += module.getNumberOfCommands(); |
|||
} |
|||
|
|||
// NOTE: currently we assume that 100 player 2 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.
|
|||
abstractionInformation.createEncodingVariables(static_cast<uint_fast64_t>(std::ceil(std::log2(totalNumberOfCommands))), 100, static_cast<uint_fast64_t>(std::ceil(std::log2(maximalUpdateCount)))); |
|||
|
|||
// For each module of the concrete program, we create an abstract counterpart.
|
|||
for (auto const& module : program.getModules()) { |
|||
this->modules.emplace_back(module, abstractionInformation, this->smtSolverFactory, addAllGuards); |
|||
} |
|||
|
|||
// Retrieve the command-update probability ADD, so we can multiply it with the abstraction BDD later.
|
|||
commandUpdateProbabilitiesAdd = modules.front().getCommandUpdateProbabilitiesAdd(); |
|||
|
|||
// Now that we have created all other DD variables, we create the DD variables for the predicates.
|
|||
std::vector<storm::expressions::Expression> initialPredicates; |
|||
if (addAllGuards) { |
|||
for (auto const& guard : allGuards) { |
|||
initialPredicates.push_back(guard); |
|||
} |
|||
} |
|||
|
|||
// Finally, refine using the all predicates and build game as a by-product.
|
|||
this->refine(initialPredicates); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
void AbstractProgram<DdType, ValueType>::refine(std::vector<storm::expressions::Expression> const& predicates) { |
|||
// Add the predicates to the global list of predicates and gather their indices.
|
|||
std::vector<uint_fast64_t> newPredicateIndices; |
|||
for (auto const& predicate : predicates) { |
|||
STORM_LOG_THROW(predicate.hasBooleanType(), storm::exceptions::InvalidArgumentException, "Expecting a predicate of type bool."); |
|||
newPredicateIndices.push_back(abstractionInformation.addPredicate(predicate)); |
|||
} |
|||
|
|||
// Refine all abstract modules.
|
|||
for (auto& module : modules) { |
|||
module.refine(newPredicateIndices); |
|||
} |
|||
|
|||
// Refine initial state abstractor.
|
|||
initialStateAbstractor.refine(newPredicateIndices); |
|||
|
|||
// Update the flag that stores whether a refinement was performed.
|
|||
refinementPerformed = refinementPerformed || !newPredicateIndices.empty(); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
MenuGame<DdType, ValueType> AbstractProgram<DdType, ValueType>::abstract() { |
|||
if (refinementPerformed) { |
|||
currentGame = buildGame(); |
|||
refinementPerformed = true; |
|||
} |
|||
return *currentGame; |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
AbstractionInformation<DdType> const& AbstractProgram<DdType, ValueType>::getAbstractionInformation() const { |
|||
return abstractionInformation; |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
storm::expressions::Expression const& AbstractProgram<DdType, ValueType>::getGuard(uint64_t player1Choice) const { |
|||
return modules.front().getGuard(player1Choice); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
std::map<storm::expressions::Variable, storm::expressions::Expression> AbstractProgram<DdType, ValueType>::getVariableUpdates(uint64_t player1Choice, uint64_t auxiliaryChoice) const { |
|||
return modules.front().getVariableUpdates(player1Choice, auxiliaryChoice); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
storm::dd::Bdd<DdType> AbstractProgram<DdType, ValueType>::getStates(storm::expressions::Expression const& predicate) { |
|||
STORM_LOG_ASSERT(currentGame != nullptr, "Game was not properly created."); |
|||
return abstractionInformation.getPredicateSourceVariable(predicate); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
std::unique_ptr<MenuGame<DdType, ValueType>> AbstractProgram<DdType, ValueType>::buildGame() { |
|||
// As long as there is only one module, we only build its game representation.
|
|||
GameBddResult<DdType> game = modules.front().getAbstractBdd(); |
|||
|
|||
// Construct a set of all unnecessary variables, so we can abstract from it.
|
|||
std::set<storm::expressions::Variable> variablesToAbstract(abstractionInformation.getPlayer1VariableSet(abstractionInformation.getPlayer1VariableCount())); |
|||
auto player2Variables = abstractionInformation.getPlayer2VariableSet(game.numberOfPlayer2Variables); |
|||
variablesToAbstract.insert(player2Variables.begin(), player2Variables.end()); |
|||
auto auxVariables = abstractionInformation.getAuxVariableSet(0, abstractionInformation.getAuxVariableCount()); |
|||
variablesToAbstract.insert(auxVariables.begin(), auxVariables.end()); |
|||
|
|||
// Do a reachability analysis on the raw transition relation.
|
|||
storm::dd::Bdd<DdType> transitionRelation = game.bdd.existsAbstract(variablesToAbstract); |
|||
storm::dd::Bdd<DdType> initialStates = initialStateAbstractor.getAbstractStates(); |
|||
storm::dd::Bdd<DdType> reachableStates = storm::utility::dd::computeReachableStates(initialStates, transitionRelation, abstractionInformation.getSourceVariables(), abstractionInformation.getSuccessorVariables()); |
|||
|
|||
// Find the deadlock states in the model. Note that this does not find the 'deadlocks' in bottom states,
|
|||
// as the bottom states are not contained in the reachable states.
|
|||
storm::dd::Bdd<DdType> deadlockStates = transitionRelation.existsAbstract(abstractionInformation.getSuccessorVariables()); |
|||
deadlockStates = reachableStates && !deadlockStates; |
|||
|
|||
// If there are deadlock states, we fix them now.
|
|||
storm::dd::Add<DdType, ValueType> deadlockTransitions = abstractionInformation.getDdManager().template getAddZero<ValueType>(); |
|||
if (!deadlockStates.isZero()) { |
|||
deadlockTransitions = (deadlockStates && abstractionInformation.getAllPredicateIdentities() && abstractionInformation.encodePlayer1Choice(0, abstractionInformation.getPlayer1VariableCount()) && abstractionInformation.encodePlayer2Choice(0, game.numberOfPlayer2Variables) && abstractionInformation.encodeAux(0, 0, abstractionInformation.getAuxVariableCount())).template toAdd<ValueType>(); |
|||
} |
|||
|
|||
// Compute bottom states and the appropriate transitions if necessary.
|
|||
BottomStateResult<DdType> bottomStateResult(abstractionInformation.getDdManager().getBddZero(), abstractionInformation.getDdManager().getBddZero()); |
|||
bool hasBottomStates = false; |
|||
if (!addedAllGuards) { |
|||
bottomStateResult = modules.front().getBottomStateTransitions(reachableStates, game.numberOfPlayer2Variables); |
|||
hasBottomStates = !bottomStateResult.states.isZero(); |
|||
} |
|||
|
|||
// Construct the transition matrix by cutting away the transitions of unreachable states.
|
|||
storm::dd::Add<DdType, ValueType> transitionMatrix = (game.bdd && reachableStates).template toAdd<ValueType>(); |
|||
transitionMatrix *= commandUpdateProbabilitiesAdd; |
|||
transitionMatrix += deadlockTransitions; |
|||
|
|||
// Extend the current game information with the 'non-bottom' tag before potentially adding bottom state transitions.
|
|||
transitionMatrix *= (abstractionInformation.getBottomStateBdd(true, true) && abstractionInformation.getBottomStateBdd(false, true)).template toAdd<ValueType>(); |
|||
reachableStates &= abstractionInformation.getBottomStateBdd(true, true); |
|||
initialStates &= abstractionInformation.getBottomStateBdd(true, true); |
|||
|
|||
// If there are bottom transitions, exnted the transition matrix and reachable states now.
|
|||
if (hasBottomStates) { |
|||
transitionMatrix += bottomStateResult.transitions.template toAdd<ValueType>(); |
|||
reachableStates |= bottomStateResult.states; |
|||
} |
|||
|
|||
std::set<storm::expressions::Variable> usedPlayer2Variables(abstractionInformation.getPlayer2Variables().begin(), abstractionInformation.getPlayer2Variables().begin() + game.numberOfPlayer2Variables); |
|||
|
|||
std::set<storm::expressions::Variable> allNondeterminismVariables = usedPlayer2Variables; |
|||
allNondeterminismVariables.insert(abstractionInformation.getPlayer1Variables().begin(), abstractionInformation.getPlayer1Variables().end()); |
|||
|
|||
std::set<storm::expressions::Variable> allSourceVariables(abstractionInformation.getSourceVariables()); |
|||
allSourceVariables.insert(abstractionInformation.getBottomStateVariable(true)); |
|||
std::set<storm::expressions::Variable> allSuccessorVariables(abstractionInformation.getSuccessorVariables()); |
|||
allSuccessorVariables.insert(abstractionInformation.getBottomStateVariable(false)); |
|||
|
|||
return std::make_unique<MenuGame<DdType, ValueType>>(abstractionInformation.getDdManagerAsSharedPointer(), reachableStates, initialStates, abstractionInformation.getDdManager().getBddZero(), transitionMatrix, bottomStateResult.states, allSourceVariables, allSuccessorVariables, abstractionInformation.getExtendedSourceSuccessorVariablePairs(), std::set<storm::expressions::Variable>(abstractionInformation.getPlayer1Variables().begin(), abstractionInformation.getPlayer1Variables().end()), usedPlayer2Variables, allNondeterminismVariables, auxVariables, abstractionInformation.getPredicateToBddMap()); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
void AbstractProgram<DdType, ValueType>::exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStatesBdd, storm::dd::Bdd<DdType> const& filter) const { |
|||
std::ofstream out(filename); |
|||
|
|||
storm::dd::Add<DdType, ValueType> filteredTransitions = filter.template toAdd<ValueType>() * currentGame->getTransitionMatrix(); |
|||
storm::dd::Bdd<DdType> filteredTransitionsBdd = filteredTransitions.toBdd().existsAbstract(currentGame->getNondeterminismVariables()); |
|||
storm::dd::Bdd<DdType> filteredReachableStates = storm::utility::dd::computeReachableStates(currentGame->getInitialStates(), filteredTransitionsBdd, currentGame->getRowVariables(), currentGame->getColumnVariables()); |
|||
filteredTransitions *= filteredReachableStates.template toAdd<ValueType>(); |
|||
|
|||
// Determine all initial states so we can color them blue.
|
|||
std::unordered_set<std::string> initialStates; |
|||
storm::dd::Add<DdType, ValueType> initialStatesAsAdd = currentGame->getInitialStates().template toAdd<ValueType>(); |
|||
for (auto stateValue : initialStatesAsAdd) { |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
initialStates.insert(stateName.str()); |
|||
} |
|||
|
|||
// Determine all highlight states so we can color them red.
|
|||
std::unordered_set<std::string> highlightStates; |
|||
storm::dd::Add<DdType, ValueType> highlightStatesAdd = highlightStatesBdd.template toAdd<ValueType>(); |
|||
for (auto stateValue : highlightStatesAdd) { |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
highlightStates.insert(stateName.str()); |
|||
} |
|||
|
|||
out << "digraph game {" << std::endl; |
|||
|
|||
// Create the player 1 nodes.
|
|||
storm::dd::Add<DdType, ValueType> statesAsAdd = filteredReachableStates.template toAdd<ValueType>(); |
|||
for (auto stateValue : statesAsAdd) { |
|||
out << "\tpl1_"; |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
std::string stateNameAsString = stateName.str(); |
|||
out << stateNameAsString; |
|||
out << " [ label=\""; |
|||
if (stateValue.first.getBooleanValue(abstractionInformation.getBottomStateVariable(true))) { |
|||
out << "*\", margin=0, width=0, height=0, shape=\"none\""; |
|||
} else { |
|||
out << stateName.str() << "\", margin=0, width=0, height=0, shape=\"oval\""; |
|||
} |
|||
bool isInitial = initialStates.find(stateNameAsString) != initialStates.end(); |
|||
bool isHighlight = highlightStates.find(stateNameAsString) != highlightStates.end(); |
|||
if (isInitial && isHighlight) { |
|||
out << ", style=\"filled\", fillcolor=\"yellow\""; |
|||
} else if (isInitial) { |
|||
out << ", style=\"filled\", fillcolor=\"blue\""; |
|||
} else if (isHighlight) { |
|||
out << ", style=\"filled\", fillcolor=\"red\""; |
|||
} |
|||
out << " ];" << std::endl; |
|||
} |
|||
|
|||
// Create the nodes of the second player.
|
|||
storm::dd::Add<DdType, ValueType> player2States = filteredTransitions.toBdd().existsAbstract(currentGame->getColumnVariables()).existsAbstract(currentGame->getPlayer2Variables()).template toAdd<ValueType>(); |
|||
for (auto stateValue : player2States) { |
|||
out << "\tpl2_"; |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
uint_fast64_t index = abstractionInformation.decodePlayer1Choice(stateValue.first, abstractionInformation.getPlayer1VariableCount()); |
|||
out << stateName.str() << "_" << index; |
|||
out << " [ shape=\"square\", width=0, height=0, margin=0, label=\"" << index << "\" ];" << std::endl; |
|||
out << "\tpl1_" << stateName.str() << " -> " << "pl2_" << stateName.str() << "_" << index << " [ label=\"" << index << "\" ];" << std::endl; |
|||
} |
|||
|
|||
// Create the nodes of the probabilistic player.
|
|||
storm::dd::Add<DdType, ValueType> playerPStates = filteredTransitions.toBdd().existsAbstract(currentGame->getColumnVariables()).template toAdd<ValueType>(); |
|||
for (auto stateValue : playerPStates) { |
|||
out << "\tplp_"; |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
uint_fast64_t index = abstractionInformation.decodePlayer1Choice(stateValue.first, abstractionInformation.getPlayer1VariableCount()); |
|||
stateName << "_" << index; |
|||
index = abstractionInformation.decodePlayer2Choice(stateValue.first, currentGame->getPlayer2Variables().size()); |
|||
out << stateName.str() << "_" << index; |
|||
out << " [ shape=\"point\", label=\"\" ];" << std::endl; |
|||
out << "\tpl2_" << stateName.str() << " -> " << "plp_" << stateName.str() << "_" << index << " [ label=\"" << index << "\" ];" << std::endl; |
|||
} |
|||
|
|||
for (auto stateValue : filteredTransitions) { |
|||
std::stringstream sourceStateName; |
|||
std::stringstream successorStateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
sourceStateName << "1"; |
|||
} else { |
|||
sourceStateName << "0"; |
|||
} |
|||
} |
|||
for (auto const& var : currentGame->getColumnVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
successorStateName << "1"; |
|||
} else { |
|||
successorStateName << "0"; |
|||
} |
|||
} |
|||
uint_fast64_t pl1Index = abstractionInformation.decodePlayer1Choice(stateValue.first, abstractionInformation.getPlayer1VariableCount()); |
|||
uint_fast64_t pl2Index = abstractionInformation.decodePlayer2Choice(stateValue.first, currentGame->getPlayer2Variables().size()); |
|||
out << "\tplp_" << sourceStateName.str() << "_" << pl1Index << "_" << pl2Index << " -> pl1_" << successorStateName.str() << " [ label=\"" << stateValue.second << "\"];" << std::endl; |
|||
} |
|||
|
|||
out << "}" << std::endl; |
|||
} |
|||
|
|||
// Explicitly instantiate the class.
|
|||
template class AbstractProgram<storm::dd::DdType::CUDD, double>; |
|||
template class AbstractProgram<storm::dd::DdType::Sylvan, double>; |
|||
#ifdef STORM_HAVE_CARL
|
|||
template class AbstractProgram<storm::dd::DdType::Sylvan, storm::RationalFunction>; |
|||
#endif
|
|||
} |
|||
} |
|||
} |
@ -1,151 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "storm/storage/dd/DdType.h" |
|||
|
|||
#include "storm/abstraction/AbstractionInformation.h" |
|||
#include "storm/abstraction/MenuGame.h" |
|||
#include "storm/abstraction/prism/AbstractModule.h" |
|||
|
|||
#include "storm/storage/dd/Add.h" |
|||
|
|||
#include "storm/storage/expressions/Expression.h" |
|||
|
|||
namespace storm { |
|||
namespace utility { |
|||
namespace solver { |
|||
class SmtSolverFactory; |
|||
} |
|||
} |
|||
|
|||
namespace models { |
|||
namespace symbolic { |
|||
template<storm::dd::DdType Type, typename ValueType> |
|||
class StochasticTwoPlayerGame; |
|||
} |
|||
} |
|||
|
|||
namespace prism { |
|||
// Forward-declare concrete Program class. |
|||
class Program; |
|||
} |
|||
|
|||
namespace abstraction { |
|||
namespace prism { |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
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 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::prism::Program const& program, std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory = std::make_shared<storm::utility::solver::MathsatSmtSolverFactory>(), bool addAllGuards = false); |
|||
|
|||
AbstractProgram(AbstractProgram const&) = default; |
|||
AbstractProgram& operator=(AbstractProgram const&) = default; |
|||
AbstractProgram(AbstractProgram&&) = default; |
|||
AbstractProgram& operator=(AbstractProgram&&) = default; |
|||
|
|||
/*! |
|||
* Uses the current set of predicates to derive the abstract menu game in the form of an ADD. |
|||
* |
|||
* @return The abstract stochastic two player game. |
|||
*/ |
|||
MenuGame<DdType, ValueType> abstract(); |
|||
|
|||
/*! |
|||
* Retrieves information about the abstraction. |
|||
* |
|||
* @return The abstraction information object. |
|||
*/ |
|||
AbstractionInformation<DdType> const& getAbstractionInformation() const; |
|||
|
|||
/*! |
|||
* Retrieves the guard predicate of the given player 1 choice. |
|||
* |
|||
* @param player1Choice The choice for which to retrieve the guard. |
|||
* @return The guard of the player 1 choice. |
|||
*/ |
|||
storm::expressions::Expression const& getGuard(uint64_t player1Choice) const; |
|||
|
|||
/*! |
|||
* Retrieves a mapping from variables to expressions that define their updates wrt. to the given player |
|||
* 1 choice and auxiliary choice. |
|||
*/ |
|||
std::map<storm::expressions::Variable, storm::expressions::Expression> getVariableUpdates(uint64_t player1Choice, uint64_t auxiliaryChoice) const; |
|||
|
|||
/*! |
|||
* 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. |
|||
* |
|||
* @param predicate The predicate for which to retrieve the states. |
|||
* @return The BDD representing the set of states. |
|||
*/ |
|||
storm::dd::Bdd<DdType> getStates(storm::expressions::Expression const& predicate); |
|||
|
|||
/*! |
|||
* Refines the abstract program with the given predicates. |
|||
* |
|||
* @param predicates The new predicates. |
|||
*/ |
|||
void refine(std::vector<storm::expressions::Expression> const& predicates); |
|||
|
|||
/*! |
|||
* Exports the current state of the abstraction in the dot format to the given file. |
|||
* |
|||
* @param filename The name of the file to which to write the dot output. |
|||
* @param highlightStates A BDD characterizing states that will be highlighted. |
|||
* @param filter A filter that is applied to select which part of the game to export. |
|||
*/ |
|||
void exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStates, storm::dd::Bdd<DdType> const& filter) const; |
|||
|
|||
private: |
|||
/*! |
|||
* Builds the stochastic game representing the abstraction of the program. |
|||
* |
|||
* @return The stochastic game. |
|||
*/ |
|||
std::unique_ptr<MenuGame<DdType, ValueType>> buildGame(); |
|||
|
|||
/*! |
|||
* Decodes the given choice over the auxiliary and successor variables to a mapping from update indices |
|||
* to bit vectors representing the successors under these updates. |
|||
* |
|||
* @param choice The choice to decode. |
|||
*/ |
|||
std::map<uint_fast64_t, storm::storage::BitVector> decodeChoiceToUpdateSuccessorMapping(storm::dd::Bdd<DdType> const& choice) const; |
|||
|
|||
// The concrete program this abstract program refers to. |
|||
std::reference_wrapper<storm::prism::Program const> program; |
|||
|
|||
// A factory that can be used to create new SMT solvers. |
|||
std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory; |
|||
|
|||
// An object containing all information about the abstraction like predicates and the corresponding DDs. |
|||
AbstractionInformation<DdType> abstractionInformation; |
|||
|
|||
// The abstract modules of the abstract program. |
|||
std::vector<AbstractModule<DdType, ValueType>> modules; |
|||
|
|||
// A state-set abstractor used to determine the initial states of the abstraction. |
|||
StateSetAbstractor<DdType, ValueType> initialStateAbstractor; |
|||
|
|||
// A flag that stores whether all guards were added (which is relevant for determining the bottom states). |
|||
bool addedAllGuards; |
|||
|
|||
// An ADD characterizing the probabilities of commands and their updates. |
|||
storm::dd::Add<DdType, ValueType> commandUpdateProbabilitiesAdd; |
|||
|
|||
// The current game-based abstraction. |
|||
std::unique_ptr<MenuGame<DdType, ValueType>> currentGame; |
|||
|
|||
// A flag storing whether a refinement was performed. |
|||
bool refinementPerformed; |
|||
}; |
|||
} |
|||
} |
|||
} |
@ -1,51 +1,349 @@ |
|||
#include "storm/abstraction/prism/PrismMenuGameAbstractor.h"
|
|||
|
|||
#include "storm/abstraction/BottomStateResult.h"
|
|||
#include "storm/abstraction/GameBddResult.h"
|
|||
|
|||
#include "storm/storage/BitVector.h"
|
|||
|
|||
#include "storm/storage/prism/Program.h"
|
|||
|
|||
#include "storm/storage/dd/DdManager.h"
|
|||
#include "storm/storage/dd/Add.h"
|
|||
|
|||
#include "storm/models/symbolic/StandardRewardModel.h"
|
|||
|
|||
#include "storm/settings/SettingsManager.h"
|
|||
#include "storm/settings/modules/AbstractionSettings.h"
|
|||
#include "storm/utility/dd.h"
|
|||
#include "storm/utility/macros.h"
|
|||
#include "storm/utility/solver.h"
|
|||
#include "storm/exceptions/WrongFormatException.h"
|
|||
#include "storm/exceptions/InvalidArgumentException.h"
|
|||
|
|||
#include "storm-config.h"
|
|||
#include "storm/adapters/CarlAdapter.h"
|
|||
|
|||
namespace storm { |
|||
namespace abstraction { |
|||
namespace prism { |
|||
|
|||
#undef LOCAL_DEBUG
|
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
PrismMenuGameAbstractor<DdType, ValueType>::PrismMenuGameAbstractor(storm::prism::Program const& program, std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory) : abstractProgram(program, smtSolverFactory, storm::settings::getModule<storm::settings::modules::AbstractionSettings>().isAddAllGuardsSet()) { |
|||
// Intentionally left empty.
|
|||
PrismMenuGameAbstractor<DdType, ValueType>::PrismMenuGameAbstractor(storm::prism::Program const& program, |
|||
std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory, |
|||
bool addAllGuards) |
|||
: program(program), smtSolverFactory(smtSolverFactory), abstractionInformation(program.getManager()), modules(), initialStateAbstractor(abstractionInformation, program.getAllExpressionVariables(), {program.getInitialStatesExpression()}, this->smtSolverFactory), addedAllGuards(addAllGuards), currentGame(nullptr), refinementPerformed(false) { |
|||
|
|||
// 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."); |
|||
|
|||
// Add all variables and range expressions to the information object.
|
|||
for (auto const& variable : this->program.get().getAllExpressionVariables()) { |
|||
abstractionInformation.addExpressionVariable(variable); |
|||
} |
|||
for (auto const& range : this->program.get().getAllRangeExpressions()) { |
|||
abstractionInformation.addConstraint(range); |
|||
initialStateAbstractor.constrain(range); |
|||
} |
|||
|
|||
uint_fast64_t totalNumberOfCommands = 0; |
|||
uint_fast64_t maximalUpdateCount = 0; |
|||
std::vector<storm::expressions::Expression> allGuards; |
|||
for (auto const& module : program.getModules()) { |
|||
// If we were requested to add all guards to the set of predicates, we do so now.
|
|||
for (auto const& command : module.getCommands()) { |
|||
if (addAllGuards) { |
|||
allGuards.push_back(command.getGuardExpression()); |
|||
} |
|||
maximalUpdateCount = std::max(maximalUpdateCount, static_cast<uint_fast64_t>(command.getNumberOfUpdates())); |
|||
} |
|||
|
|||
totalNumberOfCommands += module.getNumberOfCommands(); |
|||
} |
|||
|
|||
// NOTE: currently we assume that 100 player 2 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.
|
|||
abstractionInformation.createEncodingVariables(static_cast<uint_fast64_t>(std::ceil(std::log2(totalNumberOfCommands))), 100, static_cast<uint_fast64_t>(std::ceil(std::log2(maximalUpdateCount)))); |
|||
|
|||
// For each module of the concrete program, we create an abstract counterpart.
|
|||
for (auto const& module : program.getModules()) { |
|||
this->modules.emplace_back(module, abstractionInformation, this->smtSolverFactory, addAllGuards); |
|||
} |
|||
|
|||
// Retrieve the command-update probability ADD, so we can multiply it with the abstraction BDD later.
|
|||
commandUpdateProbabilitiesAdd = modules.front().getCommandUpdateProbabilitiesAdd(); |
|||
|
|||
// Now that we have created all other DD variables, we create the DD variables for the predicates.
|
|||
std::vector<storm::expressions::Expression> initialPredicates; |
|||
if (addAllGuards) { |
|||
for (auto const& guard : allGuards) { |
|||
initialPredicates.push_back(guard); |
|||
} |
|||
} |
|||
|
|||
// Finally, refine using the all predicates and build game as a by-product.
|
|||
this->refine(initialPredicates); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
storm::abstraction::MenuGame<DdType, ValueType> PrismMenuGameAbstractor<DdType, ValueType>::abstract() { |
|||
return abstractProgram.abstract(); |
|||
void PrismMenuGameAbstractor<DdType, ValueType>::refine(std::vector<storm::expressions::Expression> const& predicates) { |
|||
// Add the predicates to the global list of predicates and gather their indices.
|
|||
std::vector<uint_fast64_t> newPredicateIndices; |
|||
for (auto const& predicate : predicates) { |
|||
STORM_LOG_THROW(predicate.hasBooleanType(), storm::exceptions::InvalidArgumentException, "Expecting a predicate of type bool."); |
|||
newPredicateIndices.push_back(abstractionInformation.addPredicate(predicate)); |
|||
} |
|||
|
|||
// Refine all abstract modules.
|
|||
for (auto& module : modules) { |
|||
module.refine(newPredicateIndices); |
|||
} |
|||
|
|||
// Refine initial state abstractor.
|
|||
initialStateAbstractor.refine(newPredicateIndices); |
|||
|
|||
// Update the flag that stores whether a refinement was performed.
|
|||
refinementPerformed = refinementPerformed || !newPredicateIndices.empty(); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
MenuGame<DdType, ValueType> PrismMenuGameAbstractor<DdType, ValueType>::abstract() { |
|||
if (refinementPerformed) { |
|||
currentGame = buildGame(); |
|||
refinementPerformed = true; |
|||
} |
|||
return *currentGame; |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
AbstractionInformation<DdType> const& PrismMenuGameAbstractor<DdType, ValueType>::getAbstractionInformation() const { |
|||
return abstractProgram.getAbstractionInformation(); |
|||
return abstractionInformation; |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
storm::expressions::Expression const& PrismMenuGameAbstractor<DdType, ValueType>::getGuard(uint64_t player1Choice) const { |
|||
return abstractProgram.getGuard(player1Choice); |
|||
return modules.front().getGuard(player1Choice); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
std::map<storm::expressions::Variable, storm::expressions::Expression> PrismMenuGameAbstractor<DdType, ValueType>::getVariableUpdates(uint64_t player1Choice, uint64_t auxiliaryChoice) const { |
|||
return abstractProgram.getVariableUpdates(player1Choice, auxiliaryChoice); |
|||
return modules.front().getVariableUpdates(player1Choice, auxiliaryChoice); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
void PrismMenuGameAbstractor<DdType, ValueType>::refine(std::vector<storm::expressions::Expression> const& predicates) { |
|||
abstractProgram.refine(predicates); |
|||
storm::dd::Bdd<DdType> PrismMenuGameAbstractor<DdType, ValueType>::getStates(storm::expressions::Expression const& predicate) { |
|||
STORM_LOG_ASSERT(currentGame != nullptr, "Game was not properly created."); |
|||
return abstractionInformation.getPredicateSourceVariable(predicate); |
|||
} |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
void PrismMenuGameAbstractor<DdType, ValueType>::exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStates, storm::dd::Bdd<DdType> const& filter) const { |
|||
abstractProgram.exportToDot(filename, highlightStates, filter); |
|||
std::unique_ptr<MenuGame<DdType, ValueType>> PrismMenuGameAbstractor<DdType, ValueType>::buildGame() { |
|||
// As long as there is only one module, we only build its game representation.
|
|||
GameBddResult<DdType> game = modules.front().abstract(); |
|||
|
|||
// Construct a set of all unnecessary variables, so we can abstract from it.
|
|||
std::set<storm::expressions::Variable> variablesToAbstract(abstractionInformation.getPlayer1VariableSet(abstractionInformation.getPlayer1VariableCount())); |
|||
auto player2Variables = abstractionInformation.getPlayer2VariableSet(game.numberOfPlayer2Variables); |
|||
variablesToAbstract.insert(player2Variables.begin(), player2Variables.end()); |
|||
auto auxVariables = abstractionInformation.getAuxVariableSet(0, abstractionInformation.getAuxVariableCount()); |
|||
variablesToAbstract.insert(auxVariables.begin(), auxVariables.end()); |
|||
|
|||
// Do a reachability analysis on the raw transition relation.
|
|||
storm::dd::Bdd<DdType> transitionRelation = game.bdd.existsAbstract(variablesToAbstract); |
|||
storm::dd::Bdd<DdType> initialStates = initialStateAbstractor.getAbstractStates(); |
|||
storm::dd::Bdd<DdType> reachableStates = storm::utility::dd::computeReachableStates(initialStates, transitionRelation, abstractionInformation.getSourceVariables(), abstractionInformation.getSuccessorVariables()); |
|||
|
|||
// Find the deadlock states in the model. Note that this does not find the 'deadlocks' in bottom states,
|
|||
// as the bottom states are not contained in the reachable states.
|
|||
storm::dd::Bdd<DdType> deadlockStates = transitionRelation.existsAbstract(abstractionInformation.getSuccessorVariables()); |
|||
deadlockStates = reachableStates && !deadlockStates; |
|||
|
|||
// If there are deadlock states, we fix them now.
|
|||
storm::dd::Add<DdType, ValueType> deadlockTransitions = abstractionInformation.getDdManager().template getAddZero<ValueType>(); |
|||
if (!deadlockStates.isZero()) { |
|||
deadlockTransitions = (deadlockStates && abstractionInformation.getAllPredicateIdentities() && abstractionInformation.encodePlayer1Choice(0, abstractionInformation.getPlayer1VariableCount()) && abstractionInformation.encodePlayer2Choice(0, game.numberOfPlayer2Variables) && abstractionInformation.encodeAux(0, 0, abstractionInformation.getAuxVariableCount())).template toAdd<ValueType>(); |
|||
} |
|||
|
|||
// Compute bottom states and the appropriate transitions if necessary.
|
|||
BottomStateResult<DdType> bottomStateResult(abstractionInformation.getDdManager().getBddZero(), abstractionInformation.getDdManager().getBddZero()); |
|||
bool hasBottomStates = false; |
|||
if (!addedAllGuards) { |
|||
bottomStateResult = modules.front().getBottomStateTransitions(reachableStates, game.numberOfPlayer2Variables); |
|||
hasBottomStates = !bottomStateResult.states.isZero(); |
|||
} |
|||
|
|||
// Construct the transition matrix by cutting away the transitions of unreachable states.
|
|||
storm::dd::Add<DdType, ValueType> transitionMatrix = (game.bdd && reachableStates).template toAdd<ValueType>(); |
|||
transitionMatrix *= commandUpdateProbabilitiesAdd; |
|||
transitionMatrix += deadlockTransitions; |
|||
|
|||
// Extend the current game information with the 'non-bottom' tag before potentially adding bottom state transitions.
|
|||
transitionMatrix *= (abstractionInformation.getBottomStateBdd(true, true) && abstractionInformation.getBottomStateBdd(false, true)).template toAdd<ValueType>(); |
|||
reachableStates &= abstractionInformation.getBottomStateBdd(true, true); |
|||
initialStates &= abstractionInformation.getBottomStateBdd(true, true); |
|||
|
|||
// If there are bottom transitions, exnted the transition matrix and reachable states now.
|
|||
if (hasBottomStates) { |
|||
transitionMatrix += bottomStateResult.transitions.template toAdd<ValueType>(); |
|||
reachableStates |= bottomStateResult.states; |
|||
} |
|||
|
|||
std::set<storm::expressions::Variable> usedPlayer2Variables(abstractionInformation.getPlayer2Variables().begin(), abstractionInformation.getPlayer2Variables().begin() + game.numberOfPlayer2Variables); |
|||
|
|||
std::set<storm::expressions::Variable> allNondeterminismVariables = usedPlayer2Variables; |
|||
allNondeterminismVariables.insert(abstractionInformation.getPlayer1Variables().begin(), abstractionInformation.getPlayer1Variables().end()); |
|||
|
|||
std::set<storm::expressions::Variable> allSourceVariables(abstractionInformation.getSourceVariables()); |
|||
allSourceVariables.insert(abstractionInformation.getBottomStateVariable(true)); |
|||
std::set<storm::expressions::Variable> allSuccessorVariables(abstractionInformation.getSuccessorVariables()); |
|||
allSuccessorVariables.insert(abstractionInformation.getBottomStateVariable(false)); |
|||
|
|||
return std::make_unique<MenuGame<DdType, ValueType>>(abstractionInformation.getDdManagerAsSharedPointer(), reachableStates, initialStates, abstractionInformation.getDdManager().getBddZero(), transitionMatrix, bottomStateResult.states, allSourceVariables, allSuccessorVariables, abstractionInformation.getExtendedSourceSuccessorVariablePairs(), std::set<storm::expressions::Variable>(abstractionInformation.getPlayer1Variables().begin(), abstractionInformation.getPlayer1Variables().end()), usedPlayer2Variables, allNondeterminismVariables, auxVariables, abstractionInformation.getPredicateToBddMap()); |
|||
} |
|||
|
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
void PrismMenuGameAbstractor<DdType, ValueType>::exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStatesBdd, storm::dd::Bdd<DdType> const& filter) const { |
|||
std::ofstream out(filename); |
|||
|
|||
storm::dd::Add<DdType, ValueType> filteredTransitions = filter.template toAdd<ValueType>() * currentGame->getTransitionMatrix(); |
|||
storm::dd::Bdd<DdType> filteredTransitionsBdd = filteredTransitions.toBdd().existsAbstract(currentGame->getNondeterminismVariables()); |
|||
storm::dd::Bdd<DdType> filteredReachableStates = storm::utility::dd::computeReachableStates(currentGame->getInitialStates(), filteredTransitionsBdd, currentGame->getRowVariables(), currentGame->getColumnVariables()); |
|||
filteredTransitions *= filteredReachableStates.template toAdd<ValueType>(); |
|||
|
|||
// Determine all initial states so we can color them blue.
|
|||
std::unordered_set<std::string> initialStates; |
|||
storm::dd::Add<DdType, ValueType> initialStatesAsAdd = currentGame->getInitialStates().template toAdd<ValueType>(); |
|||
for (auto stateValue : initialStatesAsAdd) { |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
initialStates.insert(stateName.str()); |
|||
} |
|||
|
|||
// Determine all highlight states so we can color them red.
|
|||
std::unordered_set<std::string> highlightStates; |
|||
storm::dd::Add<DdType, ValueType> highlightStatesAdd = highlightStatesBdd.template toAdd<ValueType>(); |
|||
for (auto stateValue : highlightStatesAdd) { |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
highlightStates.insert(stateName.str()); |
|||
} |
|||
|
|||
out << "digraph game {" << std::endl; |
|||
|
|||
// Create the player 1 nodes.
|
|||
storm::dd::Add<DdType, ValueType> statesAsAdd = filteredReachableStates.template toAdd<ValueType>(); |
|||
for (auto stateValue : statesAsAdd) { |
|||
out << "\tpl1_"; |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
std::string stateNameAsString = stateName.str(); |
|||
out << stateNameAsString; |
|||
out << " [ label=\""; |
|||
if (stateValue.first.getBooleanValue(abstractionInformation.getBottomStateVariable(true))) { |
|||
out << "*\", margin=0, width=0, height=0, shape=\"none\""; |
|||
} else { |
|||
out << stateName.str() << "\", margin=0, width=0, height=0, shape=\"oval\""; |
|||
} |
|||
bool isInitial = initialStates.find(stateNameAsString) != initialStates.end(); |
|||
bool isHighlight = highlightStates.find(stateNameAsString) != highlightStates.end(); |
|||
if (isInitial && isHighlight) { |
|||
out << ", style=\"filled\", fillcolor=\"yellow\""; |
|||
} else if (isInitial) { |
|||
out << ", style=\"filled\", fillcolor=\"blue\""; |
|||
} else if (isHighlight) { |
|||
out << ", style=\"filled\", fillcolor=\"red\""; |
|||
} |
|||
out << " ];" << std::endl; |
|||
} |
|||
|
|||
// Create the nodes of the second player.
|
|||
storm::dd::Add<DdType, ValueType> player2States = filteredTransitions.toBdd().existsAbstract(currentGame->getColumnVariables()).existsAbstract(currentGame->getPlayer2Variables()).template toAdd<ValueType>(); |
|||
for (auto stateValue : player2States) { |
|||
out << "\tpl2_"; |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
uint_fast64_t index = abstractionInformation.decodePlayer1Choice(stateValue.first, abstractionInformation.getPlayer1VariableCount()); |
|||
out << stateName.str() << "_" << index; |
|||
out << " [ shape=\"square\", width=0, height=0, margin=0, label=\"" << index << "\" ];" << std::endl; |
|||
out << "\tpl1_" << stateName.str() << " -> " << "pl2_" << stateName.str() << "_" << index << " [ label=\"" << index << "\" ];" << std::endl; |
|||
} |
|||
|
|||
// Create the nodes of the probabilistic player.
|
|||
storm::dd::Add<DdType, ValueType> playerPStates = filteredTransitions.toBdd().existsAbstract(currentGame->getColumnVariables()).template toAdd<ValueType>(); |
|||
for (auto stateValue : playerPStates) { |
|||
out << "\tplp_"; |
|||
std::stringstream stateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
stateName << "1"; |
|||
} else { |
|||
stateName << "0"; |
|||
} |
|||
} |
|||
uint_fast64_t index = abstractionInformation.decodePlayer1Choice(stateValue.first, abstractionInformation.getPlayer1VariableCount()); |
|||
stateName << "_" << index; |
|||
index = abstractionInformation.decodePlayer2Choice(stateValue.first, currentGame->getPlayer2Variables().size()); |
|||
out << stateName.str() << "_" << index; |
|||
out << " [ shape=\"point\", label=\"\" ];" << std::endl; |
|||
out << "\tpl2_" << stateName.str() << " -> " << "plp_" << stateName.str() << "_" << index << " [ label=\"" << index << "\" ];" << std::endl; |
|||
} |
|||
|
|||
for (auto stateValue : filteredTransitions) { |
|||
std::stringstream sourceStateName; |
|||
std::stringstream successorStateName; |
|||
for (auto const& var : currentGame->getRowVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
sourceStateName << "1"; |
|||
} else { |
|||
sourceStateName << "0"; |
|||
} |
|||
} |
|||
for (auto const& var : currentGame->getColumnVariables()) { |
|||
if (stateValue.first.getBooleanValue(var)) { |
|||
successorStateName << "1"; |
|||
} else { |
|||
successorStateName << "0"; |
|||
} |
|||
} |
|||
uint_fast64_t pl1Index = abstractionInformation.decodePlayer1Choice(stateValue.first, abstractionInformation.getPlayer1VariableCount()); |
|||
uint_fast64_t pl2Index = abstractionInformation.decodePlayer2Choice(stateValue.first, currentGame->getPlayer2Variables().size()); |
|||
out << "\tplp_" << sourceStateName.str() << "_" << pl1Index << "_" << pl2Index << " -> pl1_" << successorStateName.str() << " [ label=\"" << stateValue.second << "\"];" << std::endl; |
|||
} |
|||
|
|||
out << "}" << std::endl; |
|||
} |
|||
|
|||
// Explicitly instantiate the class.
|
|||
template class PrismMenuGameAbstractor<storm::dd::DdType::CUDD, double>; |
|||
template class PrismMenuGameAbstractor<storm::dd::DdType::Sylvan, double>; |
|||
#ifdef STORM_HAVE_CARL
|
|||
template class PrismMenuGameAbstractor<storm::dd::DdType::Sylvan, storm::RationalFunction>; |
|||
#endif
|
|||
} |
|||
} |
|||
} |
@ -1,33 +1,152 @@ |
|||
#pragma once |
|||
|
|||
#include "storm/storage/dd/DdType.h" |
|||
|
|||
#include "storm/abstraction/MenuGameAbstractor.h" |
|||
#include "storm/abstraction/AbstractionInformation.h" |
|||
#include "storm/abstraction/MenuGame.h" |
|||
#include "storm/abstraction/prism/ModuleAbstractor.h" |
|||
|
|||
#include "storm/storage/dd/Add.h" |
|||
|
|||
#include "storm/abstraction/prism/AbstractProgram.h" |
|||
#include "storm/storage/expressions/Expression.h" |
|||
|
|||
namespace storm { |
|||
namespace utility { |
|||
namespace solver { |
|||
class SmtSolverFactory; |
|||
} |
|||
} |
|||
|
|||
namespace models { |
|||
namespace symbolic { |
|||
template<storm::dd::DdType Type, typename ValueType> |
|||
class StochasticTwoPlayerGame; |
|||
} |
|||
} |
|||
|
|||
namespace prism { |
|||
// Forward-declare concrete Program class. |
|||
class Program; |
|||
} |
|||
|
|||
namespace abstraction { |
|||
namespace prism { |
|||
|
|||
template <storm::dd::DdType DdType, typename ValueType> |
|||
class PrismMenuGameAbstractor : public MenuGameAbstractor<DdType, ValueType> { |
|||
public: |
|||
PrismMenuGameAbstractor(storm::prism::Program const& program, std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory = std::make_shared<storm::utility::solver::MathsatSmtSolverFactory>()); |
|||
/*! |
|||
* 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 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. |
|||
*/ |
|||
PrismMenuGameAbstractor(storm::prism::Program const& program, std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory = std::make_shared<storm::utility::solver::MathsatSmtSolverFactory>(), bool addAllGuards = false); |
|||
|
|||
virtual storm::abstraction::MenuGame<DdType, ValueType> abstract() override; |
|||
|
|||
virtual AbstractionInformation<DdType> const& getAbstractionInformation() const override; |
|||
virtual storm::expressions::Expression const& getGuard(uint64_t player1Choice) const override; |
|||
virtual std::map<storm::expressions::Variable, storm::expressions::Expression> getVariableUpdates(uint64_t player1Choice, uint64_t auxiliaryChoice) const override; |
|||
|
|||
virtual void refine(std::vector<storm::expressions::Expression> const& predicates) override; |
|||
PrismMenuGameAbstractor(PrismMenuGameAbstractor const&) = default; |
|||
PrismMenuGameAbstractor& operator=(PrismMenuGameAbstractor const&) = default; |
|||
PrismMenuGameAbstractor(PrismMenuGameAbstractor&&) = default; |
|||
PrismMenuGameAbstractor& operator=(PrismMenuGameAbstractor&&) = default; |
|||
|
|||
/*! |
|||
* Uses the current set of predicates to derive the abstract menu game in the form of an ADD. |
|||
* |
|||
* @return The abstract stochastic two player game. |
|||
*/ |
|||
MenuGame<DdType, ValueType> abstract(); |
|||
|
|||
/*! |
|||
* Retrieves information about the abstraction. |
|||
* |
|||
* @return The abstraction information object. |
|||
*/ |
|||
AbstractionInformation<DdType> const& getAbstractionInformation() const; |
|||
|
|||
/*! |
|||
* Retrieves the guard predicate of the given player 1 choice. |
|||
* |
|||
* @param player1Choice The choice for which to retrieve the guard. |
|||
* @return The guard of the player 1 choice. |
|||
*/ |
|||
storm::expressions::Expression const& getGuard(uint64_t player1Choice) const; |
|||
|
|||
/*! |
|||
* Retrieves a mapping from variables to expressions that define their updates wrt. to the given player |
|||
* 1 choice and auxiliary choice. |
|||
*/ |
|||
std::map<storm::expressions::Variable, storm::expressions::Expression> getVariableUpdates(uint64_t player1Choice, uint64_t auxiliaryChoice) const; |
|||
|
|||
/*! |
|||
* 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. |
|||
* |
|||
* @param predicate The predicate for which to retrieve the states. |
|||
* @return The BDD representing the set of states. |
|||
*/ |
|||
storm::dd::Bdd<DdType> getStates(storm::expressions::Expression const& predicate); |
|||
|
|||
/*! |
|||
* Refines the abstract program with the given predicates. |
|||
* |
|||
* @param predicates The new predicates. |
|||
*/ |
|||
void refine(std::vector<storm::expressions::Expression> const& predicates); |
|||
|
|||
/*! |
|||
* Exports the current state of the abstraction in the dot format to the given file. |
|||
* |
|||
* @param filename The name of the file to which to write the dot output. |
|||
* @param highlightStates A BDD characterizing states that will be highlighted. |
|||
* @param filter A filter that is applied to select which part of the game to export. |
|||
*/ |
|||
void exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStates, storm::dd::Bdd<DdType> const& filter) const; |
|||
|
|||
private: |
|||
/*! |
|||
* Builds the stochastic game representing the abstraction of the program. |
|||
* |
|||
* @return The stochastic game. |
|||
*/ |
|||
std::unique_ptr<MenuGame<DdType, ValueType>> buildGame(); |
|||
|
|||
/*! |
|||
* Decodes the given choice over the auxiliary and successor variables to a mapping from update indices |
|||
* to bit vectors representing the successors under these updates. |
|||
* |
|||
* @param choice The choice to decode. |
|||
*/ |
|||
std::map<uint_fast64_t, storm::storage::BitVector> decodeChoiceToUpdateSuccessorMapping(storm::dd::Bdd<DdType> const& choice) const; |
|||
|
|||
// The concrete program this abstract program refers to. |
|||
std::reference_wrapper<storm::prism::Program const> program; |
|||
|
|||
void exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStates, storm::dd::Bdd<DdType> const& filter) const override; |
|||
// A factory that can be used to create new SMT solvers. |
|||
std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory; |
|||
|
|||
private: |
|||
/// The abstract program that performs the actual abstraction. |
|||
AbstractProgram<DdType, ValueType> abstractProgram; |
|||
// An object containing all information about the abstraction like predicates and the corresponding DDs. |
|||
AbstractionInformation<DdType> abstractionInformation; |
|||
|
|||
// The abstract modules of the abstract program. |
|||
std::vector<ModuleAbstractor<DdType, ValueType>> modules; |
|||
|
|||
// A state-set abstractor used to determine the initial states of the abstraction. |
|||
StateSetAbstractor<DdType, ValueType> initialStateAbstractor; |
|||
|
|||
// A flag that stores whether all guards were added (which is relevant for determining the bottom states). |
|||
bool addedAllGuards; |
|||
|
|||
// An ADD characterizing the probabilities of commands and their updates. |
|||
storm::dd::Add<DdType, ValueType> commandUpdateProbabilitiesAdd; |
|||
|
|||
// The current game-based abstraction. |
|||
std::unique_ptr<MenuGame<DdType, ValueType>> currentGame; |
|||
|
|||
// A flag storing whether a refinement was performed. |
|||
bool refinementPerformed; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue