Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
MenuGameRefiner.cpp
Go to the documentation of this file.
2
14#include "storm/utility/dd.h"
16
17namespace storm::gbar {
18namespace abstraction {
19
20RefinementPredicates::RefinementPredicates(Source const& source, std::vector<storm::expressions::Expression> const& predicates)
21 : source(source), predicates(predicates) {
22 // Intentionally left empty.
23}
24
28
29std::vector<storm::expressions::Expression> const& RefinementPredicates::getPredicates() const {
30 return predicates;
31}
32
33void RefinementPredicates::addPredicates(std::vector<storm::expressions::Expression> const& newPredicates) {
34 this->predicates.insert(this->predicates.end(), newPredicates.begin(), newPredicates.end());
35}
36
37template<storm::dd::DdType Type, typename ValueType>
43
44template<storm::dd::DdType Type>
50
51template<storm::dd::DdType Type, typename ValueType>
58
59template<storm::dd::DdType Type, typename ValueType>
60MenuGameRefiner<Type, ValueType>::MenuGameRefiner(MenuGameAbstractor<Type, ValueType>& abstractor, std::unique_ptr<storm::solver::SmtSolver>&& smtSolver,
61 AbstractionOptions const& options)
62 : abstractor(abstractor),
63 useInterpolation(options.useInterpolation),
64 splitAll(options.splitMode == SplitMode::All),
65 splitPredicates(options.splitMode == SplitMode::NonGuard),
66 rankPredicates(options.rankRefinementPredicates),
67 addPredicatesEagerly(options.useEagerRefinement),
68 addedAllGuardsFlag(false),
69 refinementPredicatesToInject(options.injectedRefinementPredicates),
70 pivotSelectionHeuristic(options.pivotSelectionHeuristic),
71 splitter(),
72 equivalenceChecker(std::move(smtSolver)) {
73 equivalenceChecker.addConstraints(abstractor.getAbstractionInformation().getConstraints());
74 if (options.addAllGuards) {
75 std::vector<storm::expressions::Expression> guards;
76
77 std::pair<uint64_t, uint64_t> player1Choices = this->abstractor.get().getPlayer1ChoiceRange();
78 for (uint64_t index = player1Choices.first; index < player1Choices.second; ++index) {
79 storm::expressions::Expression guard = this->abstractor.get().getGuard(index);
80 if (!guard.isTrue() && !guard.isFalse()) {
81 guards.push_back(guard);
82 }
83 }
84 performRefinement(createGlobalRefinement(preprocessPredicates(guards, RefinementPredicates::Source::InitialGuard)));
85
86 this->abstractor.get().notifyGuardsArePredicates();
87 addedAllGuardsFlag = true;
88 }
89 if (options.addAllInitialExpressions) {
90 storm::expressions::Expression initialExpression = this->abstractor.get().getInitialExpression();
91 performRefinement(createGlobalRefinement(preprocessPredicates({initialExpression}, RefinementPredicates::Source::InitialExpression)));
92 }
93}
94
95template<storm::dd::DdType Type, typename ValueType>
96void MenuGameRefiner<Type, ValueType>::refine(std::vector<storm::expressions::Expression> const& predicates, bool allowInjection) const {
97 performRefinement(createGlobalRefinement(preprocessPredicates(predicates, RefinementPredicates::Source::Manual)), allowInjection);
98}
99
100template<storm::dd::DdType Type, typename ValueType>
102 storm::dd::Bdd<Type> const& transitionFilter) {
103 storm::dd::Add<Type, ValueType> maxProbabilities = game.getInitialStates().template toAdd<ValueType>();
104
106 storm::dd::Bdd<Type> spanningTree = game.getManager().getBddZero();
107
108 storm::dd::Add<Type, ValueType> transitionMatrix =
109 ((transitionFilter && game.getExtendedTransitionMatrix().maxAbstractRepresentative(game.getProbabilisticBranchingVariables()))
110 .template toAdd<ValueType>() *
113
114 std::set<storm::expressions::Variable> variablesToAbstract(game.getRowVariables());
115 variablesToAbstract.insert(game.getPlayer1Variables().begin(), game.getPlayer1Variables().end());
116 variablesToAbstract.insert(game.getProbabilisticBranchingVariables().begin(), game.getProbabilisticBranchingVariables().end());
117 while (!border.isZero()) {
118 // Determine the new maximal probabilities to all states.
119 storm::dd::Add<Type, ValueType> tmp = border.template toAdd<ValueType>() * transitionMatrix * maxProbabilities;
120 storm::dd::Bdd<Type> newMaxProbabilityChoices = tmp.maxAbstractRepresentative(variablesToAbstract);
121 storm::dd::Add<Type, ValueType> newMaxProbabilities = tmp.maxAbstract(variablesToAbstract).swapVariables(game.getRowColumnMetaVariablePairs());
122
123 // Determine the probability values for which states strictly increased.
124 storm::dd::Bdd<Type> updateStates = newMaxProbabilities.greater(maxProbabilities);
125 maxProbabilities = updateStates.ite(newMaxProbabilities, maxProbabilities);
126
127 // Delete all edges in the spanning tree that lead to states that need to be updated.
128 spanningTree &= ((!updateStates).swapVariables(game.getRowColumnMetaVariablePairs()));
129
130 // Add all edges that achieve the new maximal value to the spanning tree.
131 spanningTree |= updateStates.swapVariables(game.getRowColumnMetaVariablePairs()) && newMaxProbabilityChoices;
132
133 // Continue exploration from states that have been updated.
134 border = updateStates;
135 }
136
137 return SymbolicMostProbablePathsResult<Type, ValueType>(maxProbabilities, spanningTree);
138}
139
140template<storm::dd::DdType Type, typename ValueType>
143 PivotStateCandidatesResult<Type> const& pivotStateCandidateResult,
144 boost::optional<SymbolicQualitativeGameResultMinMax<Type>> const& qualitativeResult,
145 boost::optional<SymbolicQuantitativeGameResultMinMax<Type, ValueType>> const& quantitativeResult) {
146 // Get easy access to strategies.
147 storm::dd::Bdd<Type> minPlayer1Strategy;
148 storm::dd::Bdd<Type> minPlayer2Strategy;
149 storm::dd::Bdd<Type> maxPlayer1Strategy;
150 storm::dd::Bdd<Type> maxPlayer2Strategy;
151 if (qualitativeResult) {
152 minPlayer1Strategy = qualitativeResult.get().prob0Min.getPlayer1Strategy();
153 minPlayer2Strategy = qualitativeResult.get().prob0Min.getPlayer2Strategy();
154 maxPlayer1Strategy = qualitativeResult.get().prob1Max.getPlayer1Strategy();
155 maxPlayer2Strategy = qualitativeResult.get().prob1Max.getPlayer2Strategy();
156 } else if (quantitativeResult) {
157 minPlayer1Strategy = quantitativeResult.get().min.getPlayer1Strategy();
158 minPlayer2Strategy = quantitativeResult.get().min.getPlayer2Strategy();
159 maxPlayer1Strategy = quantitativeResult.get().max.getPlayer1Strategy();
160 maxPlayer2Strategy = quantitativeResult.get().max.getPlayer2Strategy();
161 } else {
162 STORM_LOG_ASSERT(false, "Either qualitative or quantitative result is required.");
163 }
164
165 storm::dd::Bdd<Type> pivotStates = pivotStateCandidateResult.pivotStates;
166
168 // Set up used variables.
169 storm::dd::Bdd<Type> initialStates = game.getInitialStates();
170 std::set<storm::expressions::Variable> const& rowVariables = game.getRowVariables();
171 std::set<storm::expressions::Variable> const& columnVariables = game.getColumnVariables();
172 storm::dd::Bdd<Type> transitionsMin = pivotStateCandidateResult.reachableTransitionsMin;
173 storm::dd::Bdd<Type> transitionsMax = pivotStateCandidateResult.reachableTransitionsMax;
174 storm::dd::Bdd<Type> frontierMin = initialStates;
175 storm::dd::Bdd<Type> frontierMax = initialStates;
176 storm::dd::Bdd<Type> frontierPivotStates = frontierMin && pivotStates;
177
178 // Check whether we have pivot states on the very first level.
179 uint64_t level = 0;
180 bool foundPivotState = !frontierPivotStates.isZero();
181 if (foundPivotState) {
182 STORM_LOG_TRACE("Picked pivot state from " << frontierPivotStates.getNonZeroCount() << " candidates on level " << level << ", "
183 << pivotStates.getNonZeroCount() << " candidates in total.");
184 return SymbolicPivotStateResult<Type, ValueType>(frontierPivotStates.existsAbstractRepresentative(rowVariables),
185 storm::OptimizationDirection::Minimize);
186 } else {
187 // Otherwise, we perform a simulatenous BFS in the sense that we make one step in both the min and max
188 // transitions and check for pivot states we encounter.
189 while (!foundPivotState) {
190 frontierMin = frontierMin.relationalProduct(transitionsMin, rowVariables, columnVariables);
191 frontierMax = frontierMax.relationalProduct(transitionsMax, rowVariables, columnVariables);
192 ++level;
193
194 storm::dd::Bdd<Type> frontierMinPivotStates = frontierMin && pivotStates;
195 storm::dd::Bdd<Type> frontierMaxPivotStates = frontierMax && pivotStates;
196 uint64_t numberOfPivotStateCandidatesOnLevel = frontierMinPivotStates.getNonZeroCount() + frontierMaxPivotStates.getNonZeroCount();
197
198 if (!frontierMinPivotStates.isZero() || !frontierMaxPivotStates.isZero()) {
199 if (quantitativeResult) {
200 storm::dd::Add<Type, ValueType> frontierMinPivotStatesAdd = frontierMinPivotStates.template toAdd<ValueType>();
201 storm::dd::Add<Type, ValueType> frontierMaxPivotStatesAdd = frontierMaxPivotStates.template toAdd<ValueType>();
202
204 frontierMinPivotStatesAdd * quantitativeResult.get().max.values - frontierMinPivotStatesAdd * quantitativeResult.get().min.values;
206 frontierMaxPivotStatesAdd * quantitativeResult.get().max.values - frontierMaxPivotStatesAdd * quantitativeResult.get().min.values;
207
208 ValueType diffValue;
210 if (diffMin.getMax() >= diffMax.getMax()) {
211 direction = storm::OptimizationDirection::Minimize;
212 diffValue = diffMin.getMax();
213 } else {
214 direction = storm::OptimizationDirection::Maximize;
215 diffValue = diffMax.getMax();
216 }
217
218 STORM_LOG_TRACE("Picked pivot state with difference " << diffValue << " from " << numberOfPivotStateCandidatesOnLevel
219 << " candidates on level " << level << ", " << pivotStates.getNonZeroCount()
220 << " candidates in total.");
221 return SymbolicPivotStateResult<Type, ValueType>(direction == storm::OptimizationDirection::Minimize
222 ? diffMin.maxAbstractRepresentative(rowVariables)
223 : diffMax.maxAbstractRepresentative(rowVariables),
224 direction);
225 } else {
226 STORM_LOG_TRACE("Picked pivot state from " << numberOfPivotStateCandidatesOnLevel << " candidates on level " << level << ", "
227 << pivotStates.getNonZeroCount() << " candidates in total.");
228
230 if (!frontierMinPivotStates.isZero()) {
231 direction = storm::OptimizationDirection::Minimize;
232 } else {
233 direction = storm::OptimizationDirection::Maximize;
234 }
235
236 return SymbolicPivotStateResult<Type, ValueType>(direction == storm::OptimizationDirection::Minimize
237 ? frontierMinPivotStates.existsAbstractRepresentative(rowVariables)
238 : frontierMaxPivotStates.existsAbstractRepresentative(rowVariables),
239 direction);
240 }
241 }
242 }
243 }
244 } else {
245 // Compute the most probable paths to the reachable states and the corresponding spanning trees.
246 SymbolicMostProbablePathsResult<Type, ValueType> minSymbolicMostProbablePathsResult =
247 getMostProbablePathSpanningTree(game, minPlayer1Strategy && minPlayer2Strategy);
248 SymbolicMostProbablePathsResult<Type, ValueType> maxSymbolicMostProbablePathsResult =
249 getMostProbablePathSpanningTree(game, maxPlayer1Strategy && maxPlayer2Strategy);
250 storm::dd::Bdd<Type> selectedPivotState;
251
252 storm::dd::Add<Type, ValueType> score = pivotStates.template toAdd<ValueType>() * minSymbolicMostProbablePathsResult.maxProbabilities.maximum(
253 maxSymbolicMostProbablePathsResult.maxProbabilities);
254
255 if (heuristic == PivotSelectionHeuristic::MaxWeightedDeviation && quantitativeResult) {
256 score = score * (quantitativeResult.get().max.values - quantitativeResult.get().min.values);
257 }
258 selectedPivotState = score.maxAbstractRepresentative(game.getRowVariables());
259 STORM_LOG_TRACE("Selected pivot state with score " << score.getMax() << ".");
260
261 storm::OptimizationDirection fromDirection = storm::OptimizationDirection::Minimize;
262 storm::dd::Add<Type, ValueType> selectedPivotStateAsAdd = selectedPivotState.template toAdd<ValueType>();
263 if ((selectedPivotStateAsAdd * maxSymbolicMostProbablePathsResult.maxProbabilities).getMax() >
264 (selectedPivotStateAsAdd * minSymbolicMostProbablePathsResult.maxProbabilities).getMax()) {
265 fromDirection = storm::OptimizationDirection::Maximize;
266 }
267
269 selectedPivotState, fromDirection,
270 fromDirection == storm::OptimizationDirection::Minimize ? minSymbolicMostProbablePathsResult : maxSymbolicMostProbablePathsResult);
271 }
272
273 STORM_LOG_ASSERT(false, "This point must not be reached, because then no pivot state could be found.");
274 return SymbolicPivotStateResult<Type, ValueType>(storm::dd::Bdd<Type>(), storm::OptimizationDirection::Minimize);
275}
276
277template<storm::dd::DdType Type, typename ValueType>
278RefinementPredicates MenuGameRefiner<Type, ValueType>::derivePredicatesFromDifferingChoices(storm::dd::Bdd<Type> const& player1Choice,
279 storm::dd::Bdd<Type> const& lowerChoice,
280 storm::dd::Bdd<Type> const& upperChoice) const {
281 // Prepare result.
283 bool fromGuard = false;
284
285 // Get abstraction information for easier access.
286 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
287
288 // Decode the index of the command chosen by player 1.
289 storm::dd::Add<Type, ValueType> player1ChoiceAsAdd = player1Choice.template toAdd<ValueType>();
290 auto pl1It = player1ChoiceAsAdd.begin();
291 uint_fast64_t player1Index = abstractionInformation.decodePlayer1Choice((*pl1It).first, abstractionInformation.getPlayer1VariableCount());
292
293 // Check whether there are bottom states in the game and whether one of the choices actually picks the
294 // bottom state as the successor.
295 bool buttomStateSuccessor =
296 !((abstractionInformation.getBottomStateBdd(false, false) && lowerChoice) || (abstractionInformation.getBottomStateBdd(false, false) && upperChoice))
297 .isZero();
298
299 // If one of the choices picks the bottom state, the new predicate is based on the guard of the appropriate
300 // command (that is the player 1 choice).
301 if (buttomStateSuccessor) {
302 STORM_LOG_TRACE("One of the successors is a bottom state, taking a guard as a new predicate.");
303 newPredicate = abstractor.get().getGuard(player1Index);
304 fromGuard = true;
305 STORM_LOG_DEBUG("Derived new predicate (based on guard): " << newPredicate);
306 } else {
307 STORM_LOG_TRACE("No bottom state successor. Deriving a new predicate using weakest precondition.");
308
309 // Decode both choices to explicit mappings.
310 std::map<uint64_t, std::pair<storm::storage::BitVector, ValueType>> lowerChoiceUpdateToSuccessorMapping =
311 abstractionInformation.template decodeChoiceToUpdateSuccessorMapping<ValueType>(lowerChoice);
312 std::map<uint64_t, std::pair<storm::storage::BitVector, ValueType>> upperChoiceUpdateToSuccessorMapping =
313 abstractionInformation.template decodeChoiceToUpdateSuccessorMapping<ValueType>(upperChoice);
315 lowerChoiceUpdateToSuccessorMapping.size() == upperChoiceUpdateToSuccessorMapping.size(),
316 "Mismatching sizes after decode (" << lowerChoiceUpdateToSuccessorMapping.size() << " vs. " << upperChoiceUpdateToSuccessorMapping.size() << ").");
317
318 // First, sort updates according to probability mass.
319 std::vector<std::pair<uint64_t, ValueType>> updateIndicesAndMasses;
320 for (auto const& entry : lowerChoiceUpdateToSuccessorMapping) {
321 updateIndicesAndMasses.emplace_back(entry.first, entry.second.second);
322 }
323 std::sort(updateIndicesAndMasses.begin(), updateIndicesAndMasses.end(),
324 [](std::pair<uint64_t, ValueType> const& a, std::pair<uint64_t, ValueType> const& b) { return a.second > b.second; });
325
326 // Now find the update with the highest probability mass among all deviating updates. More specifically,
327 // we determine the set of predicate indices for which there is a deviation.
328 std::set<uint64_t> deviationPredicates;
329 uint64_t orderedUpdateIndex = 0;
330 std::vector<storm::expressions::Expression> possibleRefinementPredicates;
331 for (; orderedUpdateIndex < updateIndicesAndMasses.size(); ++orderedUpdateIndex) {
332 storm::storage::BitVector const& lower = lowerChoiceUpdateToSuccessorMapping.at(updateIndicesAndMasses[orderedUpdateIndex].first).first;
333 storm::storage::BitVector const& upper = upperChoiceUpdateToSuccessorMapping.at(updateIndicesAndMasses[orderedUpdateIndex].first).first;
334
335 bool deviates = lower != upper;
336 if (deviates) {
337 std::map<storm::expressions::Variable, storm::expressions::Expression> variableUpdates =
338 abstractor.get().getVariableUpdates(player1Index, updateIndicesAndMasses[orderedUpdateIndex].first);
339
340 for (uint64_t predicateIndex = 0; predicateIndex < lower.size(); ++predicateIndex) {
341 if (lower[predicateIndex] != upper[predicateIndex]) {
342 possibleRefinementPredicates.push_back(
343 abstractionInformation.getPredicateByIndex(predicateIndex).substitute(variableUpdates).simplify());
344 if (!rankPredicates) {
345 break;
346 }
347 }
348 }
349 break;
350 }
351 }
352
353 STORM_LOG_ASSERT(!possibleRefinementPredicates.empty(), "Expected refinement predicates.");
354
355 STORM_LOG_TRACE("Possible refinement predicates:");
356 for (auto const& pred : possibleRefinementPredicates) {
357 STORM_LOG_TRACE(pred);
358 }
359
360 if (rankPredicates) {
361 // Since we can choose any of the deviation predicates to perform the split, we go through the remaining
362 // updates and build all deviation predicates. We can then check whether any of the possible refinement
363 // predicates also eliminates another deviation.
364 std::vector<storm::expressions::Expression> otherRefinementPredicates;
365 for (; orderedUpdateIndex < updateIndicesAndMasses.size(); ++orderedUpdateIndex) {
366 storm::storage::BitVector const& lower = lowerChoiceUpdateToSuccessorMapping.at(updateIndicesAndMasses[orderedUpdateIndex].first).first;
367 storm::storage::BitVector const& upper = upperChoiceUpdateToSuccessorMapping.at(updateIndicesAndMasses[orderedUpdateIndex].first).first;
368
369 bool deviates = lower != upper;
370 if (deviates) {
371 std::map<storm::expressions::Variable, storm::expressions::Expression> newVariableUpdates =
372 abstractor.get().getVariableUpdates(player1Index, updateIndicesAndMasses[orderedUpdateIndex].first);
373 for (uint64_t predicateIndex = 0; predicateIndex < lower.size(); ++predicateIndex) {
374 if (lower[predicateIndex] != upper[predicateIndex]) {
375 otherRefinementPredicates.push_back(
376 abstractionInformation.getPredicateByIndex(predicateIndex).substitute(newVariableUpdates).simplify());
377 }
378 }
379 }
380 }
381
382 // Finally, go through the refinement predicates and see how many deviations they cover.
383 std::vector<uint64_t> refinementPredicateIndexToCount(possibleRefinementPredicates.size(), 0);
384 for (uint64_t index = 0; index < possibleRefinementPredicates.size(); ++index) {
385 refinementPredicateIndexToCount[index] = 1;
386 }
387 for (auto const& otherPredicate : otherRefinementPredicates) {
388 for (uint64_t index = 0; index < possibleRefinementPredicates.size(); ++index) {
389 if (equivalenceChecker.areEquivalentModuloNegation(otherPredicate, possibleRefinementPredicates[index])) {
390 ++refinementPredicateIndexToCount[index];
391 }
392 }
393 }
394
395 // Find predicate that covers the most deviations.
396 uint64_t chosenPredicateIndex = 0;
397 for (uint64_t index = 0; index < possibleRefinementPredicates.size(); ++index) {
398 if (refinementPredicateIndexToCount[index] > refinementPredicateIndexToCount[chosenPredicateIndex]) {
399 chosenPredicateIndex = index;
400 }
401 }
402 newPredicate = possibleRefinementPredicates[chosenPredicateIndex];
403 STORM_LOG_DEBUG("Derived new predicate (based on weakest-precondition): " << newPredicate << ", (equivalent to "
404 << (refinementPredicateIndexToCount[chosenPredicateIndex] - 1)
405 << " other refinement predicates).");
406 } else {
407 newPredicate = possibleRefinementPredicates.front();
408 STORM_LOG_DEBUG("Derived new predicate (based on weakest-precondition): " << newPredicate << ".");
409 }
410
411 STORM_LOG_ASSERT(newPredicate.isInitialized(), "Could not derive new predicate as there is no deviation.");
412 }
413
414 return RefinementPredicates(fromGuard ? RefinementPredicates::Source::Guard : RefinementPredicates::Source::WeakestPrecondition, {newPredicate});
415}
416
417template<storm::dd::DdType Type, typename ValueType>
418RefinementPredicates MenuGameRefiner<Type, ValueType>::derivePredicatesFromChoice(storm::gbar::abstraction::MenuGame<Type, ValueType> const& game,
419 storm::dd::Bdd<Type> const& pivotState,
420 storm::dd::Bdd<Type> const& player1Choice, storm::dd::Bdd<Type> const& choice,
421 storm::dd::Bdd<Type> const& choiceSuccessors) const {
422 // Prepare result.
423 storm::expressions::Expression newPredicate;
424 bool fromGuard = false;
425
426 // Get abstraction information for easier access.
427 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
428
429 // Decode the index of the command chosen by player 1.
430 storm::dd::Add<Type, ValueType> player1ChoiceAsAdd = player1Choice.template toAdd<ValueType>();
431 auto pl1It = player1ChoiceAsAdd.begin();
432 uint_fast64_t player1Index = abstractionInformation.decodePlayer1Choice((*pl1It).first, abstractionInformation.getPlayer1VariableCount());
433
434 // Check whether there are bottom states in the game and whether the choice actually picks the bottom state
435 // as the successor.
436 bool buttomStateSuccessor = !((abstractionInformation.getBottomStateBdd(false, false) && choiceSuccessors)).isZero();
437
438 std::vector<storm::expressions::Expression> possibleRefinementPredicates;
439
440 // If the choice picks the bottom state, the new predicate is based on the guard of the appropriate command
441 // (that is the player 1 choice).
442 if (buttomStateSuccessor) {
443 STORM_LOG_TRACE("One of the successors is a bottom state, taking a guard as a new predicate.");
444 possibleRefinementPredicates.emplace_back(abstractor.get().getGuard(player1Index));
445 fromGuard = true;
446 STORM_LOG_DEBUG("Derived new predicate (based on guard): " << possibleRefinementPredicates.back());
447 } else {
448 STORM_LOG_TRACE("No bottom state successor. Deriving a new predicate using weakest precondition.");
449
450 // Decode the choice successors.
451 std::map<uint64_t, std::pair<storm::storage::BitVector, ValueType>> choiceUpdateToSuccessorMapping =
452 abstractionInformation.template decodeChoiceToUpdateSuccessorMapping<ValueType>(choiceSuccessors);
453 std::vector<std::pair<uint64_t, ValueType>> sortedChoiceUpdateIndicesAndMasses;
454 for (auto const& e : choiceUpdateToSuccessorMapping) {
455 sortedChoiceUpdateIndicesAndMasses.emplace_back(e.first, e.second.second);
456 }
457 std::sort(sortedChoiceUpdateIndicesAndMasses.begin(), sortedChoiceUpdateIndicesAndMasses.end(),
458 [](std::pair<uint64_t, ValueType> const& a, std::pair<uint64_t, ValueType> const& b) { return a.second > b.second; });
459
460 // Compute all other (not taken) choices.
461 std::set<storm::expressions::Variable> variablesToAbstract = game.getRowVariables();
462 variablesToAbstract.insert(game.getPlayer1Variables().begin(), game.getPlayer1Variables().end());
463
464 storm::dd::Bdd<Type> otherChoices =
465 (pivotState && !choice && player1Choice && game.getExtendedTransitionMatrix().toBdd()).existsAbstract(variablesToAbstract);
466 STORM_LOG_ASSERT(!otherChoices.isZero(), "Expected other choices.");
467
468 // Decode the other choices.
469 std::vector<std::map<uint64_t, std::pair<storm::storage::BitVector, ValueType>>> otherChoicesUpdateToSuccessorMappings =
470 abstractionInformation.template decodeChoicesToUpdateSuccessorMapping<ValueType>(game.getPlayer2Variables(), otherChoices);
471
472 for (auto const& otherChoice : otherChoicesUpdateToSuccessorMappings) {
473 for (uint64_t updateIndex = 0; updateIndex < sortedChoiceUpdateIndicesAndMasses.size(); ++updateIndex) {
474 storm::storage::BitVector const& choiceSuccessor =
475 choiceUpdateToSuccessorMapping.at(sortedChoiceUpdateIndicesAndMasses[updateIndex].first).first;
476 storm::storage::BitVector const& otherChoiceSuccessor = otherChoice.at(sortedChoiceUpdateIndicesAndMasses[updateIndex].first).first;
477
478 bool deviates = choiceSuccessor != otherChoiceSuccessor;
479 if (deviates) {
480 std::map<storm::expressions::Variable, storm::expressions::Expression> variableUpdates =
481 abstractor.get().getVariableUpdates(player1Index, sortedChoiceUpdateIndicesAndMasses[updateIndex].first);
482
483 for (uint64_t predicateIndex = 0; predicateIndex < choiceSuccessor.size(); ++predicateIndex) {
484 if (choiceSuccessor[predicateIndex] != otherChoiceSuccessor[predicateIndex]) {
485 possibleRefinementPredicates.push_back(
486 abstractionInformation.getPredicateByIndex(predicateIndex).substitute(variableUpdates).simplify());
487 if (!rankPredicates) {
488 break;
489 }
490 }
491 }
492
493 break;
494 }
495 }
496 }
497
498 STORM_LOG_ASSERT(!possibleRefinementPredicates.empty(), "Expected refinement predicates.");
499
500 STORM_LOG_TRACE("Possible refinement predicates:");
501 for (auto const& pred : possibleRefinementPredicates) {
502 STORM_LOG_TRACE(pred);
503 }
504 }
505
506 return RefinementPredicates(fromGuard ? RefinementPredicates::Source::Guard : RefinementPredicates::Source::WeakestPrecondition,
507 {possibleRefinementPredicates});
508}
509
510template<storm::dd::DdType Type, typename ValueType>
512 storm::dd::Bdd<Type> const& transitionMatrixBdd, storm::dd::Bdd<Type> const& minPlayer1Strategy,
513 storm::dd::Bdd<Type> const& minPlayer2Strategy, storm::dd::Bdd<Type> const& maxPlayer1Strategy,
514 storm::dd::Bdd<Type> const& maxPlayer2Strategy) {
516
517 // Build the fragment of transitions that is reachable by either the min or the max strategies.
518 result.reachableTransitionsMin = (transitionMatrixBdd && minPlayer1Strategy && minPlayer2Strategy).existsAbstract(game.getNondeterminismVariables());
519 result.reachableTransitionsMax = (transitionMatrixBdd && maxPlayer1Strategy && maxPlayer2Strategy).existsAbstract(game.getNondeterminismVariables());
520
521 // Start with all reachable states as potential pivot states.
522 result.pivotStates =
524 .first ||
526 .first;
527
528 // Then constrain these states by the requirement that for either the lower or upper player 1 choice the player 2 choices must be different and
529 // that the difference is not because of a missing strategy in either case.
530
531 // Start with constructing the player 2 states that have a min and a max strategy.
532 storm::dd::Bdd<Type> constraint =
533 minPlayer2Strategy.existsAbstract(game.getPlayer2Variables()) && maxPlayer2Strategy.existsAbstract(game.getPlayer2Variables());
534
535 // Now construct all player 2 choices that actually exist and differ in the min and max case.
536 constraint &= minPlayer2Strategy.exclusiveOr(maxPlayer2Strategy);
537
538 // Then restrict the pivot states by requiring existing and different player 2 choices.
539 result.pivotStates &= ((minPlayer1Strategy || maxPlayer1Strategy) && constraint).existsAbstract(game.getNondeterminismVariables());
540
541 return result;
542}
543
544template<storm::dd::DdType Type, typename ValueType>
545RefinementPredicates MenuGameRefiner<Type, ValueType>::derivePredicatesFromPivotState(
546 storm::gbar::abstraction::MenuGame<Type, ValueType> const& game, storm::dd::Bdd<Type> const& pivotState, storm::dd::Bdd<Type> const& minPlayer1Strategy,
547 storm::dd::Bdd<Type> const& minPlayer2Strategy, storm::dd::Bdd<Type> const& maxPlayer1Strategy, storm::dd::Bdd<Type> const& maxPlayer2Strategy) const {
548 // Compute the lower and the upper choice for the pivot state.
549 std::set<storm::expressions::Variable> variablesToAbstract = game.getNondeterminismVariables();
550 variablesToAbstract.insert(game.getRowVariables().begin(), game.getRowVariables().end());
551
552 bool player1ChoicesDifferent = !(pivotState && minPlayer1Strategy).exclusiveOr(pivotState && maxPlayer1Strategy).isZero();
553
554 boost::optional<RefinementPredicates> predicates;
555
556 // Derive predicates from lower choice.
557 storm::dd::Bdd<Type> lowerChoice = pivotState && game.getExtendedTransitionMatrix().toBdd() && minPlayer1Strategy;
558 storm::dd::Bdd<Type> lowerChoice1 = (lowerChoice && minPlayer2Strategy).existsAbstract(variablesToAbstract);
559 storm::dd::Bdd<Type> lowerChoice2 = (lowerChoice && maxPlayer2Strategy).existsAbstract(variablesToAbstract);
560
561 bool lowerChoicesDifferent = !lowerChoice1.exclusiveOr(lowerChoice2).isZero() && !lowerChoice1.isZero() && !lowerChoice2.isZero();
562 if (lowerChoicesDifferent) {
563 STORM_LOG_TRACE("Deriving predicates based on lower choice.");
564 if (this->addPredicatesEagerly) {
565 predicates = derivePredicatesFromChoice(game, pivotState, (pivotState && minPlayer1Strategy).existsAbstract(game.getRowVariables()),
566 lowerChoice && minPlayer2Strategy, lowerChoice1);
567 } else {
568 predicates =
569 derivePredicatesFromDifferingChoices((pivotState && minPlayer1Strategy).existsAbstract(game.getRowVariables()), lowerChoice1, lowerChoice2);
570 }
571 }
572
573 if (predicates && !player1ChoicesDifferent) {
574 return predicates.get();
575 }
576
577 boost::optional<RefinementPredicates> additionalPredicates;
578
579 storm::dd::Bdd<Type> upperChoice = pivotState && game.getExtendedTransitionMatrix().toBdd() && maxPlayer1Strategy;
580 storm::dd::Bdd<Type> upperChoice1 = (upperChoice && minPlayer2Strategy).existsAbstract(variablesToAbstract);
581 storm::dd::Bdd<Type> upperChoice2 = (upperChoice && maxPlayer2Strategy).existsAbstract(variablesToAbstract);
582
583 bool upperChoicesDifferent = !upperChoice1.exclusiveOr(upperChoice2).isZero() && !upperChoice1.isZero() && !upperChoice2.isZero();
584 if (upperChoicesDifferent) {
585 STORM_LOG_TRACE("Deriving predicates based on upper choice.");
586 if (this->addPredicatesEagerly) {
587 additionalPredicates = derivePredicatesFromChoice(game, pivotState, (pivotState && maxPlayer1Strategy).existsAbstract(game.getRowVariables()),
588 upperChoice && maxPlayer2Strategy, upperChoice1);
589 } else {
590 additionalPredicates =
591 derivePredicatesFromDifferingChoices((pivotState && maxPlayer1Strategy).existsAbstract(game.getRowVariables()), upperChoice1, upperChoice2);
592 }
593 }
594
595 if (additionalPredicates) {
596 if (additionalPredicates.get().getSource() == RefinementPredicates::Source::Guard) {
597 return additionalPredicates.get();
598 } else {
599 if (!predicates) {
600 predicates = additionalPredicates;
601 } else {
602 predicates.get().addPredicates(additionalPredicates.get().getPredicates());
603 }
604 }
605 }
606
607 STORM_LOG_THROW(static_cast<bool>(predicates), storm::exceptions::InvalidStateException, "Could not derive predicates for refinement.");
608
609 return predicates.get();
610}
611
612template<storm::dd::DdType Type, typename ValueType>
613std::pair<std::vector<std::vector<storm::expressions::Expression>>, std::map<storm::expressions::Variable, storm::expressions::Expression>>
614MenuGameRefiner<Type, ValueType>::buildTrace(storm::expressions::ExpressionManager& expressionManager,
615 storm::gbar::abstraction::MenuGame<Type, ValueType> const& game, storm::dd::Bdd<Type> const& spanningTree,
616 storm::dd::Bdd<Type> const& pivotState) const {
617 std::vector<std::vector<storm::expressions::Expression>> predicates;
618
619 // Prepare some variables.
620 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
621 std::set<storm::expressions::Variable> variablesToAbstract(game.getColumnVariables());
622 variablesToAbstract.insert(game.getPlayer1Variables().begin(), game.getPlayer1Variables().end());
623 variablesToAbstract.insert(game.getProbabilisticBranchingVariables().begin(), game.getProbabilisticBranchingVariables().end());
624
625 storm::expressions::Expression initialExpression = abstractor.get().getInitialExpression();
626
627 std::set<storm::expressions::Variable> oldVariables = initialExpression.getVariables();
628 for (auto const& predicate : abstractionInformation.getPredicates()) {
629 std::set<storm::expressions::Variable> usedVariables = predicate.getVariables();
630 oldVariables.insert(usedVariables.begin(), usedVariables.end());
631 }
632
633 std::map<storm::expressions::Variable, storm::expressions::Variable> oldToNewVariables;
634 for (auto const& variable : oldVariables) {
635 oldToNewVariables[variable] = expressionManager.getVariable(variable.getName());
636 }
637 std::map<storm::expressions::Variable, storm::expressions::Expression> lastSubstitution;
638 for (auto const& variable : oldToNewVariables) {
639 lastSubstitution[variable.second] = variable.second;
640 }
641 std::map<storm::expressions::Variable, storm::expressions::Expression> stepVariableToCopiedVariableMap;
642
643 // Start with the target state part of the trace.
644 storm::storage::BitVector decodedTargetState = abstractionInformation.decodeState(pivotState);
645 predicates.emplace_back(abstractionInformation.getPredicates(decodedTargetState));
646 for (auto& predicate : predicates.back()) {
647 predicate = predicate.changeManager(expressionManager);
648 }
649 // Add ranges of variables.
650 for (auto const& pred : abstractionInformation.getConstraints()) {
651 predicates.back().push_back(pred.changeManager(expressionManager));
652 }
653
654 // Perform a backward search for an initial state.
655 storm::dd::Bdd<Type> currentState = pivotState;
656 while ((currentState && game.getInitialStates()).isZero()) {
657 storm::dd::Bdd<Type> predecessorTransition = currentState.swapVariables(game.getRowColumnMetaVariablePairs()) && spanningTree;
658 std::tuple<storm::storage::BitVector, uint64_t, uint64_t> decodedPredecessor =
659 abstractionInformation.decodeStatePlayer1ChoiceAndUpdate(predecessorTransition);
660
661 // Create a new copy of each variable to use for this step.
662 std::map<storm::expressions::Variable, storm::expressions::Expression> substitution;
663 for (auto const& variablePair : oldToNewVariables) {
664 storm::expressions::Variable variableCopy = expressionManager.declareVariableCopy(variablePair.second);
665 substitution[variablePair.second] = variableCopy;
666 stepVariableToCopiedVariableMap[variableCopy] = variablePair.second;
667 }
668
669 // Retrieve the variable updates that the predecessor needs to perform to get to the current state.
670 auto variableUpdates = abstractor.get().getVariableUpdates(std::get<1>(decodedPredecessor), std::get<2>(decodedPredecessor));
671 for (auto const& oldNewVariablePair : oldToNewVariables) {
672 storm::expressions::Variable const& newVariable = oldNewVariablePair.second;
673
674 // If the variable was set, use its update expression.
675 auto updateIt = variableUpdates.find(oldNewVariablePair.first);
676 if (updateIt != variableUpdates.end()) {
677 auto const& update = *updateIt;
678
679 if (update.second.hasBooleanType()) {
680 predicates.back().push_back(
681 storm::expressions::iff(lastSubstitution.at(newVariable), update.second.changeManager(expressionManager).substitute(substitution)));
682 } else {
683 predicates.back().push_back(lastSubstitution.at(newVariable) == update.second.changeManager(expressionManager).substitute(substitution));
684 }
685 } else {
686 // Otherwise, make sure that the new variable maintains the old value.
687 if (newVariable.hasBooleanType()) {
688 predicates.back().push_back(storm::expressions::iff(lastSubstitution.at(newVariable), substitution.at(newVariable)));
689 } else {
690 predicates.back().push_back(lastSubstitution.at(newVariable) == substitution.at(newVariable));
691 }
692 }
693 }
694
695 // Add the guard of the choice.
696 predicates.back().push_back(abstractor.get().getGuard(std::get<1>(decodedPredecessor)).changeManager(expressionManager).substitute(substitution));
697
698 // Retrieve the predicate valuation in the predecessor.
699 predicates.emplace_back(abstractionInformation.getPredicates(std::get<0>(decodedPredecessor)));
700 for (auto& predicate : predicates.back()) {
701 predicate = predicate.changeManager(expressionManager).substitute(substitution);
702 }
703 // Add ranges of variables.
704 for (auto const& pred : abstractionInformation.getConstraints()) {
705 predicates.back().push_back(pred.changeManager(expressionManager).substitute(substitution));
706 }
707 // Move backwards one step.
708 lastSubstitution = std::move(substitution);
709 currentState = predecessorTransition.existsAbstract(variablesToAbstract);
710 }
711
712 predicates.back().push_back(initialExpression.changeManager(expressionManager).substitute(lastSubstitution));
713 return std::make_pair(predicates, stepVariableToCopiedVariableMap);
714}
715
716template<typename ValueType>
717const uint64_t ExplicitPivotStateResult<ValueType>::NO_PREDECESSOR = std::numeric_limits<uint64_t>::max();
718
719template<storm::dd::DdType Type, typename ValueType>
720std::pair<std::vector<uint64_t>, std::vector<uint64_t>> MenuGameRefiner<Type, ValueType>::buildReversedLabeledPath(
721 ExplicitPivotStateResult<ValueType> const& pivotStateResult) const {
722 std::pair<std::vector<uint64_t>, std::vector<uint64_t>> result;
723 result.first.emplace_back(pivotStateResult.pivotState);
724
725 uint64_t currentState = pivotStateResult.predecessors[pivotStateResult.pivotState].first;
726 uint64_t currentAction = pivotStateResult.predecessors[pivotStateResult.pivotState].second;
728 STORM_LOG_ASSERT(currentAction != ExplicitPivotStateResult<ValueType>::NO_PREDECESSOR, "Expected predecessor action.");
729 result.first.emplace_back(currentState);
730 result.second.emplace_back(currentAction);
731 currentAction = pivotStateResult.predecessors[currentState].second;
732 currentState = pivotStateResult.predecessors[currentState].first;
733 }
734
735 STORM_LOG_ASSERT(result.first.size() == result.second.size() + 1, "Path size mismatch.");
736 return result;
737}
738
739template<storm::dd::DdType Type, typename ValueType>
740std::pair<std::vector<std::vector<storm::expressions::Expression>>, std::map<storm::expressions::Variable, storm::expressions::Expression>>
741MenuGameRefiner<Type, ValueType>::buildTraceFromReversedLabeledPath(storm::expressions::ExpressionManager& expressionManager,
742 std::vector<uint64_t> const& reversedPath, std::vector<uint64_t> const& reversedLabels,
743 storm::dd::Odd const& odd, std::vector<uint64_t> const* stateToOffset) const {
744 STORM_LOG_ASSERT(reversedPath.size() == reversedLabels.size() + 1, "Path size mismatch.");
745
746 std::vector<std::vector<storm::expressions::Expression>> predicates;
747
748 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
749 storm::expressions::Expression initialExpression = abstractor.get().getInitialExpression();
750
751 std::set<storm::expressions::Variable> oldVariables = initialExpression.getVariables();
752 for (auto const& predicate : abstractionInformation.getPredicates()) {
753 std::set<storm::expressions::Variable> usedVariables = predicate.getVariables();
754 oldVariables.insert(usedVariables.begin(), usedVariables.end());
755 }
756
757 std::map<storm::expressions::Variable, storm::expressions::Variable> oldToNewVariables;
758 for (auto const& variable : oldVariables) {
759 oldToNewVariables[variable] = expressionManager.getVariable(variable.getName());
760 }
761 std::map<storm::expressions::Variable, storm::expressions::Expression> currentSubstitution;
762 for (auto const& variable : oldToNewVariables) {
763 currentSubstitution[variable.second] = variable.second;
764 }
765 std::map<storm::expressions::Variable, storm::expressions::Expression> stepVariableToCopiedVariableMap;
766
767 auto pathIt = reversedPath.rbegin();
768
769 // Decode pivot state. The state valuation also includes
770 // * the bottom state, so we need to reserve one more, and
771 // * the location variables,
772 // so we need to reserve an appropriate size.
773 uint64_t predicateValuationOffset = abstractionInformation.getNumberOfDdSourceLocationVariables() + 1;
774 storm::storage::BitVector extendedPredicateValuation =
775 odd.getEncoding(stateToOffset ? (*stateToOffset)[*pathIt] : *pathIt, abstractionInformation.getNumberOfPredicates() + predicateValuationOffset);
776 ++pathIt;
777
778 // Add all predicates of initial block.
779 predicates.emplace_back(abstractionInformation.getPredicatesExcludingBottom(extendedPredicateValuation));
780 for (auto& predicate : predicates.back()) {
781 predicate = predicate.changeManager(expressionManager);
782 }
783
784 // Add further constraints (like ranges).
785 for (auto const& pred : abstractionInformation.getConstraints()) {
786 predicates.back().push_back(pred.changeManager(expressionManager));
787 }
788
789 // Add initial expression.
790 predicates.back().push_back(initialExpression.changeManager(expressionManager));
791
792 // Traverse the path and construct necessary formula parts.
793 auto actionIt = reversedLabels.rbegin();
794 for (; pathIt != reversedPath.rend(); ++pathIt) {
795 // Add new predicate frame.
796 predicates.emplace_back();
797
798 // Add guard of action.
799 predicates.back().emplace_back(abstractor.get().getGuard(*actionIt).changeManager(expressionManager).substitute(currentSubstitution));
800
801 // Determine which variables are affected by the updates of the player 1 choice.
802 std::set<storm::expressions::Variable> const& assignedVariables = abstractor.get().getAssignedVariables(*actionIt);
803
804 // Create new instances of the affected variables.
805 std::map<storm::expressions::Variable, storm::expressions::Variable> newVariableMaps;
806 for (auto const& variable : assignedVariables) {
807 storm::expressions::Variable variableCopy = expressionManager.declareVariableCopy(variable);
808 newVariableMaps[oldToNewVariables.at(variable)] = variableCopy;
809 stepVariableToCopiedVariableMap[variableCopy] = variable;
810 }
811
812 // Retrieves the possible updates to the variables.
813 auto variableUpdateVector = abstractor.get().getVariableUpdates(*actionIt);
814
815 // Encode these updates.
816 storm::expressions::Expression allVariableUpdateExpression;
817 for (auto const& variableUpdates : variableUpdateVector) {
818 storm::expressions::Expression variableUpdateExpression;
819 for (auto const& update : variableUpdates) {
820 if (update.second.hasBooleanType()) {
821 variableUpdateExpression =
822 variableUpdateExpression && storm::expressions::iff(newVariableMaps.at(update.first),
823 update.second.changeManager(expressionManager).substitute(currentSubstitution));
824 } else {
825 variableUpdateExpression = variableUpdateExpression && newVariableMaps.at(update.first) ==
826 update.second.changeManager(expressionManager).substitute(currentSubstitution);
827 }
828 }
829
830 allVariableUpdateExpression = allVariableUpdateExpression || variableUpdateExpression;
831 }
832 if (!allVariableUpdateExpression.isInitialized()) {
833 allVariableUpdateExpression = expressionManager.boolean(true);
834 }
835 predicates.back().emplace_back(allVariableUpdateExpression);
836
837 // Incorporate the new variables in the current substitution.
838 for (auto const& variablePair : newVariableMaps) {
839 currentSubstitution[variablePair.first] = variablePair.second;
840 }
841
842 // Decode current state.
843 extendedPredicateValuation =
844 odd.getEncoding(stateToOffset ? (*stateToOffset)[*pathIt] : *pathIt, abstractionInformation.getNumberOfPredicates() + predicateValuationOffset);
845
846 // Encode the predicates whose value might have changed.
847 // FIXME: could be optimized by precomputation.
848 for (uint64_t predicateIndex = 0; predicateIndex < abstractionInformation.getNumberOfPredicates(); ++predicateIndex) {
849 auto const& predicate = abstractionInformation.getPredicateByIndex(predicateIndex);
850 std::set<storm::expressions::Variable> usedVariables = predicate.getVariables();
851
852 bool containsAssignedVariables = false;
853 for (auto usedIt = usedVariables.begin(), assignedIt = assignedVariables.begin();;) {
854 if (usedIt == usedVariables.end() || assignedIt == assignedVariables.end()) {
855 break;
856 }
857
858 if (*usedIt == *assignedIt) {
859 containsAssignedVariables = true;
860 break;
861 }
862
863 if (*usedIt < *assignedIt) {
864 ++usedIt;
865 } else {
866 ++assignedIt;
867 }
868 }
869
870 if (containsAssignedVariables) {
871 auto transformedPredicate = predicate.changeManager(expressionManager).substitute(currentSubstitution);
872 predicates.back().emplace_back(extendedPredicateValuation.get(predicateIndex + predicateValuationOffset) ? transformedPredicate
873 : !transformedPredicate);
874 }
875 }
876
877 // Enforce constraints of all assigned variables.
878 for (auto const& constraint : abstractionInformation.getConstraints()) {
879 std::set<storm::expressions::Variable> usedVariables = constraint.getVariables();
880
881 bool containsAssignedVariables = false;
882 for (auto usedIt = usedVariables.begin(), assignedIt = assignedVariables.begin();;) {
883 if (usedIt == usedVariables.end() || assignedIt == assignedVariables.end()) {
884 break;
885 }
886
887 if (*usedIt == *assignedIt) {
888 containsAssignedVariables = true;
889 break;
890 }
891
892 if (*usedIt < *assignedIt) {
893 ++usedIt;
894 } else {
895 ++assignedIt;
896 }
897 }
898
899 if (containsAssignedVariables) {
900 auto transformedConstraint = constraint.changeManager(expressionManager).substitute(currentSubstitution);
901 predicates.back().emplace_back(transformedConstraint);
902 }
903 }
904
905 ++actionIt;
906 }
907
908 return std::make_pair(predicates, stepVariableToCopiedVariableMap);
909}
910
911template<storm::dd::DdType Type, typename ValueType>
912boost::optional<RefinementPredicates> MenuGameRefiner<Type, ValueType>::derivePredicatesFromInterpolationFromTrace(
913 storm::expressions::ExpressionManager& interpolationManager, std::vector<std::vector<storm::expressions::Expression>> const& trace,
914 std::map<storm::expressions::Variable, storm::expressions::Expression> const& variableSubstitution) const {
915 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
916
917 auto start = std::chrono::high_resolution_clock::now();
918 boost::optional<RefinementPredicates> predicates;
919
920 // Create solver and interpolation groups.
921 auto assertionStart = std::chrono::high_resolution_clock::now();
922 storm::solver::MathsatSmtSolver interpolatingSolver(interpolationManager, storm::solver::MathsatSmtSolver::Options(true, false, true));
923 uint64_t stepCounter = 0;
924 for (auto const& traceElement : trace) {
925 // interpolatingSolver.push();
926
927 interpolatingSolver.setInterpolationGroup(stepCounter);
928 for (auto const& predicate : traceElement) {
929 interpolatingSolver.add(predicate);
930 }
931
932 ++stepCounter;
933 }
934 auto assertionEnd = std::chrono::high_resolution_clock::now();
935 STORM_LOG_TRACE("Asserting trace formula took " << std::chrono::duration_cast<std::chrono::milliseconds>(assertionEnd - assertionStart).count() << "ms.");
936
937 // Now encode the trace as an SMT problem.
938 storm::solver::SmtSolver::CheckResult result = interpolatingSolver.check();
940 STORM_LOG_TRACE("Trace formula is unsatisfiable. Starting interpolation.");
941
942 std::vector<storm::expressions::Expression> interpolants;
943 std::vector<uint64_t> prefix;
944 for (uint64_t step = 0; step < stepCounter; ++step) {
945 prefix.push_back(step);
946 storm::expressions::Expression interpolant =
947 interpolatingSolver.getInterpolant(prefix).substitute(variableSubstitution).changeManager(abstractionInformation.getExpressionManager());
948 if (interpolant.isFalse()) {
949 // If the interpolant is false, it means that the prefix has become unsatisfiable.
950 STORM_LOG_TRACE("Trace formula became unsatisfiable at position " << step << " of " << stepCounter << ".");
951 break;
952 }
953 if (!interpolant.isTrue()) {
954 STORM_LOG_DEBUG("Derived new predicate (based on interpolation at step " << step << " out of " << stepCounter << "): " << interpolant);
955 interpolants.push_back(interpolant);
956 }
957 }
958
959 STORM_LOG_ASSERT(!interpolants.empty(), "Expected to have non-empty set of interpolants.");
960
961 predicates = boost::make_optional(RefinementPredicates(RefinementPredicates::Source::Interpolation, interpolants));
962 } else {
963 STORM_LOG_TRACE("Trace formula is satisfiable, not using interpolation.");
964 }
965 auto end = std::chrono::high_resolution_clock::now();
966
967 if (predicates) {
968 STORM_LOG_TRACE("Deriving predicates using interpolation from witness of size "
969 << trace.size() << " took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
970 } else {
971 STORM_LOG_TRACE("Tried deriving predicates using interpolation but failed in "
972 << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
973 }
974
975 return predicates;
976}
977
978template<storm::dd::DdType Type, typename ValueType>
979boost::optional<RefinementPredicates> MenuGameRefiner<Type, ValueType>::derivePredicatesFromInterpolation(
980 storm::gbar::abstraction::MenuGame<Type, ValueType> const& game, SymbolicPivotStateResult<Type, ValueType> const& symbolicPivotStateResult,
981 storm::dd::Bdd<Type> const& minPlayer1Strategy, storm::dd::Bdd<Type> const& minPlayer2Strategy, storm::dd::Bdd<Type> const& maxPlayer1Strategy,
982 storm::dd::Bdd<Type> const& maxPlayer2Strategy) const {
983 // Compute the most probable path from any initial state to the pivot state.
984 SymbolicMostProbablePathsResult<Type, ValueType> symbolicMostProbablePathsResult;
985 if (!symbolicPivotStateResult.symbolicMostProbablePathsResult) {
986 symbolicMostProbablePathsResult = getMostProbablePathSpanningTree(game, symbolicPivotStateResult.fromDirection == storm::OptimizationDirection::Minimize
987 ? minPlayer1Strategy && minPlayer2Strategy
988 : maxPlayer1Strategy && maxPlayer2Strategy);
989 } else {
990 symbolicMostProbablePathsResult = symbolicPivotStateResult.symbolicMostProbablePathsResult.get();
991 }
992
993 // Create a new expression manager that we can use for the interpolation.
994 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
995 std::shared_ptr<storm::expressions::ExpressionManager> interpolationManager = abstractionInformation.getExpressionManager().clone();
996
997 // Build the trace of the most probable path in terms of which predicates hold in each step.
998 auto start = std::chrono::high_resolution_clock::now();
999 std::pair<std::vector<std::vector<storm::expressions::Expression>>, std::map<storm::expressions::Variable, storm::expressions::Expression>>
1000 traceAndVariableSubstitution =
1001 buildTrace(*interpolationManager, game, symbolicMostProbablePathsResult.spanningTree, symbolicPivotStateResult.pivotState);
1002 auto end = std::chrono::high_resolution_clock::now();
1003 STORM_LOG_DEBUG("Building the trace and variable substitution for interpolation from symbolic most-probable paths result took "
1004 << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
1005
1006 return derivePredicatesFromInterpolationFromTrace(*interpolationManager, traceAndVariableSubstitution.first, traceAndVariableSubstitution.second);
1007}
1008
1009template<storm::dd::DdType Type, typename ValueType>
1010boost::optional<RefinementPredicates> MenuGameRefiner<Type, ValueType>::derivePredicatesFromInterpolation(
1011 storm::gbar::abstraction::MenuGame<Type, ValueType> const& /*game*/, ExplicitPivotStateResult<ValueType> const& pivotStateResult,
1012 storm::dd::Odd const& odd) const {
1013 // Game is not necessary if representation is explicit via odd
1014 // Create a new expression manager that we can use for the interpolation.
1015 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
1016 std::shared_ptr<storm::expressions::ExpressionManager> interpolationManager = abstractionInformation.getExpressionManager().clone();
1017
1018 // Build the trace of the most probable path in terms of which predicates hold in each step.
1019 auto start = std::chrono::high_resolution_clock::now();
1020 std::pair<std::vector<uint64_t>, std::vector<uint64_t>> labeledReversedPath = buildReversedLabeledPath(pivotStateResult);
1021
1022 // If the initial state is the pivot state, we can stop here.
1023 if (labeledReversedPath.first.size() == 1) {
1024 return boost::none;
1025 }
1026
1027 std::pair<std::vector<std::vector<storm::expressions::Expression>>, std::map<storm::expressions::Variable, storm::expressions::Expression>>
1028 traceAndVariableSubstitution = buildTraceFromReversedLabeledPath(*interpolationManager, labeledReversedPath.first, labeledReversedPath.second, odd);
1029 auto end = std::chrono::high_resolution_clock::now();
1030 STORM_LOG_DEBUG("Building the trace and variable substitution for interpolation from explicit most-probable paths result took "
1031 << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
1032
1033 return derivePredicatesFromInterpolationFromTrace(*interpolationManager, traceAndVariableSubstitution.first, traceAndVariableSubstitution.second);
1034}
1035
1036template<storm::dd::DdType Type, typename ValueType>
1038 SymbolicQualitativeGameResultMinMax<Type> const& qualitativeResult) const {
1039 STORM_LOG_TRACE("Trying refinement after qualitative check.");
1040 // Get all relevant strategies.
1041 storm::dd::Bdd<Type> minPlayer1Strategy = qualitativeResult.prob0Min.getPlayer1Strategy();
1042 storm::dd::Bdd<Type> minPlayer2Strategy = qualitativeResult.prob0Min.getPlayer2Strategy();
1043 storm::dd::Bdd<Type> maxPlayer1Strategy = qualitativeResult.prob1Max.getPlayer1Strategy();
1044 storm::dd::Bdd<Type> maxPlayer2Strategy = qualitativeResult.prob1Max.getPlayer2Strategy();
1045
1046 // Redirect all player 1 choices of the min strategy to that of the max strategy if this leads to a player 2
1047 // state that is also a prob 0 state.
1048 auto oldMinPlayer1Strategy = minPlayer1Strategy;
1049 minPlayer1Strategy = (maxPlayer1Strategy && qualitativeResult.prob0Min.getPlayer2States())
1051 .ite(maxPlayer1Strategy, minPlayer1Strategy);
1052
1053 // Compute all reached pivot states.
1054 PivotStateCandidatesResult<Type> pivotStateCandidatesResult =
1055 computePivotStates(game, transitionMatrixBdd, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy, maxPlayer2Strategy);
1056
1057 // We can only refine in case we have a reachable player 1 state with a player 2 successor (under either
1058 // player 1's min or max strategy) such that from this player 2 state, both prob0 min and prob1 max define
1059 // strategies and they differ. Hence, it is possible that we arrive at a point where no suitable pivot state
1060 // is found. In this case, we abort the qualitative refinement here.
1061 if (pivotStateCandidatesResult.pivotStates.isZero()) {
1062 STORM_LOG_TRACE("Did not find pivot state in qualitative fragment.");
1063 return false;
1064 }
1065 STORM_LOG_ASSERT(!pivotStateCandidatesResult.pivotStates.isZero(), "Unable to proceed without pivot state candidates.");
1066
1067 // Now that we have the pivot state candidates, we need to pick one.
1068 SymbolicPivotStateResult<Type, ValueType> symbolicPivotStateResult =
1069 pickPivotState<Type, ValueType>(pivotSelectionHeuristic, game, pivotStateCandidatesResult, qualitativeResult, boost::none);
1070
1071 boost::optional<RefinementPredicates> predicates;
1072 if (useInterpolation) {
1073 predicates =
1074 derivePredicatesFromInterpolation(game, symbolicPivotStateResult, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy, maxPlayer2Strategy);
1075 }
1076 if (predicates) {
1077 STORM_LOG_TRACE("Obtained predicates by interpolation.");
1078 } else {
1079 predicates = derivePredicatesFromPivotState(game, symbolicPivotStateResult.pivotState, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy,
1080 maxPlayer2Strategy);
1081 }
1082 STORM_LOG_THROW(static_cast<bool>(predicates), storm::exceptions::InvalidStateException, "Predicates needed to continue.");
1083
1084 // Derive predicate based on the selected pivot state.
1085 std::vector<storm::expressions::Expression> preparedPredicates = preprocessPredicates(predicates.get().getPredicates(), predicates.get().getSource());
1086 performRefinement(createGlobalRefinement(preparedPredicates));
1087 return true;
1088}
1089
1090template<typename ValueType>
1093 // Intentionally left empty.
1094 }
1095
1096 ValueType distance;
1097 uint64_t state;
1098 bool lower;
1099};
1100
1101template<typename ValueType>
1104 if (a.distance < b.distance) {
1105 return true;
1106 } else if (a.distance > b.distance) {
1107 return false;
1108 } else {
1109 if (a.state < b.state) {
1110 return true;
1111 } else if (a.state > b.state) {
1112 return false;
1113 } else {
1114 if (a.lower < b.lower) {
1115 return true;
1116 } else {
1117 return false;
1118 }
1119 }
1120 }
1121 }
1122};
1123
1124template<typename ValueType>
1126 bool probabilityDistances, std::vector<ValueType>& distances, std::vector<std::pair<uint64_t, uint64_t>>& predecessors,
1127 bool generatePredecessors, bool lower, uint64_t currentState, ValueType const& currentDistance, [[maybe_unused]] bool isPivotState,
1128 storm::storage::ExplicitGameStrategyPair const& strategyPair,
1129 [[maybe_unused]] storm::storage::ExplicitGameStrategyPair const& otherStrategyPair, std::vector<uint64_t> const& player1Labeling,
1130 [[maybe_unused]] std::vector<uint64_t> const& player2Grouping, storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
1131 [[maybe_unused]] storm::storage::BitVector const& targetStates, storm::storage::BitVector const& relevantStates) {
1132 if (strategyPair.getPlayer1Strategy().hasDefinedChoice(currentState)) {
1133 uint64_t player2Successor = strategyPair.getPlayer1Strategy().getChoice(currentState);
1134 uint64_t player2Choice = strategyPair.getPlayer2Strategy().getChoice(player2Successor);
1135 STORM_LOG_ASSERT(isPivotState || !otherStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2Successor) ||
1136 strategyPair.getPlayer2Strategy().getChoice(player2Successor) == player2Choice,
1137 "Did not expect deviation in player 2 strategy.");
1138 STORM_LOG_ASSERT(player2Grouping[player2Successor] <= player2Choice && player2Choice < player2Grouping[player2Successor + 1],
1139 "Illegal choice for player 2.");
1140
1141 for (auto const& entry : transitionMatrix.getRow(player2Choice)) {
1142 uint64_t player1Successor = entry.getColumn();
1143 if (!relevantStates.get(player1Successor)) {
1144 continue;
1145 }
1146
1147 ValueType alternateDistance;
1148 if (probabilityDistances) {
1149 alternateDistance = currentDistance * entry.getValue();
1150 } else {
1151 alternateDistance = currentDistance + storm::utility::one<ValueType>();
1152 }
1153 if (probabilityDistances ? alternateDistance > distances[player1Successor] : alternateDistance < distances[player1Successor]) {
1154 distances[player1Successor] = alternateDistance;
1155 if (generatePredecessors) {
1156 predecessors[player1Successor] = std::make_pair(currentState, player1Labeling[player2Successor]);
1157 }
1158 dijkstraQueue.emplace(alternateDistance, player1Successor, lower);
1159 }
1160 }
1161 } else {
1162 STORM_LOG_ASSERT(targetStates.get(currentState), "Expecting min strategy for non-target states.");
1163 }
1164}
1165
1166template<typename ValueType>
1167boost::optional<ExplicitPivotStateResult<ValueType>> pickPivotState(
1168 bool generatePredecessors, PivotSelectionHeuristic pivotSelectionHeuristic, storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
1169 std::vector<uint64_t> const& player1Labeling, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& relevantStates,
1170 storm::storage::BitVector const& targetStates, storage::ExplicitGameStrategyPair const& minStrategyPair,
1171 storage::ExplicitGameStrategyPair const& maxStrategyPair, std::vector<ValueType> const* lowerValues = nullptr,
1172 std::vector<ValueType> const* upperValues = nullptr) {
1173 // The method used to have an unused player1Grouping.
1174 STORM_LOG_ASSERT(!lowerValues || upperValues, "Expected none or both value results.");
1175 STORM_LOG_ASSERT(!upperValues || lowerValues, "Expected none or both value results.");
1176
1177 // Perform Dijkstra search that stays within the relevant states and searches for a (pivot) state in which
1178 // the strategies the lower or upper player 1 action leads to a player 2 state in which the choices differ.
1179 // To guarantee that the pivot state is reachable by either of the strategies, we do a parallel Dijkstra
1180 // search in both the lower and upper strategy.
1181
1182 bool probabilityDistances = pivotSelectionHeuristic == PivotSelectionHeuristic::MostProbablePath;
1183 uint64_t numberOfStates = initialStates.size();
1184 // The distance of a state that has not been reached yet, i.e. one that any real distance improves upon. With
1185 // probability distances that is zero. With hop distances no reachable state is more than numberOfStates - 1 hops
1186 // away, so numberOfStates lies beyond every achievable distance.
1187 ValueType unreachableDistance =
1189 ValueType zeroDistance = probabilityDistances ? storm::utility::one<ValueType>() : storm::utility::zero<ValueType>();
1190
1191 // Create storages for the lower and upper Dijkstra search.
1192 std::vector<ValueType> lowerDistances(numberOfStates, unreachableDistance);
1193 std::vector<std::pair<uint64_t, uint64_t>> lowerPredecessors;
1194 std::vector<ValueType> upperDistances(numberOfStates, unreachableDistance);
1195 std::vector<std::pair<uint64_t, uint64_t>> upperPredecessors;
1196
1197 if (generatePredecessors) {
1198 lowerPredecessors.resize(numberOfStates,
1200 upperPredecessors.resize(numberOfStates,
1202 }
1203
1204 // Use set as priority queue with unique membership.
1205 std::set<ExplicitDijkstraQueueElement<ValueType>, ExplicitDijkstraQueueElementLess<ValueType>> dijkstraQueue;
1206
1207 for (uint64_t initialState : initialStates) {
1208 if (!relevantStates.get(initialState)) {
1209 continue;
1210 }
1211
1212 lowerDistances[initialState] = zeroDistance;
1213 upperDistances[initialState] = zeroDistance;
1214 dijkstraQueue.emplace(zeroDistance, initialState, true);
1215 dijkstraQueue.emplace(zeroDistance, initialState, false);
1216 }
1217
1218 // For some heuristics, we need to potentially find more than just one pivot.
1219 bool considerDeviation = (pivotSelectionHeuristic == PivotSelectionHeuristic::NearestMaximalDeviation ||
1220 pivotSelectionHeuristic == PivotSelectionHeuristic::MaxWeightedDeviation) &&
1221 lowerValues && upperValues;
1222 bool foundPivotState = false;
1223
1224 ExplicitDijkstraQueueElement<ValueType> pivotState(unreachableDistance, 0, true);
1225 ValueType pivotStateDeviation = storm::utility::zero<ValueType>();
1226 auto const& player2Grouping = transitionMatrix.getRowGroupIndices();
1227
1228 while (!dijkstraQueue.empty()) {
1229 // Take out currently best state.
1230 auto currentDijkstraElement = *dijkstraQueue.begin();
1231 ValueType currentDistance = currentDijkstraElement.distance;
1232 uint64_t currentState = currentDijkstraElement.state;
1233 bool currentLower = currentDijkstraElement.lower;
1234 dijkstraQueue.erase(dijkstraQueue.begin());
1235
1236 if (foundPivotState && (probabilityDistances ? currentDistance < pivotState.distance : currentDistance > pivotState.distance)) {
1237 if (pivotSelectionHeuristic != PivotSelectionHeuristic::MaxWeightedDeviation) {
1238 // For the nearest maximal deviation and most probable path heuristics, future pivot states are
1239 // not important any more, because their distance will be strictly larger, so we can return the
1240 // current pivot state.
1241
1242 return ExplicitPivotStateResult<ValueType>(pivotState.state, pivotState.distance,
1243 pivotState.lower ? std::move(lowerPredecessors) : std::move(upperPredecessors));
1244 } else if (pivotStateDeviation >= currentDistance) {
1245 // If the heuristic is maximal weighted deviation and the weighted deviation for any future pivot
1246 // state is necessarily at most as high as the current one, we can abort the search.
1247 return ExplicitPivotStateResult<ValueType>(pivotState.state, pivotState.distance,
1248 pivotState.lower ? std::move(lowerPredecessors) : std::move(upperPredecessors));
1249 }
1250 }
1251
1252 // Determine whether the current state is a pivot state.
1253 bool isPivotState = false;
1254 if (minStrategyPair.getPlayer1Strategy().hasDefinedChoice(currentState)) {
1255 uint64_t player2Successor = minStrategyPair.getPlayer1Strategy().getChoice(currentState);
1256 if (minStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2Successor) &&
1257 maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2Successor) &&
1258 minStrategyPair.getPlayer2Strategy().getChoice(player2Successor) != maxStrategyPair.getPlayer2Strategy().getChoice(player2Successor)) {
1259 isPivotState = true;
1260 }
1261 }
1262 if (!isPivotState && maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(currentState)) {
1263 uint64_t player2Successor = maxStrategyPair.getPlayer1Strategy().getChoice(currentState);
1264 if (minStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2Successor) &&
1265 maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2Successor) &&
1266 minStrategyPair.getPlayer2Strategy().getChoice(player2Successor) != maxStrategyPair.getPlayer2Strategy().getChoice(player2Successor)) {
1267 isPivotState = true;
1268 }
1269 }
1270
1271 // If it is indeed a pivot state, we can potentially abort the search here.
1272 if (isPivotState) {
1273 if (considerDeviation && foundPivotState) {
1274 ValueType deviationOfCurrentState = (*upperValues)[currentState] - (*lowerValues)[currentState];
1275
1276 if (deviationOfCurrentState > pivotStateDeviation) {
1277 pivotState = currentDijkstraElement;
1278 pivotStateDeviation = deviationOfCurrentState;
1279 if (pivotSelectionHeuristic == PivotSelectionHeuristic::MaxWeightedDeviation) {
1280 // Scale the deviation with the distance (probability) for this heuristic.
1281 pivotStateDeviation *= currentDistance;
1282 }
1283 }
1284 } else if (!foundPivotState) {
1285 pivotState = currentDijkstraElement;
1286 foundPivotState = true;
1287 }
1288
1289 // If there is no need to look at other deviations, stop here.
1290 if (!considerDeviation) {
1291 return ExplicitPivotStateResult<ValueType>(pivotState.state, pivotState.distance,
1292 pivotState.lower ? std::move(lowerPredecessors) : std::move(upperPredecessors));
1293 }
1294 }
1295
1296 // We only need to search further if the state has some value deviation.
1297 if (!lowerValues || !upperValues || (*lowerValues)[currentState] < (*upperValues)[currentState]) {
1298 if (currentLower) {
1299 performDijkstraStep(dijkstraQueue, probabilityDistances, lowerDistances, lowerPredecessors, generatePredecessors, true, currentState,
1300 currentDistance, isPivotState, minStrategyPair, maxStrategyPair, player1Labeling, player2Grouping, transitionMatrix,
1301 targetStates, relevantStates);
1302 } else {
1303 performDijkstraStep(dijkstraQueue, probabilityDistances, upperDistances, upperPredecessors, generatePredecessors, false, currentState,
1304 currentDistance, isPivotState, maxStrategyPair, minStrategyPair, player1Labeling, player2Grouping, transitionMatrix,
1305 targetStates, relevantStates);
1306 }
1307 }
1308 }
1309
1310 if (foundPivotState) {
1311 return ExplicitPivotStateResult<ValueType>(pivotState.state, pivotState.distance,
1312 pivotState.lower ? std::move(lowerPredecessors) : std::move(upperPredecessors));
1313 }
1314
1315 // If we arrived at this point, we have explored all relevant states, but none of them was a pivot state,
1316 // which can happen when trying to refine using the qualitative result only.
1317 STORM_LOG_TRACE("Did not find pivot state in explicit Dijkstra search.");
1318 return boost::none;
1319}
1320
1321template<storm::dd::DdType Type, typename ValueType>
1322boost::optional<RefinementPredicates> MenuGameRefiner<Type, ValueType>::derivePredicatesFromInterpolationReversedPath(
1323 storm::dd::Odd const& odd, storm::expressions::ExpressionManager& interpolationManager, std::vector<uint64_t> const& reversedPath,
1324 std::vector<uint64_t> const& stateToOffset, std::vector<uint64_t> const& reversedLabels) const {
1325 // Build the trace of the most probable path in terms of which predicates hold in each step.
1326 auto start = std::chrono::high_resolution_clock::now();
1327 std::pair<std::vector<std::vector<storm::expressions::Expression>>, std::map<storm::expressions::Variable, storm::expressions::Expression>>
1328 traceAndVariableSubstitution = buildTraceFromReversedLabeledPath(interpolationManager, reversedPath, reversedLabels, odd, &stateToOffset);
1329 auto end = std::chrono::high_resolution_clock::now();
1330 STORM_LOG_DEBUG("Building the trace and variable substitution for interpolation from explicit most-probable paths result took "
1331 << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
1332
1333 return derivePredicatesFromInterpolationFromTrace(interpolationManager, traceAndVariableSubstitution.first, traceAndVariableSubstitution.second);
1334}
1335
1336template<storm::dd::DdType Type, typename ValueType>
1338 storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
1339 [[maybe_unused]] std::vector<uint64_t> const& player1Grouping, std::vector<uint64_t> const& player1Labeling,
1340 std::vector<uint64_t> const& player2Labeling, storm::storage::BitVector const& initialStates,
1341 storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates,
1342 ExplicitQualitativeGameResultMinMax const& qualitativeResult,
1343 storage::ExplicitGameStrategyPair const& minStrategyPair,
1344 storage::ExplicitGameStrategyPair const& maxStrategyPair) const {
1345 STORM_LOG_ASSERT(constraintStates.full(), "Constraint states are ignored. Likely buggy.");
1346 (void)constraintStates; // Disable warning, in debug mode we monitor wrong usage of this method.
1347 // Compute the set of states whose result we have for the min and max case.
1348 storm::storage::BitVector relevantStates = (qualitativeResult.getProb0Min().getStates() | qualitativeResult.getProb1Min().getStates()) &
1349 (qualitativeResult.getProb0Max().getStates() | qualitativeResult.getProb1Max().getStates());
1350
1351 boost::optional<ExplicitPivotStateResult<ValueType>> optionalPivotStateResult =
1352 pickPivotState(useInterpolation, pivotSelectionHeuristic, transitionMatrix, player1Labeling, initialStates, relevantStates, targetStates,
1353 minStrategyPair, maxStrategyPair);
1354
1355 // If there was no pivot state, continue the search.
1356 if (!optionalPivotStateResult) {
1357 STORM_LOG_TRACE("Did not find pivot state in qualitative fragment.");
1358 return false;
1359 }
1360
1361 // Otherwise, we can refine.
1362 auto const& pivotStateResult = optionalPivotStateResult.get();
1363 STORM_LOG_TRACE("Found pivot state " << pivotStateResult.pivotState << " with distance " << pivotStateResult.distance << ".");
1364
1365 // Translate the explicit states/choices to the symbolic ones, so we can reuse the predicate derivation techniques.
1366 auto const& abstractionInformation = abstractor.get().getAbstractionInformation();
1367 uint64_t pivotState = pivotStateResult.pivotState;
1368 storm::dd::Bdd<Type> symbolicPivotState = storm::dd::Bdd<Type>::getEncoding(game.getManager(), pivotState, odd, game.getRowVariables());
1369 storm::dd::Bdd<Type> minPlayer1Strategy = game.getManager().getBddZero();
1370 storm::dd::Bdd<Type> maxPlayer1Strategy = game.getManager().getBddZero();
1371 storm::dd::Bdd<Type> minPlayer2Strategy = game.getManager().getBddZero();
1372 storm::dd::Bdd<Type> maxPlayer2Strategy = game.getManager().getBddZero();
1373 if (minStrategyPair.getPlayer1Strategy().hasDefinedChoice(pivotState)) {
1374 uint64_t player2State = minStrategyPair.getPlayer1Strategy().getChoice(pivotState);
1375 STORM_LOG_ASSERT(player1Grouping[pivotState] <= player2State && player2State < player1Grouping[pivotState + 1], "Illegal choice for player 1.");
1376 minPlayer1Strategy |=
1377 symbolicPivotState && abstractionInformation.encodePlayer1Choice(player1Labeling[player2State], abstractionInformation.getPlayer1VariableCount());
1378
1379 if (minStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1380 uint64_t player2Choice = minStrategyPair.getPlayer2Strategy().getChoice(player2State);
1381 minPlayer2Strategy |=
1382 minPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1383 }
1384 if (maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1385 uint64_t player2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(player2State);
1386 maxPlayer2Strategy |=
1387 minPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1388 }
1389 }
1390 if (maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(pivotState)) {
1391 uint64_t player2State = maxStrategyPair.getPlayer1Strategy().getChoice(pivotState);
1392 STORM_LOG_ASSERT(player1Grouping[pivotState] <= player2State && player2State < player1Grouping[pivotState + 1], "Illegal choice for player 1.");
1393 maxPlayer1Strategy |=
1394 symbolicPivotState && abstractionInformation.encodePlayer1Choice(player1Labeling[player2State], abstractionInformation.getPlayer1VariableCount());
1395
1396 if (minStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1397 uint64_t player2Choice = minStrategyPair.getPlayer2Strategy().getChoice(player2State);
1398 minPlayer2Strategy |=
1399 maxPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1400 }
1401 if (maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1402 uint64_t player2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(player2State);
1403 maxPlayer2Strategy |=
1404 maxPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1405 }
1406 }
1407 boost::optional<RefinementPredicates> predicates;
1408 if (useInterpolation) {
1409 predicates = derivePredicatesFromInterpolation(game, pivotStateResult, odd);
1410 }
1411 if (!predicates) {
1412 predicates = derivePredicatesFromPivotState(game, symbolicPivotState, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy, maxPlayer2Strategy);
1413 }
1414 STORM_LOG_THROW(static_cast<bool>(predicates), storm::exceptions::InvalidStateException, "Predicates needed to continue.");
1415
1416 std::vector<storm::expressions::Expression> preparedPredicates = preprocessPredicates(predicates.get().getPredicates(), predicates.get().getSource());
1417 performRefinement(createGlobalRefinement(preparedPredicates));
1418 return true;
1419}
1420
1421template<storm::dd::DdType Type, typename ValueType>
1423 storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
1424 [[maybe_unused]] std::vector<uint64_t> const& player1Grouping, std::vector<uint64_t> const& player1Labeling,
1425 std::vector<uint64_t> const& player2Labeling, storm::storage::BitVector const& initialStates,
1426 storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates,
1427 ExplicitQuantitativeResultMinMax<ValueType> const& quantitativeResult,
1428 storage::ExplicitGameStrategyPair const& minStrategyPair,
1429 storage::ExplicitGameStrategyPair const& maxStrategyPair) const {
1430 STORM_LOG_ASSERT(constraintStates.full(), "Constraint states are ignored. Likely buggy.");
1431 (void)constraintStates; // Disable warning, in debug mode we monitor wrong usage of this method.
1432
1433 // Compute the set of states whose result we have for the min and max case.
1434 storm::storage::BitVector relevantStates(odd.getTotalOffset(), true);
1435
1436 boost::optional<ExplicitPivotStateResult<ValueType>> optionalPivotStateResult =
1437 pickPivotState(useInterpolation, pivotSelectionHeuristic, transitionMatrix, player1Labeling, initialStates, relevantStates, targetStates,
1438 minStrategyPair, maxStrategyPair, &quantitativeResult.getMin().getValues(), &quantitativeResult.getMax().getValues());
1439
1440 STORM_LOG_THROW(optionalPivotStateResult, storm::exceptions::InvalidStateException, "Did not find pivot state to proceed.");
1441
1442 // Otherwise, we can refine.
1443 auto const& pivotStateResult = optionalPivotStateResult.get();
1444 STORM_LOG_TRACE("Found pivot state " << pivotStateResult.pivotState << " with distance " << pivotStateResult.distance << ".");
1445
1446 // Translate the explicit states/choices to the symbolic ones, so we can reuse the predicate derivation techniques.
1447 auto const& abstractionInformation = abstractor.get().getAbstractionInformation();
1448 uint64_t pivotState = pivotStateResult.pivotState;
1449 storm::dd::Bdd<Type> symbolicPivotState = storm::dd::Bdd<Type>::getEncoding(game.getManager(), pivotState, odd, game.getRowVariables());
1450 storm::dd::Bdd<Type> minPlayer1Strategy = game.getManager().getBddZero();
1451 storm::dd::Bdd<Type> maxPlayer1Strategy = game.getManager().getBddZero();
1452 storm::dd::Bdd<Type> minPlayer2Strategy = game.getManager().getBddZero();
1453 storm::dd::Bdd<Type> maxPlayer2Strategy = game.getManager().getBddZero();
1454 if (minStrategyPair.getPlayer1Strategy().hasDefinedChoice(pivotState)) {
1455 uint64_t player2State = minStrategyPair.getPlayer1Strategy().getChoice(pivotState);
1456 STORM_LOG_ASSERT(player1Grouping[pivotState] <= player2State && player2State < player1Grouping[pivotState + 1], "Illegal choice for player 1.");
1457 minPlayer1Strategy |=
1458 symbolicPivotState && abstractionInformation.encodePlayer1Choice(player1Labeling[player2State], abstractionInformation.getPlayer1VariableCount());
1459
1460 if (minStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1461 uint64_t player2Choice = minStrategyPair.getPlayer2Strategy().getChoice(player2State);
1462 minPlayer2Strategy |=
1463 minPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1464 }
1465 if (maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1466 uint64_t player2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(player2State);
1467 maxPlayer2Strategy |=
1468 minPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1469 }
1470 }
1471 if (maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(pivotState)) {
1472 uint64_t player2State = maxStrategyPair.getPlayer1Strategy().getChoice(pivotState);
1473 STORM_LOG_ASSERT(player1Grouping[pivotState] <= player2State && player2State < player1Grouping[pivotState + 1], "Illegal choice for player 1.");
1474 maxPlayer1Strategy |=
1475 symbolicPivotState && abstractionInformation.encodePlayer1Choice(player1Labeling[player2State], abstractionInformation.getPlayer1VariableCount());
1476
1477 if (minStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1478 uint64_t player2Choice = minStrategyPair.getPlayer2Strategy().getChoice(player2State);
1479 minPlayer2Strategy |=
1480 maxPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1481 }
1482 if (maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(player2State)) {
1483 uint64_t player2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(player2State);
1484 maxPlayer2Strategy |=
1485 maxPlayer1Strategy && abstractionInformation.encodePlayer2Choice(player2Labeling[player2Choice], 0, game.getPlayer2Variables().size());
1486 }
1487 }
1488
1489 boost::optional<RefinementPredicates> predicates;
1490 if (useInterpolation) {
1491 predicates = derivePredicatesFromInterpolation(game, pivotStateResult, odd);
1492 }
1493 if (!predicates) {
1494 predicates = derivePredicatesFromPivotState(game, symbolicPivotState, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy, maxPlayer2Strategy);
1495 }
1496 STORM_LOG_THROW(static_cast<bool>(predicates), storm::exceptions::InvalidStateException, "Predicates needed to continue.");
1497
1498 std::vector<storm::expressions::Expression> preparedPredicates = preprocessPredicates(predicates.get().getPredicates(), predicates.get().getSource());
1499 performRefinement(createGlobalRefinement(preparedPredicates));
1500 return true;
1501}
1502
1503template<storm::dd::DdType Type, typename ValueType>
1505 SymbolicQuantitativeGameResultMinMax<Type, ValueType> const& quantitativeResult) const {
1506 STORM_LOG_TRACE("Refining after quantitative check.");
1507 // Get all relevant strategies.
1508 storm::dd::Bdd<Type> minPlayer1Strategy = quantitativeResult.min.getPlayer1Strategy();
1509 storm::dd::Bdd<Type> minPlayer2Strategy = quantitativeResult.min.getPlayer2Strategy();
1510 storm::dd::Bdd<Type> maxPlayer1Strategy = quantitativeResult.max.getPlayer1Strategy();
1511 storm::dd::Bdd<Type> maxPlayer2Strategy = quantitativeResult.max.getPlayer2Strategy();
1512
1513 // Compute all reached pivot states.
1514 PivotStateCandidatesResult<Type> pivotStateCandidatesResult =
1515 computePivotStates(game, transitionMatrixBdd, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy, maxPlayer2Strategy);
1516
1517 STORM_LOG_ASSERT(!pivotStateCandidatesResult.pivotStates.isZero(), "Unable to refine without pivot state candidates.");
1518
1519 // Now that we have the pivot state candidates, we need to pick one.
1520 SymbolicPivotStateResult<Type, ValueType> symbolicPivotStateResult =
1521 pickPivotState<Type, ValueType>(pivotSelectionHeuristic, game, pivotStateCandidatesResult, boost::none, quantitativeResult);
1522
1523 boost::optional<RefinementPredicates> predicates;
1524 if (useInterpolation) {
1525 predicates =
1526 derivePredicatesFromInterpolation(game, symbolicPivotStateResult, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy, maxPlayer2Strategy);
1527 }
1528 if (predicates) {
1529 STORM_LOG_TRACE("Obtained predicates by interpolation.");
1530 } else {
1531 predicates = derivePredicatesFromPivotState(game, symbolicPivotStateResult.pivotState, minPlayer1Strategy, minPlayer2Strategy, maxPlayer1Strategy,
1532 maxPlayer2Strategy);
1533 }
1534 STORM_LOG_THROW(static_cast<bool>(predicates), storm::exceptions::InvalidStateException, "Predicates needed to continue.");
1535
1536 std::vector<storm::expressions::Expression> preparedPredicates = preprocessPredicates(predicates.get().getPredicates(), predicates.get().getSource());
1537 performRefinement(createGlobalRefinement(preparedPredicates));
1538 return true;
1539}
1540
1542 std::size_t operator()(std::set<storm::expressions::Variable> const& set) const {
1543 return set.size();
1544 }
1545};
1546
1547template<storm::dd::DdType Type, typename ValueType>
1548std::vector<storm::expressions::Expression> MenuGameRefiner<Type, ValueType>::preprocessPredicates(
1549 std::vector<storm::expressions::Expression> const& predicates, RefinementPredicates::Source const& source) const {
1550 bool split = source == RefinementPredicates::Source::WeakestPrecondition && splitPredicates;
1551 split |= source == RefinementPredicates::Source::Interpolation && splitPredicates;
1552 split |= splitAll;
1553
1554 if (split) {
1555 auto start = std::chrono::high_resolution_clock::now();
1556 AbstractionInformation<Type> const& abstractionInformation = abstractor.get().getAbstractionInformation();
1557 std::vector<storm::expressions::Expression> cleanedAtoms;
1558
1559 std::unordered_map<std::set<storm::expressions::Variable>, std::vector<storm::expressions::Expression>, VariableSetHash> predicateClasses;
1560
1561 for (auto const& predicate : predicates) {
1562 // Split the predicates.
1563 std::vector<storm::expressions::Expression> atoms = splitter.split(predicate);
1564
1565 // Put the atoms into the right class.
1566 for (auto const& atom : atoms) {
1567 std::set<storm::expressions::Variable> vars = atom.getVariables();
1568 predicateClasses[vars].push_back(atom);
1569 }
1570 }
1571
1572 // Now clean the classes in the sense that redundant predicates are cleaned.
1573 uint64_t checkCounter = 0;
1574 for (auto& predicateClass : predicateClasses) {
1575 std::vector<storm::expressions::Expression> cleanedAtomsOfClass;
1576
1577 for (auto const& predicate : predicateClass.second) {
1578 bool addPredicate = true;
1579 for (auto const& atom : cleanedAtomsOfClass) {
1580 if (predicate.areSame(atom)) {
1581 addPredicate = false;
1582 break;
1583 }
1584
1585 ++checkCounter;
1586 if (equivalenceChecker.areEquivalentModuloNegation(predicate, atom)) {
1587 addPredicate = false;
1588 break;
1589 }
1590 }
1591 if (addPredicate) {
1592 cleanedAtomsOfClass.push_back(predicate);
1593 }
1594 }
1595
1596 predicateClass.second = std::move(cleanedAtomsOfClass);
1597 }
1598
1599 std::unordered_map<std::set<storm::expressions::Variable>, std::vector<storm::expressions::Expression>, VariableSetHash> oldPredicateClasses;
1600 for (auto const& oldPredicate : abstractionInformation.getPredicates()) {
1601 std::set<storm::expressions::Variable> vars = oldPredicate.getVariables();
1602
1603 oldPredicateClasses[vars].push_back(oldPredicate);
1604 }
1605
1606 for (auto const& predicateClass : predicateClasses) {
1607 auto oldPredicateClassIt = oldPredicateClasses.find(predicateClass.first);
1608 if (oldPredicateClassIt != oldPredicateClasses.end()) {
1609 for (auto const& newAtom : predicateClass.second) {
1610 bool addAtom = true;
1611 for (auto const& oldPredicate : oldPredicateClassIt->second) {
1612 if (newAtom.areSame(oldPredicate)) {
1613 addAtom = false;
1614 break;
1615 }
1616 ++checkCounter;
1617 if (equivalenceChecker.areEquivalentModuloNegation(newAtom, oldPredicate)) {
1618 addAtom = false;
1619 break;
1620 }
1621 }
1622 if (addAtom) {
1623 cleanedAtoms.push_back(newAtom);
1624 }
1625 }
1626 } else {
1627 cleanedAtoms.insert(cleanedAtoms.end(), predicateClass.second.begin(), predicateClass.second.end());
1628 }
1629 }
1630 auto end = std::chrono::high_resolution_clock::now();
1631 STORM_LOG_TRACE("Preprocessing predicates took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms (" << checkCounter
1632 << " checks).");
1633
1634 return cleanedAtoms;
1635 } else {
1636 // If no splitting of the predicates is required, just forward the refinement request to the abstractor.
1637 }
1638
1639 return predicates;
1640}
1641
1642template<storm::dd::DdType Type, typename ValueType>
1643std::vector<RefinementCommand> MenuGameRefiner<Type, ValueType>::createGlobalRefinement(std::vector<storm::expressions::Expression> const& predicates) const {
1644 std::vector<RefinementCommand> commands;
1645 commands.emplace_back(predicates);
1646 return commands;
1647}
1648
1649template<storm::dd::DdType Type, typename ValueType>
1650void MenuGameRefiner<Type, ValueType>::performRefinement(std::vector<RefinementCommand> const& refinementCommands, bool allowInjection) const {
1651 if (!refinementPredicatesToInject.empty() && allowInjection) {
1652 STORM_LOG_INFO("Refining with (injected) predicates.");
1653
1654 for (auto const& predicate : refinementPredicatesToInject.back()) {
1655 STORM_LOG_INFO(predicate);
1656 }
1657
1658 abstractor.get().refine(RefinementCommand(refinementPredicatesToInject.back()));
1659 refinementPredicatesToInject.pop_back();
1660 } else {
1661 for (auto const& command : refinementCommands) {
1662 STORM_LOG_INFO("Refining with " << command.getPredicates().size() << " predicates.");
1663 for (auto const& predicate : command.getPredicates()) {
1664 STORM_LOG_INFO(predicate);
1665 }
1666 if (!command.getPredicates().empty()) {
1667 abstractor.get().refine(command);
1668 }
1669 }
1670 }
1671
1672 STORM_LOG_TRACE("Current set of predicates:");
1673 for (auto const& predicate : abstractor.get().getAbstractionInformation().getPredicates()) {
1674 STORM_LOG_TRACE(predicate);
1675 }
1676}
1677
1678template<storm::dd::DdType Type, typename ValueType>
1680 return addedAllGuardsFlag;
1681}
1682
1686
1687} // namespace abstraction
1688} // namespace storm::gbar
ValueType getMax() const
Retrieves the highest function value of any encoding.
Definition Add.cpp:468
Bdd< LibraryType > greater(Add< LibraryType, ValueType > const &other) const
Retrieves the function that maps all evaluations to one whose function value in the first ADD are gre...
Definition Add.cpp:109
Add< LibraryType, ValueType > maximum(Add< LibraryType, ValueType > const &other) const
Retrieves the function that maps all evaluations to the maximum of the function values of the two ADD...
Definition Add.cpp:166
Add< LibraryType, ValueType > maxAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Max-abstracts from the given meta variables.
Definition Add.cpp:191
AddIterator< LibraryType, ValueType > begin(bool enumerateDontCareMetaVariables=true) const
Retrieves an iterator that points to the first meta variable assignment with a non-zero function valu...
Definition Add.cpp:1142
Add< LibraryType, ValueType > sumAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Sum-abstracts from the given meta variables.
Definition Add.cpp:171
Bdd< LibraryType > toBdd() const
Converts the ADD to a BDD by mapping all values unequal to zero to 1.
Definition Add.cpp:1180
Bdd< LibraryType > maxAbstractRepresentative(std::set< storm::expressions::Variable > const &metaVariables) const
Similar to maxAbstract, but does not abstract from the variables but rather picks a valuation of each...
Definition Add.cpp:198
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
virtual uint_fast64_t getNonZeroCount() const override
Retrieves the number of encodings that are mapped to a non-zero value.
Definition Bdd.cpp:507
static Bdd< LibraryType > getEncoding(DdManager< LibraryType > const &ddManager, uint64_t targetOffset, storm::dd::Odd const &odd, std::set< storm::expressions::Variable > const &metaVariables)
Constructs the BDD representation of the encoding with the given offset.
Definition Bdd.cpp:88
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
Bdd< LibraryType > existsAbstractRepresentative(std::set< storm::expressions::Variable > const &metaVariables) const
Similar to existsAbstract, but does not abstract from the variables but rather picks a valuation of e...
Definition Bdd.cpp:178
Bdd< LibraryType > ite(Bdd< LibraryType > const &thenBdd, Bdd< LibraryType > const &elseBdd) const
Performs an if-then-else with the given operands, i.e.
Definition Bdd.cpp:107
Bdd< LibraryType > exclusiveOr(Bdd< LibraryType > const &other) const
Performs a logical exclusive-or of the current and the given BDD.
Definition Bdd.cpp:151
Bdd< LibraryType > relationalProduct(Bdd< LibraryType > const &relation, std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables) const
Computes the relational product of the current BDD and the given BDD representing a relation.
Definition Bdd.cpp:214
uint_fast64_t getTotalOffset() const
Retrieves the total offset, i.e., the sum of the then- and else-offset.
Definition Odd.cpp:44
storm::storage::BitVector getEncoding(uint64_t offset, uint64_t variableCount=0) const
Retrieves the encoding for the given offset.
Definition Odd.cpp:186
bool isFalse() const
Checks if the expression is equal to the boolean literal false.
std::set< storm::expressions::Variable > getVariables() const
Retrieves the set of all variables that appear in the expression.
Expression changeManager(ExpressionManager const &newExpressionManager) const
Converts the expression to an expression over the variables of the provided expression manager.
Expression substitute(std::map< Variable, Expression > const &variableToExpressionMap) const
Substitutes all occurrences of the variables according to the given map.
bool isTrue() const
Checks if the expression is equal to the boolean literal true.
bool isInitialized() const
Checks whether the object encapsulates a base-expression.
This class is responsible for managing a set of typed variables and all expressions using these varia...
std::shared_ptr< ExpressionManager > clone() const
Creates a new expression manager with the same set of variables.
Variable declareVariableCopy(Variable const &variable)
Declares a variable that is a copy of the provided variable (i.e.
Variable getVariable(std::string const &name) const
Retrieves the expression that represents the variable with the given name.
Expression boolean(bool value) const
Creates an expression that characterizes the given boolean literal.
bool hasBooleanType() const
Checks whether the variable is of boolean type.
Definition Variable.cpp:59
virtual storm::storage::BitVector const & getStates() const =0
ExplicitQuantitativeResult< ValueType > const & getMin() const
ExplicitQuantitativeResult< ValueType > const & getMax() const
This class represents a discrete-time stochastic two-player game.
Definition MenuGame.h:14
std::set< storm::expressions::Variable > const & getProbabilisticBranchingVariables() const
Retrieves the variables used to encode additional information for the probabilistic branching in the ...
Definition MenuGame.cpp:74
storm::dd::Add< Type, ValueType > const & getExtendedTransitionMatrix() const
Retrieves the transition matrix extended by variables that encode additional information for the prob...
Definition MenuGame.cpp:69
bool addedAllGuards() const
Retrieves whether all guards were added.
void refine(std::vector< storm::expressions::Expression > const &predicates, bool allowInjection=true) const
Refines the abstractor with the given predicates.
MenuGameRefiner(MenuGameAbstractor< Type, ValueType > &abstractor, std::unique_ptr< storm::solver::SmtSolver > &&smtSolver, AbstractionOptions const &options=AbstractionOptions())
Creates a refiner for the provided abstractor.
void addPredicates(std::vector< storm::expressions::Expression > const &newPredicates)
std::vector< storm::expressions::Expression > const & getPredicates() const
storm::dd::DdManager< Type > & getManager() const
Retrieves the manager responsible for the DDs that represent this model.
Definition Model.cpp:89
std::set< storm::expressions::Variable > const & getColumnVariables() const
Retrieves the meta variables used to encode the columns of the transition matrix and the vector indic...
Definition Model.cpp:194
storm::dd::Bdd< Type > const & getInitialStates() const
Retrieves the initial states of the model.
Definition Model.cpp:104
std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const & getRowColumnMetaVariablePairs() const
Retrieves the pairs of row and column meta variables.
Definition Model.cpp:220
std::set< storm::expressions::Variable > const & getRowVariables() const
Retrieves the meta variables used to encode the rows of the transition matrix and the vector indices.
Definition Model.cpp:189
virtual std::set< storm::expressions::Variable > const & getNondeterminismVariables() const override
Retrieves the meta variables used to encode the nondeterminism in the model.
std::set< storm::expressions::Variable > const & getPlayer1Variables() const
Retrieeves the set of meta variables used to encode the nondeterministic choices of player 1.
std::set< storm::expressions::Variable > const & getPlayer2Variables() const
Retrieeves the set of meta variables used to encode the nondeterministic choices of player 2.
CheckResult
possible check results
Definition SmtSolver.h:24
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
const_reverse_iterator rbegin() const
Returns a reverse iterator to the indices of the set bits in the bit vector.
bool full() const
Retrieves whether all bits are set in this bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
bool hasDefinedChoice(uint64_t state) const
uint64_t getChoice(uint64_t state) const
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
#define STORM_LOG_INFO(message)
Definition logging.h:34
#define STORM_LOG_DEBUG(message)
Definition logging.h:28
#define STORM_LOG_TRACE(message)
Definition logging.h:22
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
Expression iff(Expression const &first, Expression const &second)
SymbolicPivotStateResult< Type, ValueType > pickPivotState(PivotSelectionHeuristic const &heuristic, storm::gbar::abstraction::MenuGame< Type, ValueType > const &game, PivotStateCandidatesResult< Type > const &pivotStateCandidateResult, boost::optional< SymbolicQualitativeGameResultMinMax< Type > > const &qualitativeResult, boost::optional< SymbolicQuantitativeGameResultMinMax< Type, ValueType > > const &quantitativeResult)
PivotStateCandidatesResult< Type > computePivotStates(storm::gbar::abstraction::MenuGame< Type, ValueType > const &game, storm::dd::Bdd< Type > const &transitionMatrixBdd, storm::dd::Bdd< Type > const &minPlayer1Strategy, storm::dd::Bdd< Type > const &minPlayer2Strategy, storm::dd::Bdd< Type > const &maxPlayer1Strategy, storm::dd::Bdd< Type > const &maxPlayer2Strategy)
SymbolicMostProbablePathsResult< Type, ValueType > getMostProbablePathSpanningTree(storm::gbar::abstraction::MenuGame< Type, ValueType > const &game, storm::dd::Bdd< Type > const &transitionFilter)
void performDijkstraStep(std::set< ExplicitDijkstraQueueElement< ValueType >, ExplicitDijkstraQueueElementLess< ValueType > > &dijkstraQueue, bool probabilityDistances, std::vector< ValueType > &distances, std::vector< std::pair< uint64_t, uint64_t > > &predecessors, bool generatePredecessors, bool lower, uint64_t currentState, ValueType const &currentDistance, bool isPivotState, storm::storage::ExplicitGameStrategyPair const &strategyPair, storm::storage::ExplicitGameStrategyPair const &otherStrategyPair, std::vector< uint64_t > const &player1Labeling, std::vector< uint64_t > const &player2Grouping, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::BitVector const &targetStates, storm::storage::BitVector const &relevantStates)
std::pair< storm::RationalNumber, storm::RationalNumber > count(std::vector< storm::storage::BitVector > const &origSets, std::vector< storm::storage::BitVector > const &intersects, std::vector< storm::storage::BitVector > const &intersectsInfo, storm::RationalNumber val, bool plus, uint64_t remdepth)
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
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
TargetType convertNumber(SourceType const &number)
solver::OptimizationDirection OptimizationDirection
Options that configure the game-based abstraction-refinement procedure.
bool addAllGuards
A flag indicating whether all guards are added as initial predicates.
ExplicitDijkstraQueueElement(ValueType const &distance, uint64_t state, bool lower)
bool operator()(ExplicitDijkstraQueueElement< ValueType > const &a, ExplicitDijkstraQueueElement< ValueType > const &b) const
static const uint64_t NO_PREDECESSOR
The value filled in for states without predecessors in the search.
std::vector< std::pair< uint64_t, uint64_t > > predecessors
The predecessors for the states to obtain the given distance.
boost::optional< SymbolicMostProbablePathsResult< Type, ValueType > > symbolicMostProbablePathsResult
SymbolicPivotStateResult(storm::dd::Bdd< Type > const &pivotState, storm::OptimizationDirection fromDirection, boost::optional< SymbolicMostProbablePathsResult< Type, ValueType > > const &symbolicMostProbablePathsResult=boost::none)
std::size_t operator()(std::set< storm::expressions::Variable > const &set) const