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