Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
PrismMenuGameAbstractor.cpp
Go to the documentation of this file.
2
16#include "storm/utility/dd.h"
19
20namespace storm::gbar {
21namespace abstraction {
22namespace prism {
23
25
26template<storm::dd::DdType DdType, typename ValueType>
28 std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory,
29 MenuGameAbstractorOptions const& options)
30 : program(program),
31 smtSolverFactory(smtSolverFactory),
32 abstractionInformation(program.getManager(), program.getAllExpressionVariables(), smtSolverFactory->create(program.getManager()),
33 AbstractionInformationOptions(options.constraints)),
34 modules(),
35 initialStateAbstractor(abstractionInformation, {program.getInitialStatesExpression()}, this->smtSolverFactory),
36 validBlockAbstractor(abstractionInformation, smtSolverFactory),
37 currentGame(nullptr),
38 refinementPerformed(false) {
39 // For now, we assume that there is a single module. If the program has more than one module, it needs
40 // to be flattened before the procedure.
41 STORM_LOG_THROW(program.getNumberOfModules() == 1, storm::exceptions::WrongFormatException,
42 "Cannot create abstract program from program containing too many modules.");
43
44 // Add all variables range expressions to the information object.
45 for (auto const& range : this->program.get().getAllRangeExpressions()) {
46 abstractionInformation.addConstraint(range);
47 initialStateAbstractor.constrain(range);
48 validBlockAbstractor.constrain(range);
49 }
50
51 uint_fast64_t totalNumberOfCommands = 0;
52 uint_fast64_t maximalUpdateCount = 0;
53 std::vector<storm::expressions::Expression> allGuards;
54 for (auto const& module : program.getModules()) {
55 for (auto const& command : module.getCommands()) {
56 maximalUpdateCount = std::max(maximalUpdateCount, static_cast<uint_fast64_t>(command.getNumberOfUpdates()));
57 }
58
59 totalNumberOfCommands += module.getNumberOfCommands();
60 }
61
62 // NOTE: currently we assume that 64 player 2 variables suffice, which corresponds to 2^64 possible
63 // choices. If for some reason this should not be enough, we could grow this vector dynamically, but
64 // odds are that it's impossible to treat such models in any event.
65 abstractionInformation.createEncodingVariables(static_cast<uint_fast64_t>(std::ceil(std::log2(totalNumberOfCommands))), 64,
66 static_cast<uint_fast64_t>(std::ceil(std::log2(maximalUpdateCount))));
67
68 // For each module of the concrete program, we create an abstract counterpart.
69 auto const& settings = storm::settings::getModule<storm::settings::modules::AbstractionSettings>();
70 bool useDecomposition = settings.isUseDecompositionSet();
71 restrictToValidBlocks = settings.getValidBlockMode() == storm::settings::modules::AbstractionSettings::ValidBlockMode::BlockEnumeration;
72 bool addPredicatesForValidBlocks = !restrictToValidBlocks;
73 bool debug = settings.isDebugSet();
74 for (auto const& module : program.getModules()) {
75 this->modules.emplace_back(module, abstractionInformation, this->smtSolverFactory, useDecomposition, addPredicatesForValidBlocks, debug);
76 }
77
78 // Retrieve the command-update probability ADD, so we can multiply it with the abstraction BDD later.
79 commandUpdateProbabilitiesAdd = modules.front().getCommandUpdateProbabilitiesAdd();
80}
81
82template<storm::dd::DdType DdType, typename ValueType>
84 return abstractionInformation.getDdManager();
85}
86
87template<storm::dd::DdType DdType, typename ValueType>
89 // Add the predicates to the global list of predicates and gather their indices.
90 std::vector<uint_fast64_t> predicateIndices;
91 for (auto const& predicate : command.getPredicates()) {
92 STORM_LOG_THROW(predicate.hasBooleanType(), storm::exceptions::InvalidArgumentException, "Expecting a predicate of type bool.");
93 predicateIndices.push_back(abstractionInformation.getOrAddPredicate(predicate));
94 }
95
96 // Refine all abstract modules.
97 for (auto& module : modules) {
98 module.refine(predicateIndices);
99 }
100
101 // Refine initial state abstractor.
102 initialStateAbstractor.refine(predicateIndices);
103
104 if (restrictToValidBlocks) {
105 // Refine the valid blocks.
106 validBlockAbstractor.refine(predicateIndices);
107 }
108
109 refinementPerformed |= !command.getPredicates().empty();
110}
111
112template<storm::dd::DdType DdType, typename ValueType>
114 if (refinementPerformed) {
115 currentGame = buildGame();
116 refinementPerformed = false;
117 }
118 return *currentGame;
119}
120
121template<storm::dd::DdType DdType, typename ValueType>
125
126template<storm::dd::DdType DdType, typename ValueType>
128 return modules.front().getGuard(player1Choice);
129}
130
131template<storm::dd::DdType DdType, typename ValueType>
133 return modules.front().getNumberOfUpdates(player1Choice);
134}
135
136template<storm::dd::DdType DdType, typename ValueType>
137std::map<storm::expressions::Variable, storm::expressions::Expression> PrismMenuGameAbstractor<DdType, ValueType>::getVariableUpdates(
138 uint64_t player1Choice, uint64_t auxiliaryChoice) const {
139 return modules.front().getVariableUpdates(player1Choice, auxiliaryChoice);
140}
141
142template<storm::dd::DdType DdType, typename ValueType>
143std::set<storm::expressions::Variable> const& PrismMenuGameAbstractor<DdType, ValueType>::getAssignedVariables(uint64_t player1Choice) const {
144 return modules.front().getAssignedVariables(player1Choice);
145}
146
147template<storm::dd::DdType DdType, typename ValueType>
149 return std::make_pair(0, modules.front().getCommands().size());
150}
151
152template<storm::dd::DdType DdType, typename ValueType>
154 return program.get().getInitialStatesExpression();
155}
156
157template<storm::dd::DdType DdType, typename ValueType>
159 storm::gbar::abstraction::ExpressionTranslator<DdType> translator(abstractionInformation,
160 smtSolverFactory->create(abstractionInformation.getExpressionManager()));
161 return translator.translate(expression);
162}
163
164template<storm::dd::DdType DdType, typename ValueType>
165std::unique_ptr<MenuGame<DdType, ValueType>> PrismMenuGameAbstractor<DdType, ValueType>::buildGame() {
166 // As long as there is only one module, we only build its game representation.
167 GameBddResult<DdType> game = modules.front().abstract();
168
169 // Construct a set of all unnecessary variables, so we can abstract from it.
170 std::set<storm::expressions::Variable> variablesToAbstract(abstractionInformation.getPlayer1VariableSet(abstractionInformation.getPlayer1VariableCount()));
171 std::set<storm::expressions::Variable> successorAndAuxVariables(abstractionInformation.getSuccessorVariables());
172 auto player2Variables = abstractionInformation.getPlayer2VariableSet(game.numberOfPlayer2Variables);
173 variablesToAbstract.insert(player2Variables.begin(), player2Variables.end());
174 auto auxVariables = abstractionInformation.getAuxVariableSet(0, abstractionInformation.getAuxVariableCount());
175 variablesToAbstract.insert(auxVariables.begin(), auxVariables.end());
176 successorAndAuxVariables.insert(auxVariables.begin(), auxVariables.end());
177
178 storm::utility::Stopwatch relevantStatesWatch(true);
179 storm::dd::Bdd<DdType> nonTerminalStates = this->abstractionInformation.getDdManager().getBddOne();
180 if (this->isRestrictToRelevantStatesSet()) {
181 // Compute which states are non-terminal.
182 for (auto const& expression : this->terminalStateExpressions) {
183 nonTerminalStates &= !this->getStates(expression);
184 }
185 if (this->hasTargetStateExpression()) {
186 nonTerminalStates &= !this->getStates(this->getTargetStateExpression());
187 }
188 }
189 relevantStatesWatch.stop();
190
191 storm::dd::Bdd<DdType> extendedTransitionRelation = nonTerminalStates && game.bdd;
192 storm::dd::Bdd<DdType> initialStates = initialStateAbstractor.getAbstractStates();
193 if (restrictToValidBlocks) {
194 STORM_LOG_DEBUG("Restricting to valid blocks.");
195 storm::dd::Bdd<DdType> validBlocks = validBlockAbstractor.getValidBlocks();
196
197 // Compute the choices with only valid successors so we can restrict the game to these.
198 auto choicesWithOnlyValidSuccessors =
199 !game.bdd.andExists(!validBlocks.swapVariables(abstractionInformation.getSourceSuccessorVariablePairs()), successorAndAuxVariables) &&
200 game.bdd.existsAbstract(successorAndAuxVariables);
201
202 // Restrict the proper parts.
203 extendedTransitionRelation &= validBlocks && choicesWithOnlyValidSuccessors;
204 initialStates &= validBlocks;
205 }
206
207 // Do a reachability analysis on the raw transition relation.
208 storm::dd::Bdd<DdType> transitionRelation = extendedTransitionRelation.existsAbstract(variablesToAbstract);
209 initialStates.addMetaVariables(abstractionInformation.getSourcePredicateVariables());
210 storm::dd::Bdd<DdType> reachableStates =
211 storm::utility::dd::computeReachableStates(initialStates, transitionRelation, abstractionInformation.getSourceVariables(),
212 abstractionInformation.getSuccessorVariables())
213 .first;
214
215 relevantStatesWatch.start();
216 if (this->isRestrictToRelevantStatesSet() && this->hasTargetStateExpression()) {
217 // Get the target state BDD.
218 storm::dd::Bdd<DdType> targetStates = reachableStates && this->getStates(this->getTargetStateExpression());
219
220 // In the presence of target states, we keep only states that can reach the target states.
222 targetStates, reachableStates, transitionRelation, abstractionInformation.getSourceVariables(), abstractionInformation.getSuccessorVariables());
223
224 // Include all successors of reachable states, because the backward search otherwise potentially
225 // cuts probability 0 choices of these states.
226 reachableStates |=
227 (reachableStates && !targetStates)
228 .relationalProduct(transitionRelation, abstractionInformation.getSourceVariables(), abstractionInformation.getSuccessorVariables());
229
230 // Restrict transition relation to relevant fragment for computation of deadlock states.
231 transitionRelation &= reachableStates && reachableStates.swapVariables(abstractionInformation.getExtendedSourceSuccessorVariablePairs());
232
233 relevantStatesWatch.stop();
234 STORM_LOG_TRACE("Restricting to relevant states took " << relevantStatesWatch.getTimeInMilliseconds() << "ms.");
235 }
236
237 // Find the deadlock states in the model. Note that this does not find the 'deadlocks' in bottom states,
238 // as the bottom states are not contained in the reachable states.
239 storm::dd::Bdd<DdType> deadlockStates = transitionRelation.existsAbstract(abstractionInformation.getSuccessorVariables());
240 deadlockStates = reachableStates && !deadlockStates;
241
242 // If there are deadlock states, we fix them now.
243 storm::dd::Add<DdType, ValueType> deadlockTransitions = abstractionInformation.getDdManager().template getAddZero<ValueType>();
244 if (!deadlockStates.isZero()) {
245 deadlockTransitions = (deadlockStates && abstractionInformation.getAllPredicateIdentities() &&
246 abstractionInformation.encodePlayer1Choice(0, abstractionInformation.getPlayer1VariableCount()) &&
247 abstractionInformation.encodePlayer2Choice(0, 0, game.numberOfPlayer2Variables) &&
248 abstractionInformation.encodeAux(0, 0, abstractionInformation.getAuxVariableCount()))
249 .template toAdd<ValueType>();
250 }
251
252 // Compute bottom states and the appropriate transitions if necessary.
253 BottomStateResult<DdType> bottomStateResult(abstractionInformation.getDdManager().getBddZero(), abstractionInformation.getDdManager().getBddZero());
254 bottomStateResult = modules.front().getBottomStateTransitions(reachableStates, game.numberOfPlayer2Variables);
255 bool hasBottomStates = !bottomStateResult.states.isZero();
256
257 // Construct the transition matrix by cutting away the transitions of unreachable states.
258 // Note that we also restrict the successor states of transitions, because there might be successors
259 // that are not in the set of relevant states we restrict to.
260 storm::dd::Add<DdType, ValueType> transitionMatrix =
261 (extendedTransitionRelation && reachableStates && reachableStates.swapVariables(abstractionInformation.getSourceSuccessorVariablePairs()))
262 .template toAdd<ValueType>();
263 transitionMatrix *= commandUpdateProbabilitiesAdd;
264 transitionMatrix += deadlockTransitions;
265
266 // Extend the current game information with the 'non-bottom' tag before potentially adding bottom state transitions.
267 transitionMatrix *=
268 (abstractionInformation.getBottomStateBdd(true, true) && abstractionInformation.getBottomStateBdd(false, true)).template toAdd<ValueType>();
269 reachableStates &= abstractionInformation.getBottomStateBdd(true, true);
270 initialStates &= abstractionInformation.getBottomStateBdd(true, true);
271
272 // If there are bottom transitions, exnted the transition matrix and reachable states now.
273 if (hasBottomStates) {
274 transitionMatrix += bottomStateResult.transitions.template toAdd<ValueType>();
275 reachableStates |= bottomStateResult.states;
276 }
277
278 std::set<storm::expressions::Variable> allNondeterminismVariables = player2Variables;
279 allNondeterminismVariables.insert(abstractionInformation.getPlayer1Variables().begin(), abstractionInformation.getPlayer1Variables().end());
280
281 std::set<storm::expressions::Variable> allSourceVariables(abstractionInformation.getSourceVariables());
282 allSourceVariables.insert(abstractionInformation.getBottomStateVariable(true));
283 std::set<storm::expressions::Variable> allSuccessorVariables(abstractionInformation.getSuccessorVariables());
284 allSuccessorVariables.insert(abstractionInformation.getBottomStateVariable(false));
285
286 return std::make_unique<MenuGame<DdType, ValueType>>(
287 abstractionInformation.getDdManagerAsSharedPointer(), reachableStates, initialStates, abstractionInformation.getDdManager().getBddZero(),
288 transitionMatrix, bottomStateResult.states, allSourceVariables, allSuccessorVariables, abstractionInformation.getExtendedSourceSuccessorVariablePairs(),
289 std::set<storm::expressions::Variable>(abstractionInformation.getPlayer1Variables().begin(), abstractionInformation.getPlayer1Variables().end()),
290 player2Variables, allNondeterminismVariables, auxVariables, abstractionInformation.getPredicateToBddMap());
291}
292
293template<storm::dd::DdType DdType, typename ValueType>
294void PrismMenuGameAbstractor<DdType, ValueType>::exportToDot(std::string const& filename, storm::dd::Bdd<DdType> const& highlightStates,
295 storm::dd::Bdd<DdType> const& filter) const {
296 this->exportToDot(*currentGame, filename, highlightStates, filter);
297}
298
299template<storm::dd::DdType DdType, typename ValueType>
301 return abstractionInformation.getNumberOfPredicates();
302}
303
304template<storm::dd::DdType DdType, typename ValueType>
306 terminalStateExpressions.emplace_back(expression);
307}
308
309template<storm::dd::DdType DdType, typename ValueType>
311 for (auto& module : modules) {
312 module.notifyGuardsArePredicates();
313 }
314}
315
316// Explicitly instantiate the class.
320} // namespace prism
321} // namespace abstraction
322} // namespace storm::gbar
Bdd< LibraryType > existsAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Existentially abstracts from the given meta variables.
Definition Bdd.cpp:172
bool isZero() const
Retrieves whether this DD represents the constant zero function.
Definition Bdd.cpp:541
Bdd< LibraryType > andExists(Bdd< LibraryType > const &other, std::set< storm::expressions::Variable > const &existentialVariables) const
Computes the logical and of the current and the given BDD and existentially abstracts from the given ...
Definition Bdd.cpp:190
Bdd< LibraryType > swapVariables(std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const &metaVariablePairs) const
Swaps the given pairs of meta variables in the BDD.
Definition Bdd.cpp:296
void addMetaVariables(std::set< storm::expressions::Variable > const &metaVariables)
Adds the given set of meta variables to the DD.
Definition Dd.cpp:43
DdManager< LibraryType > & getDdManager() const
Retrieves the manager that is responsible for this DD.
Definition Dd.cpp:38
storm::dd::Bdd< DdType > translate(storm::expressions::Expression const &expression)
This class represents a discrete-time stochastic two-player game.
Definition MenuGame.h:14
std::vector< storm::expressions::Expression > const & getPredicates() const
virtual void exportToDot(std::string const &filename, storm::dd::Bdd< DdType > const &highlightStates, storm::dd::Bdd< DdType > const &filter) const override
Exports the current state of the abstraction in the dot format to the given file.
virtual uint64_t getNumberOfPredicates() const override
Retrieves the number of predicates currently in use.
virtual void notifyGuardsArePredicates() override
Notifies the abstractor that the guards are predicates, which may be used to improve the bottom state...
AbstractionInformation< DdType > const & getAbstractionInformation() const override
Retrieves information about the abstraction.
std::map< storm::expressions::Variable, storm::expressions::Expression > getVariableUpdates(uint64_t player1Choice, uint64_t auxiliaryChoice) const override
Retrieves a mapping from variables to expressions that define their updates wrt.
virtual uint64_t getNumberOfUpdates(uint64_t player1Choice) const override
Retrieves the number of updates of the specified player 1 choice.
storm::expressions::Expression const & getGuard(uint64_t player1Choice) const override
Retrieves the guard predicate of the given player 1 choice.
PrismMenuGameAbstractor(storm::prism::Program const &program, std::shared_ptr< storm::utility::solver::SmtSolverFactory > const &smtSolverFactory, MenuGameAbstractorOptions const &options=MenuGameAbstractorOptions())
Constructs an abstractor for the given program.
std::pair< uint64_t, uint64_t > getPlayer1ChoiceRange() const override
Retrieves the range of player 1 choices.
virtual void refine(RefinementCommand const &command) override
Performs the given refinement command.
MenuGame< DdType, ValueType > abstract() override
Uses the current set of predicates to derive the abstract menu game in the form of an ADD.
storm::expressions::Expression getInitialExpression() const override
Retrieves the expression that characterizes the initial states.
storm::dd::DdManager< DdType > const & getDdManager() const override
storm::dd::Bdd< DdType > getStates(storm::expressions::Expression const &expression) override
Retrieves a BDD that characterizes the states corresponding to the given expression.
virtual void addTerminalStates(storm::expressions::Expression const &expression) override
Adds the expression to the ones characterizing terminal states, i.e.
virtual std::set< storm::expressions::Variable > const & getAssignedVariables(uint64_t player1Choice) const override
Retrieves the variables assigned by the given player 1 choice.
storm::expressions::Expression getInitialStatesExpression() const
Retrieves an expression characterizing the initial states.
Definition Program.cpp:662
This class represents the settings for the abstraction procedures.
A class that provides convenience operations to display run times.
Definition Stopwatch.h:14
#define STORM_LOG_DEBUG(message)
Definition logging.h:18
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
storm::dd::Bdd< Type > computeBackwardsReachableStates(storm::dd::Bdd< Type > const &initialStates, storm::dd::Bdd< Type > const &constraintStates, storm::dd::Bdd< Type > const &transitions, std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables)
Definition dd.cpp:50
std::pair< storm::dd::Bdd< Type >, uint64_t > computeReachableStates(storm::dd::Bdd< Type > const &initialStates, storm::dd::Bdd< Type > const &transitions, std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables)
Definition dd.cpp:13