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