Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GameBasedMdpModelChecker.cpp
Go to the documentation of this file.
2
12#include "storm/io/file.h"
39#include "storm/utility/prism.h"
41
42namespace storm::gbar {
43namespace modelchecker {
44
45using detail::PreviousExplicitResult;
51
52template<storm::dd::DdType Type, typename ModelType>
55 std::shared_ptr<storm::utility::solver::SmtSolverFactory> const& smtSolverFactory)
56 : options(options),
57 smtSolverFactory(smtSolverFactory),
58 comparator(storm::utility::convertNumber<ValueType>(storm::settings::getModule<storm::settings::modules::AbstractionSettings>().getPrecision()),
59 storm::settings::getModule<storm::settings::modules::AbstractionSettings>().getRelativeTerminationCriterion()),
60 reuseQualitativeResults(false),
61 reuseQuantitativeResults(false),
62 solveMode(storm::settings::getModule<storm::settings::modules::AbstractionSettings>().getSolveMode()),
63 debug(storm::settings::getModule<storm::settings::modules::AbstractionSettings>().isDebugSet()) {
64 if (model.hasUndefinedConstants()) {
65 auto undefinedConstants = model.getUndefinedConstants();
66 std::vector<std::string> undefinedConstantNames;
67 for (auto undefinedConstant : undefinedConstants) {
68 undefinedConstantNames.emplace_back(undefinedConstant.getName());
69 }
70
72 "Model contains undefined constants ("
73 << boost::algorithm::join(undefinedConstantNames, ",")
74 << "). Game-based abstraction can treat such models, but you should make sure that you did not simply forget to define these "
75 "constants. In particular, it may be necessary to constrain the values of the undefined constants.");
76 }
77
78 if (model.isPrismProgram()) {
79 storm::prism::Program const& originalProgram = model.asPrismProgram();
82 storm::exceptions::NotSupportedException, "Currently only DTMCs/MDPs are supported by the game-based model checker.");
83
84 auto flattenStart = std::chrono::high_resolution_clock::now();
85 // Flatten the modules if there is more than one.
86 if (originalProgram.getNumberOfModules() > 1) {
87 preprocessedModel = originalProgram.substituteFormulas().flattenModules(this->smtSolverFactory);
88 } else {
89 preprocessedModel = originalProgram;
90 }
91 auto flattenEnd = std::chrono::high_resolution_clock::now();
92 STORM_LOG_INFO("Flattened model in " << std::chrono::duration_cast<std::chrono::milliseconds>(flattenEnd - flattenStart).count() << "ms.");
93
94 STORM_LOG_TRACE("Game-based model checker got program " << preprocessedModel.asPrismProgram());
95 } else {
96 storm::jani::Model const& originalModel = model.asJaniModel();
98 storm::exceptions::NotSupportedException, "Currently only DTMCs/MDPs are supported by the game-based model checker.");
99
100 // Flatten the parallel composition.
101 preprocessedModel = model.asJaniModel().flattenComposition();
102 }
103
105 storm::settings::modules::AbstractionSettings::ReuseMode reuseMode = abstractionSettings.getReuseMode();
106 reuseQualitativeResults = reuseMode == storm::settings::modules::AbstractionSettings::ReuseMode::All ||
108 reuseQuantitativeResults = reuseMode == storm::settings::modules::AbstractionSettings::ReuseMode::All ||
110 maximalNumberOfAbstractions = abstractionSettings.getMaximalAbstractionCount();
111 fixPlayer1Strategy = abstractionSettings.isFixPlayer1StrategySet();
112 fixPlayer2Strategy = abstractionSettings.isFixPlayer2StrategySet();
113}
114
115template<storm::dd::DdType Type, typename ModelType>
121
122template<storm::dd::DdType Type, typename ModelType>
123std::unique_ptr<storm::modelchecker::CheckResult> GameBasedMdpModelChecker<Type, ModelType>::computeUntilProbabilities(
125 storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();
126 std::map<std::string, storm::expressions::Expression> labelToExpressionMapping;
127 if (preprocessedModel.isPrismProgram()) {
128 labelToExpressionMapping = preprocessedModel.asPrismProgram().getLabelToExpressionMapping();
129 } else {
130 storm::jani::Model const& janiModel = preprocessedModel.asJaniModel();
131 for (auto const& variable : janiModel.getGlobalVariables().getBooleanVariables()) {
132 if (variable.isTransient()) {
133 labelToExpressionMapping[variable.getName()] = janiModel.getLabelExpression(variable);
134 }
135 }
136 }
137
138 storm::expressions::Expression constraintExpression =
139 pathFormula.getLeftSubformula().toExpression(preprocessedModel.getManager(), labelToExpressionMapping);
140 storm::expressions::Expression targetStateExpression =
141 pathFormula.getRightSubformula().toExpression(preprocessedModel.getManager(), labelToExpressionMapping);
142
143 return performGameBasedAbstractionRefinement(env, checkTask.template substituteFormula<storm::logic::Formula>(pathFormula), constraintExpression,
144 targetStateExpression);
145}
146
147template<storm::dd::DdType Type, typename ModelType>
150 storm::logic::EventuallyFormula const& pathFormula = checkTask.getFormula();
151 std::map<std::string, storm::expressions::Expression> labelToExpressionMapping;
152 if (preprocessedModel.isPrismProgram()) {
153 labelToExpressionMapping = preprocessedModel.asPrismProgram().getLabelToExpressionMapping();
154 } else {
155 storm::jani::Model const& janiModel = preprocessedModel.asJaniModel();
156 for (auto const& variable : janiModel.getGlobalVariables().getBooleanVariables()) {
157 if (variable.isTransient()) {
158 labelToExpressionMapping[variable.getName()] = janiModel.getLabelExpression(variable);
159 }
160 }
161 }
162
163 storm::expressions::Expression constraintExpression = preprocessedModel.getManager().boolean(true);
164 storm::expressions::Expression targetStateExpression = pathFormula.getSubformula().toExpression(preprocessedModel.getManager(), labelToExpressionMapping);
165
166 return performGameBasedAbstractionRefinement(env, checkTask.template substituteFormula<storm::logic::Formula>(pathFormula), constraintExpression,
167 targetStateExpression);
168}
169
170template<storm::dd::DdType Type, typename ValueType>
171std::unique_ptr<storm::modelchecker::CheckResult> checkForResultAfterQualitativeCheck(
173 storm::dd::Bdd<Type> const& initialStates, storm::dd::Bdd<Type> const& prob0, storm::dd::Bdd<Type> const& prob1) {
174 std::unique_ptr<storm::modelchecker::CheckResult> result;
175
176 if (checkTask.isBoundSet()) {
177 // Despite having a bound, we create a quantitative result so that the next layer can perform the comparison.
178
179 if (player2Direction == storm::OptimizationDirection::Minimize) {
181 if ((prob1 && initialStates) == initialStates) {
182 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
183 storm::utility::one<ValueType>());
184 }
185 } else {
186 if (!(prob1 && initialStates).isZero()) {
187 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
188 storm::utility::one<ValueType>());
189 }
190 }
191 } else if (player2Direction == storm::OptimizationDirection::Maximize) {
193 if ((prob0 && initialStates) == initialStates) {
194 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
195 storm::utility::zero<ValueType>());
196 }
197 } else {
198 if (!(prob0 && initialStates).isZero()) {
199 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
200 storm::utility::zero<ValueType>());
201 }
202 }
203 }
204 } else {
205 if (player2Direction == storm::OptimizationDirection::Minimize && (prob1 && initialStates) == initialStates) {
206 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
207 storm::utility::one<ValueType>());
208 } else if (player2Direction == storm::OptimizationDirection::Maximize && (prob0 && initialStates) == initialStates) {
209 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
210 storm::utility::zero<ValueType>());
211 }
212 }
213
214 return result;
215}
216
217template<storm::dd::DdType Type, typename ValueType>
218std::unique_ptr<storm::modelchecker::CheckResult> checkForResultAfterQualitativeCheck(
220 SymbolicQualitativeGameResultMinMax<Type> const& qualitativeResult) {
221 // Check whether we can already give the answer based on the current information.
222 std::unique_ptr<storm::modelchecker::CheckResult> result =
223 checkForResultAfterQualitativeCheck<Type, ValueType>(checkTask, storm::OptimizationDirection::Minimize, initialStates,
224 qualitativeResult.prob0Min.getPlayer1States(), qualitativeResult.prob1Min.getPlayer1States());
225 if (result) {
226 return result;
227 }
228 result = checkForResultAfterQualitativeCheck<Type, ValueType>(checkTask, storm::OptimizationDirection::Maximize, initialStates,
229 qualitativeResult.prob0Max.getPlayer1States(), qualitativeResult.prob1Max.getPlayer1States());
230 if (result) {
231 return result;
232 }
233 return result;
234}
235
236template<typename ValueType>
237std::unique_ptr<storm::modelchecker::CheckResult> checkForResultAfterQualitativeCheck(
239 storm::storage::BitVector const& initialStates, storm::storage::BitVector const& prob0, storm::storage::BitVector const& prob1) {
240 std::unique_ptr<storm::modelchecker::CheckResult> result;
241
242 if (checkTask.isBoundSet()) {
243 // Despite having a bound, we create a quantitative result so that the next layer can perform the comparison.
244
245 if (player2Direction == storm::OptimizationDirection::Minimize) {
247 if (initialStates.isSubsetOf(prob1)) {
248 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
249 storm::utility::one<ValueType>());
250 }
251 } else {
252 if (!initialStates.isDisjointFrom(prob1)) {
253 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
254 storm::utility::one<ValueType>());
255 }
256 }
257 } else if (player2Direction == storm::OptimizationDirection::Maximize) {
259 if (initialStates.isSubsetOf(prob0)) {
260 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
261 storm::utility::zero<ValueType>());
262 }
263 } else {
264 if (!initialStates.isDisjointFrom(prob0)) {
265 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
266 storm::utility::zero<ValueType>());
267 }
268 }
269 }
270 } else {
271 if (player2Direction == storm::OptimizationDirection::Minimize && initialStates.isSubsetOf(prob1)) {
272 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
273 storm::utility::one<ValueType>());
274 } else if (player2Direction == storm::OptimizationDirection::Maximize && initialStates.isSubsetOf(prob0)) {
275 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
276 storm::utility::zero<ValueType>());
277 }
278 }
279
280 return result;
281}
282
283template<typename ValueType>
284std::unique_ptr<storm::modelchecker::CheckResult> checkForResultAfterQualitativeCheck(
286 ExplicitQualitativeGameResultMinMax const& qualitativeResult) {
287 // Check whether we can already give the answer based on the current information.
288 std::unique_ptr<storm::modelchecker::CheckResult> result =
289 checkForResultAfterQualitativeCheck<ValueType>(checkTask, storm::OptimizationDirection::Minimize, initialStates,
290 qualitativeResult.prob0Min.getPlayer1States(), qualitativeResult.prob1Min.getPlayer1States());
291 if (result) {
292 return result;
293 }
294 result = checkForResultAfterQualitativeCheck<ValueType>(checkTask, storm::OptimizationDirection::Maximize, initialStates,
295 qualitativeResult.prob0Max.getPlayer1States(), qualitativeResult.prob1Max.getPlayer1States());
296 if (result) {
297 return result;
298 }
299 return result;
300}
301
302template<typename ValueType>
303std::unique_ptr<storm::modelchecker::CheckResult> checkForResultAfterQuantitativeCheck(
305 std::pair<ValueType, ValueType> const& initialValueRange) {
306 std::unique_ptr<storm::modelchecker::CheckResult> result;
307
308 // If the minimum value exceeds an upper threshold or the maximum value is below a lower threshold, we can
309 // return the value because the property will definitely hold. Vice versa, if the minimum value exceeds an
310 // upper bound or the maximum value is below a lower bound, the property will definitely not hold and we can
311 // return the value.
312 if (!checkTask.isBoundSet()) {
313 return result;
314 }
315
316 ValueType const& lowerValue = initialValueRange.first;
317 ValueType const& upperValue = initialValueRange.second;
318
319 storm::logic::ComparisonType comparisonType = checkTask.getBoundComparisonType();
320 ValueType threshold = checkTask.getBoundThreshold();
321
322 if (storm::logic::isLowerBound(comparisonType)) {
323 if (player2Direction == storm::OptimizationDirection::Minimize) {
324 if ((storm::logic::isStrict(comparisonType) && lowerValue > threshold) || (!storm::logic::isStrict(comparisonType) && lowerValue >= threshold)) {
325 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0), lowerValue);
326 }
327 } else {
328 if ((storm::logic::isStrict(comparisonType) && upperValue <= threshold) || (!storm::logic::isStrict(comparisonType) && upperValue < threshold)) {
329 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0), upperValue);
330 }
331 }
332 } else {
333 if (player2Direction == storm::OptimizationDirection::Maximize) {
334 if ((storm::logic::isStrict(comparisonType) && upperValue < threshold) || (!storm::logic::isStrict(comparisonType) && upperValue <= threshold)) {
335 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0), upperValue);
336 }
337 } else {
338 if ((storm::logic::isStrict(comparisonType) && lowerValue >= threshold) || (!storm::logic::isStrict(comparisonType) && lowerValue > threshold)) {
339 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0), lowerValue);
340 }
341 }
342 }
343
344 return result;
345}
346
347template<typename ValueType>
348std::unique_ptr<storm::modelchecker::CheckResult> checkForResultAfterQuantitativeCheck(ValueType const& minValue, ValueType const& maxValue,
350 std::unique_ptr<storm::modelchecker::CheckResult> result;
351
352 // If the lower and upper bounds are close enough, we can return the result.
353 if (comparator.isEqual(minValue, maxValue)) {
354 result = std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(storm::storage::sparse::state_type(0),
355 (minValue + maxValue) / ValueType(2));
356 }
357
358 return result;
359}
360
361template<storm::dd::DdType Type, typename ValueType>
363 Environment const& env, storm::OptimizationDirection const& player1Direction, storm::OptimizationDirection const& player2Direction,
365 boost::optional<SymbolicQuantitativeGameResult<Type, ValueType>> const& startInfo = boost::none) {
366 STORM_LOG_TRACE("Performing quantative solution step. Player 1: " << player1Direction << ", player 2: " << player2Direction << ".");
367
368 // Compute the ingredients of the equation system.
369 storm::dd::Add<Type, ValueType> maybeStatesAdd = maybeStates.template toAdd<ValueType>();
370 storm::dd::Add<Type, ValueType> submatrix = maybeStatesAdd * game.getTransitionMatrix();
371 storm::dd::Add<Type, ValueType> prob1StatesAsColumn = prob1States.template toAdd<ValueType>().swapVariables(game.getRowColumnMetaVariablePairs());
372 storm::dd::Add<Type, ValueType> subvector = submatrix * prob1StatesAsColumn;
373 subvector = subvector.sumAbstract(game.getColumnVariables());
374
375 // Cut away all columns targeting non-maybe states.
376 submatrix *= maybeStatesAdd.swapVariables(game.getRowColumnMetaVariablePairs());
377
378 // Cut the starting vector to the maybe states of this query.
380 if (startInfo) {
381 startVector = startInfo.get().values * maybeStatesAdd;
382 } else {
383 startVector = game.getManager().template getAddZero<ValueType>();
384 }
385
386 // Create the solver and solve the equation system.
388 std::unique_ptr<storm::solver::SymbolicGameSolver<Type, ValueType>> solver =
389 solverFactory.create(submatrix, maybeStates, game.getIllegalPlayer1Mask(), game.getIllegalPlayer2Mask(), game.getRowVariables(),
391 solver->setGeneratePlayersStrategies(true);
392 auto values = solver->solveGame(env, player1Direction, player2Direction, startVector, subvector,
393 startInfo ? boost::make_optional(startInfo.get().getPlayer1Strategy()) : boost::none,
394 startInfo ? boost::make_optional(startInfo.get().getPlayer2Strategy()) : boost::none);
395 return SymbolicQuantitativeGameResult<Type, ValueType>(std::make_pair(storm::utility::zero<ValueType>(), storm::utility::one<ValueType>()), values,
396 solver->getPlayer1Strategy(), solver->getPlayer2Strategy());
397}
398
399template<storm::dd::DdType Type, typename ValueType>
401 Environment const& env, storm::OptimizationDirection player1Direction, storm::OptimizationDirection player2Direction,
403 storm::dd::Add<Type, ValueType> const& initialStatesAdd, storm::dd::Bdd<Type> const& maybeStates,
404 boost::optional<SymbolicQuantitativeGameResult<Type, ValueType>> const& startInfo = boost::none) {
405 bool min = player2Direction == storm::OptimizationDirection::Minimize;
407
408 // We fix the strategies. That is, we take the decisions of the strategies obtained in the qualitiative
409 // preprocessing if possible.
410 storm::dd::Bdd<Type> combinedPlayer1QualitativeStrategies;
411 storm::dd::Bdd<Type> combinedPlayer2QualitativeStrategies;
412 if (min) {
413 combinedPlayer1QualitativeStrategies = (qualitativeResult.prob0Min.getPlayer1Strategy() || qualitativeResult.prob1Min.getPlayer1Strategy());
414 combinedPlayer2QualitativeStrategies = (qualitativeResult.prob0Min.getPlayer2Strategy() || qualitativeResult.prob1Min.getPlayer2Strategy());
415 } else {
416 combinedPlayer1QualitativeStrategies = (qualitativeResult.prob0Max.getPlayer1Strategy() || qualitativeResult.prob1Max.getPlayer1Strategy());
417 combinedPlayer2QualitativeStrategies = (qualitativeResult.prob0Max.getPlayer2Strategy() || qualitativeResult.prob1Max.getPlayer2Strategy());
418 }
419
420 result.player1Strategy = combinedPlayer1QualitativeStrategies;
421 result.player2Strategy = combinedPlayer2QualitativeStrategies;
422 result.values = game.getManager().template getAddZero<ValueType>();
423
424 auto start = std::chrono::high_resolution_clock::now();
425 if (!maybeStates.isZero()) {
426 STORM_LOG_TRACE("Solving " << maybeStates.getNonZeroCount() << " maybe states.");
427
428 // Solve the quantitative values of maybe states.
429 result = solveMaybeStates(env, player1Direction, player2Direction, game, maybeStates,
430 min ? qualitativeResult.prob1Min.getPlayer1States() : qualitativeResult.prob1Max.getPlayer1States(), startInfo);
431
432 // Cut the obtained strategies to the reachable states of the game.
433 result.getPlayer1Strategy() &= game.getReachableStates();
434 result.getPlayer2Strategy() &= game.getReachableStates();
435
436 // Extend the values of the maybe states by the qualitative values.
437 result.values += min ? qualitativeResult.prob1Min.getPlayer1States().template toAdd<ValueType>()
438 : qualitativeResult.prob1Max.getPlayer1States().template toAdd<ValueType>();
439 } else {
440 STORM_LOG_TRACE("No " << (player2Direction == storm::OptimizationDirection::Minimize ? "min" : "max") << " maybe states.");
441
442 // Extend the values of the maybe states by the qualitative values.
443 result.values += min ? qualitativeResult.prob1Min.getPlayer1States().template toAdd<ValueType>()
444 : qualitativeResult.prob1Max.getPlayer1States().template toAdd<ValueType>();
445 }
446
447 // Construct an ADD holding the initial values of initial states and extract the bound on the initial states.
448 storm::dd::Add<Type, ValueType> initialStateValueAdd = initialStatesAdd * result.values;
449
450 ValueType maxValueOverInitialStates = initialStateValueAdd.getMax();
451 initialStateValueAdd += (!game.getInitialStates()).template toAdd<ValueType>();
452 ValueType minValueOverInitialStates = initialStateValueAdd.getMin();
453
454 result.initialStatesRange = std::make_pair(minValueOverInitialStates, maxValueOverInitialStates);
455
456 result.player1Strategy =
457 combinedPlayer1QualitativeStrategies.existsAbstract(game.getPlayer1Variables()).ite(combinedPlayer1QualitativeStrategies, result.getPlayer1Strategy());
458 result.player2Strategy =
459 combinedPlayer2QualitativeStrategies.existsAbstract(game.getPlayer2Variables()).ite(combinedPlayer2QualitativeStrategies, result.getPlayer2Strategy());
460
461 auto end = std::chrono::high_resolution_clock::now();
462 STORM_LOG_TRACE("Obtained quantitative " << (min ? "lower" : "upper") << " bound "
463 << (min ? result.getInitialStatesRange().first : result.getInitialStatesRange().second) << " in "
464 << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms.");
465
466 return result;
467}
468
469template<typename ValueType>
471 Environment const& env, storm::OptimizationDirection player1Direction, storm::OptimizationDirection player2Direction,
472 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint64_t> const& player1Groups,
473 ExplicitQualitativeGameResultMinMax const& qualitativeResult, storm::storage::BitVector const& maybeStates, storage::ExplicitGameStrategyPair& strategyPair,
474 storm::dd::Odd const& odd, ExplicitQuantitativeResult<ValueType> const* startingQuantitativeResult = nullptr,
475 storage::ExplicitGameStrategyPair const* startingStrategyPair = nullptr,
476 boost::optional<PreviousExplicitResult<ValueType>> const& previousResult = boost::none) {
477 bool player2Min = player2Direction == storm::OptimizationDirection::Minimize;
478 auto const& player1Prob1States = player2Min ? qualitativeResult.getProb1Min().asExplicitQualitativeGameResult().getPlayer1States()
480 auto const& player2Prob0States = player2Min ? qualitativeResult.getProb0Min().asExplicitQualitativeGameResult().getPlayer2States()
482 auto const& player2Prob1States = player2Min ? qualitativeResult.getProb1Min().asExplicitQualitativeGameResult().getPlayer2States()
484
485 ExplicitQuantitativeResult<ValueType> result(maybeStates.size());
486 storm::utility::vector::setVectorValues(result.getValues(), player1Prob1States, storm::utility::one<ValueType>());
487
488 // If there are no maybe states, there is nothing we need to solve.
489 if (maybeStates.empty()) {
490 return result;
491 }
492
493 // If there is a previous result, unpack the previous values with respect to the new ODD.
494 if (previousResult) {
495 STORM_LOG_ASSERT(player2Min, "Can only reuse previous values when minimizing.");
496 previousResult.get().odd.oldToNewIndex(odd, [&previousResult, &result, player2Min, player1Prob1States](uint64_t oldOffset, uint64_t newOffset) {
497 if (!player1Prob1States.get(newOffset)) {
498 result.getValues()[newOffset] =
499 player2Min ? previousResult.get().values.getValues()[oldOffset] : previousResult.get().values.getValues()[oldOffset];
500 }
501 });
502 }
503
504 // Otherwise, we need to solve a (sub)game.
505 STORM_LOG_TRACE("[" << player1Direction << ", " << player2Direction << "]: Solving " << maybeStates.getNumberOfSetBits() << " maybe states.");
506
507 // Create the game by selecting all maybe player 2 states (non-prob0/1) of all maybe player 1 states.
508 std::vector<uint64_t> subPlayer1Groups(maybeStates.getNumberOfSetBits() + 1);
509 uint64_t position = 0;
510 uint64_t previousPlayer2States = 0;
511 storm::storage::BitVector player2MaybeStates(transitionMatrix.getRowGroupCount());
512 for (auto state : maybeStates) {
513 subPlayer1Groups[position] = previousPlayer2States;
514
515 bool hasMaybePlayer2Successor = false;
516 for (uint64_t player2State = player1Groups[state]; player2State < player1Groups[state + 1]; ++player2State) {
517 if (!player2Prob0States.get(player2State) && !player2Prob1States.get(player2State)) {
518 player2MaybeStates.set(player2State);
519 hasMaybePlayer2Successor = true;
520 ++previousPlayer2States;
521 }
522 }
523 STORM_LOG_ASSERT(hasMaybePlayer2Successor, "Player 1 maybe state has no player2 maybe successor.");
524 ++position;
525 }
526 subPlayer1Groups.back() = previousPlayer2States;
527
528 // Create the player 2 matrix using the maybe player 2 states.
529 storm::storage::SparseMatrix<ValueType> submatrix = transitionMatrix.getSubmatrix(true, player2MaybeStates, maybeStates);
530 std::vector<ValueType> b = transitionMatrix.getConstrainedRowGroupSumVector(player2MaybeStates, player1Prob1States);
531
532 // Set up game solver.
533 auto gameSolver = storm::solver::GameSolverFactory<ValueType>().create(env, subPlayer1Groups, submatrix);
534
535 // Prepare the value storage for the maybe states. If the starting values were given, extract them now.
536 std::vector<ValueType> values(maybeStates.getNumberOfSetBits());
537 if (startingQuantitativeResult) {
538 storm::utility::vector::selectVectorValues(values, maybeStates, startingQuantitativeResult->getValues());
539 }
540 if (previousResult) {
541 STORM_LOG_ASSERT(!startingQuantitativeResult, "Cannot take two different hints.");
542 storm::utility::vector::selectVectorValues(values, maybeStates, result.getValues());
543 }
544
545 // Prepare scheduler storage.
546 std::vector<uint64_t> player1Scheduler(subPlayer1Groups.size() - 1);
547 std::vector<uint64_t> player2Scheduler(submatrix.getRowGroupCount());
548 if (startingStrategyPair) {
549 // If the starting strategy pair was provided, we need to extract the choices of the maybe states here.
550 uint64_t maybeStatePosition = 0;
551 previousPlayer2States = 0;
552 for (auto state : maybeStates) {
553 uint64_t chosenPlayer2State = startingStrategyPair->getPlayer1Strategy().getChoice(state);
554
555 uint64_t previousPlayer2MaybeStatesForState = 0;
556 for (uint64_t player2State = player1Groups[state]; player2State < player1Groups[state + 1]; ++player2State) {
557 if (player2MaybeStates.get(player2State)) {
558 if (player2State == chosenPlayer2State) {
559 player1Scheduler[maybeStatePosition] = previousPlayer2MaybeStatesForState;
560 }
561
562 // Copy over the player 2 action (modulo making it local) as all rows for the player 2 state are taken.
563 if (startingStrategyPair->getPlayer2Strategy().hasDefinedChoice(player2State)) {
564 player2Scheduler[previousPlayer2States] =
565 startingStrategyPair->getPlayer2Strategy().getChoice(player2State) - transitionMatrix.getRowGroupIndices()[player2State];
566 } else {
567 player2Scheduler[previousPlayer2States] = 0;
568 }
569
570 ++previousPlayer2MaybeStatesForState;
571 ++previousPlayer2States;
572 }
573 }
574
575 ++maybeStatePosition;
576 }
577 STORM_LOG_ASSERT(previousPlayer2States == submatrix.getRowGroupCount(), "Expected correct number of player 2 states.");
578 }
579
580 // Solve actual game and track schedulers.
581 gameSolver->solveGame(env, player1Direction, player2Direction, values, b, &player1Scheduler, &player2Scheduler);
582
583 // Set values according to quantitative result (qualitative result has already been taken care of).
584 storm::utility::vector::setVectorValues(result.getValues(), maybeStates, values);
585
586 // Obtain strategies from solver and fuse them with the pre-existing strategy pair for the qualitative result.
587 uint64_t previousPlayer1MaybeStates = 0;
588 uint64_t previousPlayer2MaybeStates = 0;
589 for (auto state : maybeStates) {
590 uint64_t previousPlayer2MaybeStatesForState = 0;
591 bool madePlayer1Choice = false;
592 for (uint64_t player2State = player1Groups[state]; player2State < player1Groups[state + 1]; ++player2State) {
593 if (player1Scheduler[previousPlayer1MaybeStates] == previousPlayer2MaybeStatesForState) {
594 strategyPair.getPlayer1Strategy().setChoice(state, player2State);
595 madePlayer1Choice = true;
596 }
597
598 if (player2MaybeStates.get(player2State)) {
599 strategyPair.getPlayer2Strategy().setChoice(player2State,
600 transitionMatrix.getRowGroupIndices()[player2State] + player2Scheduler[previousPlayer2MaybeStates]);
601
602 ++previousPlayer2MaybeStatesForState;
603 ++previousPlayer2MaybeStates;
604 }
605 }
606 STORM_LOG_ASSERT(madePlayer1Choice, "[" << player1Direction << "]: player 1 state " << state
607 << " did not make a choice, scheduler: " << player1Scheduler[previousPlayer1MaybeStates] << ".");
608
609 ++previousPlayer1MaybeStates;
610 }
611
612 return result;
613}
614
615template<storm::dd::DdType Type, typename ModelType>
616std::unique_ptr<storm::modelchecker::CheckResult> GameBasedMdpModelChecker<Type, ModelType>::performGameBasedAbstractionRefinement(
618 storm::expressions::Expression const& constraintExpression, storm::expressions::Expression const& targetStateExpression) {
619 STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::InvalidPropertyException,
620 "The game-based abstraction refinement model checker can only compute the result for the initial states.");
621
622 // Optimization: do not compute both bounds if not necessary (e.g. if bound given and exceeded, etc.)
623 totalWatch.start();
624
625 // Set up initial predicates.
626 setupWatch.start();
627 std::vector<storm::expressions::Expression> initialPredicates = getInitialPredicates(constraintExpression, targetStateExpression);
628
629 // Derive the optimization direction for player 1 (assuming menu-game abstraction).
630 storm::OptimizationDirection player1Direction = getPlayer1Direction(checkTask);
631
632 // Create the abstractor.
633 storm::gbar::abstraction::MenuGameAbstractorOptions abstractorOptions(std::move(options.constraints));
634 if (preprocessedModel.isPrismProgram()) {
635 abstractor = std::make_shared<storm::gbar::abstraction::prism::PrismMenuGameAbstractor<Type, ValueType>>(preprocessedModel.asPrismProgram(),
636 smtSolverFactory, abstractorOptions);
637 } else {
638 abstractor = std::make_shared<storm::gbar::abstraction::jani::JaniMenuGameAbstractor<Type, ValueType>>(preprocessedModel.asJaniModel(),
639 smtSolverFactory, abstractorOptions);
640 }
641 std::unique_ptr<storm::modelchecker::CheckResult> result;
642 abstractor->getDdManager().execute([&]() {
643 if (!constraintExpression.isTrue()) {
644 abstractor->addTerminalStates(!constraintExpression);
645 }
646 abstractor->addTerminalStates(targetStateExpression);
647 abstractor->setTargetStates(targetStateExpression);
648
649 // Create a refiner that can be used to refine the abstraction when needed.
650 storm::gbar::abstraction::MenuGameRefinerOptions refinerOptions(std::move(options.injectedRefinementPredicates));
651 storm::gbar::abstraction::MenuGameRefiner<Type, ValueType> refiner(*abstractor, smtSolverFactory->create(preprocessedModel.getManager()),
652 refinerOptions);
653 refiner.refine(initialPredicates, false);
654
655 storm::dd::Bdd<Type> globalConstraintStates = abstractor->getStates(constraintExpression);
656 storm::dd::Bdd<Type> globalTargetStates = abstractor->getStates(targetStateExpression);
657 setupWatch.stop();
658
659 // Enter the main-loop of abstraction refinement.
660 boost::optional<SymbolicQualitativeGameResultMinMax<Type>> previousSymbolicQualitativeResult = boost::none;
661 boost::optional<SymbolicQuantitativeGameResult<Type, ValueType>> previousSymbolicMinQuantitativeResult = boost::none;
662 boost::optional<PreviousExplicitResult<ValueType>> previousExplicitResult = boost::none;
663 uint64_t peakPlayer1States = 0;
664 uint64_t peakTransitions = 0;
665 for (iteration = 0; iteration < maximalNumberOfAbstractions; ++iteration) {
666 auto iterationStart = std::chrono::high_resolution_clock::now();
667 STORM_LOG_TRACE("Starting iteration " << iteration << ".");
668
669 // (1) build the abstraction.
670 storm::utility::Stopwatch abstractionWatch(true);
671 storm::gbar::abstraction::MenuGame<Type, ValueType> game = abstractor->abstract();
672 abstractionWatch.stop();
673 totalAbstractionWatch.add(abstractionWatch);
674
675 uint64_t numberOfPlayer1States = game.getNumberOfStates();
676 peakPlayer1States = std::max(peakPlayer1States, numberOfPlayer1States);
677 uint64_t numberOfTransitions = game.getNumberOfTransitions();
678 peakTransitions = std::max(peakTransitions, numberOfTransitions);
679 STORM_LOG_INFO("Abstraction in iteration "
680 << iteration << " has " << numberOfPlayer1States << " player 1 states (" << game.getInitialStates().getNonZeroCount()
681 << " initial), " << game.getNumberOfPlayer2States() << " player 2 states, " << numberOfTransitions << " transitions, "
682 << game.getBottomStates().getNonZeroCount() << " bottom states, " << abstractor->getNumberOfPredicates() << " predicate(s), "
683 << game.getTransitionMatrix().getNodeCount() << " nodes (transition matrix) (computed in "
684 << abstractionWatch.getTimeInMilliseconds() << "ms).");
685
686 // (2) Prepare initial, constraint and target state BDDs for later use.
687 storm::dd::Bdd<Type> initialStates = game.getInitialStates();
688 // STORM_LOG_THROW(initialStates.getNonZeroCount() == 1 || checkTask.isBoundSet(), storm::exceptions::InvalidPropertyException,
689 // "Game-based abstraction refinement requires a bound on the formula for model with " << initialStates.getNonZeroCount() << "
690 // initial states.");
691 storm::dd::Bdd<Type> constraintStates = globalConstraintStates && game.getReachableStates();
692 storm::dd::Bdd<Type> targetStates = globalTargetStates && game.getReachableStates();
693 if (player1Direction == storm::OptimizationDirection::Minimize) {
694 targetStates |= game.getBottomStates();
695 }
696
697 // #ifdef LOCAL_DEBUG
698 // initialStates.template toAdd<ValueType>().exportToDot("init" + std::to_string(iteration) + ".dot");
699 // targetStates.template toAdd<ValueType>().exportToDot("target" + std::to_string(iteration) + ".dot");
700 // abstractor->exportToDot("game" + std::to_string(iteration) + ".dot", targetStates, game.getManager().getBddOne());
701 // game.getReachableStates().template toAdd<ValueType>().exportToDot("reach" + std::to_string(iteration) + ".dot");
702 // #endif
703
705 result = performSymbolicAbstractionSolutionStep(env, checkTask, game, player1Direction, initialStates, constraintStates, targetStates, refiner,
706 previousSymbolicQualitativeResult, previousSymbolicMinQuantitativeResult);
707 } else {
708 result = performExplicitAbstractionSolutionStep(env, checkTask, game, player1Direction, initialStates, constraintStates, targetStates, refiner,
709 previousExplicitResult);
710 }
711
712 if (result) {
713 totalWatch.stop();
714 printStatistics(*abstractor, game, iteration, peakPlayer1States, peakTransitions);
715 return;
716 }
717
718 auto iterationEnd = std::chrono::high_resolution_clock::now();
719 STORM_LOG_INFO("Iteration " << iteration << " took " << std::chrono::duration_cast<std::chrono::milliseconds>(iterationEnd - iterationStart).count()
720 << "ms.");
721 }
722 });
723 if (result) {
724 return result;
725 }
726 totalWatch.stop();
727
728 // If this point is reached, we have given up on abstraction.
729 STORM_LOG_WARN("Could not derive result, maximal number of abstractions exceeded.");
730 return nullptr;
731}
732
733template<storm::dd::DdType Type, typename ModelType>
734std::unique_ptr<storm::modelchecker::CheckResult> GameBasedMdpModelChecker<Type, ModelType>::performSymbolicAbstractionSolutionStep(
735 Environment const& env, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& checkTask,
737 storm::dd::Bdd<Type> const& constraintStates, storm::dd::Bdd<Type> const& targetStates,
739 boost::optional<SymbolicQualitativeGameResultMinMax<Type>>& previousQualitativeResult,
740 boost::optional<SymbolicQuantitativeGameResult<Type, ValueType>>& previousMinQuantitativeResult) {
741 STORM_LOG_TRACE("Using dd-based solving.");
742
743 // Prepare transition matrix BDD.
744 storm::dd::Bdd<Type> transitionMatrixBdd = game.getTransitionMatrix().toBdd();
745
746 // (1) compute all states with probability 0/1 wrt. to the two different player 2 goals (min/max).
747 storm::utility::Stopwatch qualitativeWatch(true);
748 SymbolicQualitativeGameResultMinMax<Type> qualitativeResult =
749 computeProb01States(previousQualitativeResult, game, player1Direction, transitionMatrixBdd, constraintStates, targetStates);
750 std::unique_ptr<storm::modelchecker::CheckResult> result =
751 checkForResultAfterQualitativeCheck<Type, ValueType>(checkTask, initialStates, qualitativeResult);
752 if (result) {
753 return result;
754 }
755 previousQualitativeResult = qualitativeResult;
756 qualitativeWatch.stop();
757 totalSolutionWatch.add(qualitativeWatch);
758 STORM_LOG_INFO("Qualitative computation completed in " << qualitativeWatch.getTimeInMilliseconds() << "ms.");
759
760 // (2) compute the states for which we have to determine quantitative information.
761 storm::dd::Bdd<Type> maybeMin =
762 !(qualitativeResult.prob0Min.getPlayer1States() || qualitativeResult.prob1Min.getPlayer1States()) && game.getReachableStates();
763 storm::dd::Bdd<Type> maybeMax =
764 !(qualitativeResult.prob0Max.getPlayer1States() || qualitativeResult.prob1Max.getPlayer1States()) && game.getReachableStates();
765
766 // (3) if the initial states are not maybe states, then we can refine at this point.
767 storm::dd::Bdd<Type> initialMaybeStates = (initialStates && maybeMin) || (initialStates && maybeMax);
768 bool qualitativeRefinement = false;
769 if (initialMaybeStates.isZero()) {
770 // In this case, we know the result for the initial states for both player 2 minimizing and maximizing.
771 STORM_LOG_TRACE("No initial state is a 'maybe' state.");
772
773 STORM_LOG_INFO("Obtained qualitative bounds [0, 1] on the actual value for the initial states (after "
774 << totalWatch.getTimeInMilliseconds() << "ms in iteration " << this->iteration << "). Refining abstraction based on qualitative check.");
775
776 // If we get here, the initial states were all identified as prob0/1 states, but the value (0 or 1)
777 // depends on whether player 2 is minimizing or maximizing. Therefore, we need to find a place to refine.
778 storm::utility::Stopwatch refinementWatch(true);
779 qualitativeRefinement = refiner.refine(game, transitionMatrixBdd, qualitativeResult);
780 refinementWatch.stop();
781 totalRefinementWatch.add(refinementWatch);
782 STORM_LOG_INFO("Qualitative refinement completed in " << refinementWatch.getTimeInMilliseconds() << "ms.");
783 }
784
785 // (4) if we arrived at this point and no refinement was made, we need to compute the quantitative solution.
786 if (!qualitativeRefinement) {
787 // At this point, we know that we cannot answer the query without further numeric computation.
788 STORM_LOG_TRACE("Starting numerical solution step.");
789
790 storm::dd::Add<Type, ValueType> initialStatesAdd = initialStates.template toAdd<ValueType>();
791
792 SymbolicQuantitativeGameResultMinMax<Type, ValueType> quantitativeResult;
793
794 // (7) Solve the min values and check whether we can give the answer already.
795 storm::utility::Stopwatch quantitativeWatch(true);
796 quantitativeResult.min = computeQuantitativeResult(env, player1Direction, storm::OptimizationDirection::Minimize, game, qualitativeResult,
797 initialStatesAdd, maybeMin, reuseQuantitativeResults ? previousMinQuantitativeResult : boost::none);
798 quantitativeWatch.stop();
799 previousMinQuantitativeResult = quantitativeResult.min;
800 result =
801 checkForResultAfterQuantitativeCheck<ValueType>(checkTask, storm::OptimizationDirection::Minimize, quantitativeResult.min.getInitialStatesRange());
802 if (result) {
803 totalSolutionWatch.add(quantitativeWatch);
804 return result;
805 }
806
807 // (8) Solve the max values and check whether we can give the answer already.
808 quantitativeWatch.start();
809 quantitativeResult.max = computeQuantitativeResult(env, player1Direction, storm::OptimizationDirection::Maximize, game, qualitativeResult,
810 initialStatesAdd, maybeMax, boost::make_optional(quantitativeResult.min));
811 quantitativeWatch.stop();
812 result =
813 checkForResultAfterQuantitativeCheck<ValueType>(checkTask, storm::OptimizationDirection::Maximize, quantitativeResult.max.getInitialStatesRange());
814 totalSolutionWatch.add(quantitativeWatch);
815 if (result) {
816 return result;
817 }
818
819 ValueType minVal = quantitativeResult.min.getInitialStatesRange().first;
820 ValueType maxVal = quantitativeResult.max.getInitialStatesRange().second;
821 ValueType difference = maxVal - minVal;
822 if (std::is_same<ValueType, double>::value) {
823 std::stringstream differenceStream;
824 differenceStream.setf(std::ios::fixed, std::ios::floatfield);
825 differenceStream.precision(15);
826 differenceStream << difference;
827 STORM_LOG_INFO("Obtained quantitative bounds [" << minVal << ", " << maxVal << "] (difference " << differenceStream.str()
828 << ") on the actual value for the initial states in " << quantitativeWatch.getTimeInMilliseconds()
829 << "ms (after " << totalWatch.getTimeInMilliseconds() << "ms in iteration " << this->iteration
830 << ").");
831 } else {
832 STORM_LOG_INFO("Obtained quantitative bounds [" << minVal << ", " << maxVal << "] (approx. [" << storm::utility::convertNumber<double>(minVal)
833 << ", " << storm::utility::convertNumber<double>(maxVal) << "], difference " << difference
834 << ") on the actual value for the initial states in " << quantitativeWatch.getTimeInMilliseconds()
835 << "ms (after " << totalWatch.getTimeInMilliseconds() << "ms in iteration " << this->iteration
836 << ").");
837 }
838
839 // (9) Check whether the lower and upper bounds are close enough to terminate with an answer.
840 result = checkForResultAfterQuantitativeCheck<ValueType>(quantitativeResult.min.getInitialStatesRange().first,
841 quantitativeResult.max.getInitialStatesRange().second, comparator);
842 if (result) {
843 return result;
844 }
845
846 // Make sure that all strategies are still valid strategies.
847 STORM_LOG_ASSERT(quantitativeResult.min.getPlayer1Strategy().isZero() ||
848 quantitativeResult.min.getPlayer1Strategy().template toAdd<ValueType>().sumAbstract(game.getPlayer1Variables()).getMax() <= 1,
849 "Player 1 strategy for min is illegal.");
850 STORM_LOG_ASSERT(quantitativeResult.max.getPlayer1Strategy().isZero() ||
851 quantitativeResult.max.getPlayer1Strategy().template toAdd<ValueType>().sumAbstract(game.getPlayer1Variables()).getMax() <= 1,
852 "Player 1 strategy for max is illegal.");
853 STORM_LOG_ASSERT(quantitativeResult.min.getPlayer2Strategy().isZero() ||
854 quantitativeResult.min.getPlayer2Strategy().template toAdd<ValueType>().sumAbstract(game.getPlayer2Variables()).getMax() <= 1,
855 "Player 2 strategy for min is illegal.");
856 STORM_LOG_ASSERT(quantitativeResult.max.getPlayer2Strategy().isZero() ||
857 quantitativeResult.max.getPlayer2Strategy().template toAdd<ValueType>().sumAbstract(game.getPlayer2Variables()).getMax() <= 1,
858 "Player 2 strategy for max is illegal.");
859
860 // (10) If we arrived at this point, it means that we have all qualitative and quantitative
861 // information about the game, but we could not yet answer the query. In this case, we need to refine.
862 storm::utility::Stopwatch refinementWatch(true);
863 refiner.refine(game, transitionMatrixBdd, quantitativeResult);
864 refinementWatch.stop();
865 totalRefinementWatch.add(refinementWatch);
866 STORM_LOG_INFO("Quantitative refinement completed in " << refinementWatch.getTimeInMilliseconds() << "ms.");
867 }
868
869 // Return null to indicate no result has been found yet.
870 return nullptr;
871}
872
873template<typename ValueType>
875 storage::ExplicitGameStrategyPair& maxStrategyPair, std::vector<uint64_t> const& player1Groups,
876 std::vector<uint64_t> const& player2Groups, storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
877 storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates,
878 ExplicitQualitativeGameResultMinMax const& qualitativeResult, bool redirectPlayer1, bool redirectPlayer2, bool sanityCheck) {
879 if (!redirectPlayer1 && !redirectPlayer2) {
880 return;
881 }
882
883 for (uint64_t state = 0; state < player1Groups.size() - 1; ++state) {
884 bool isProb0Min = qualitativeResult.getProb0Min().getStates().get(state);
885
886 bool hasMinPlayer1Choice = false;
887 uint64_t lowerPlayer1Choice = 0;
888 bool hasMaxPlayer1Choice = false;
889 uint64_t upperPlayer1Choice = 0;
890
891 if (minStrategyPair.getPlayer1Strategy().hasDefinedChoice(state)) {
892 hasMinPlayer1Choice = true;
893 lowerPlayer1Choice = minStrategyPair.getPlayer1Strategy().getChoice(state);
894
895 if (maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(lowerPlayer1Choice)) {
896 uint64_t lowerPlayer2Choice = minStrategyPair.getPlayer2Strategy().getChoice(lowerPlayer1Choice);
897 uint64_t upperPlayer2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(lowerPlayer1Choice);
898
899 if (lowerPlayer2Choice == upperPlayer2Choice) {
900 continue;
901 }
902
903 bool redirect = true;
904 if (isProb0Min) {
905 for (auto const& entry : transitionMatrix.getRow(upperPlayer2Choice)) {
906 if (!qualitativeResult.getProb0Min().getStates().get(entry.getColumn())) {
907 redirect = false;
908 break;
909 }
910 }
911 }
912
913 if (redirectPlayer2 && redirect) {
914 minStrategyPair.getPlayer2Strategy().setChoice(lowerPlayer1Choice, upperPlayer2Choice);
915 }
916 }
917 }
918
919 bool lowerChoiceUnderUpperIsProb0 = false;
920 if (maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(state)) {
921 upperPlayer1Choice = maxStrategyPair.getPlayer1Strategy().getChoice(state);
922
923 if (lowerPlayer1Choice != upperPlayer1Choice && minStrategyPair.getPlayer2Strategy().hasDefinedChoice(upperPlayer1Choice)) {
924 hasMaxPlayer1Choice = true;
925
926 uint64_t lowerPlayer2Choice = minStrategyPair.getPlayer2Strategy().getChoice(upperPlayer1Choice);
927 uint64_t upperPlayer2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(upperPlayer1Choice);
928
929 if (lowerPlayer2Choice == upperPlayer2Choice) {
930 continue;
931 }
932
933 lowerChoiceUnderUpperIsProb0 = true;
934 for (auto const& entry : transitionMatrix.getRow(lowerPlayer2Choice)) {
935 if (!qualitativeResult.getProb0Min().getStates().get(entry.getColumn())) {
936 lowerChoiceUnderUpperIsProb0 = false;
937 break;
938 }
939 }
940
941 bool redirect = true;
942 if (lowerChoiceUnderUpperIsProb0) {
943 for (auto const& entry : transitionMatrix.getRow(upperPlayer2Choice)) {
944 if (!qualitativeResult.getProb0Min().getStates().get(entry.getColumn())) {
945 redirect = false;
946 break;
947 }
948 }
949 }
950
951 if (redirectPlayer2 && redirect) {
952 minStrategyPair.getPlayer2Strategy().setChoice(lowerPlayer1Choice, upperPlayer2Choice);
953 }
954 }
955 }
956
957 if (redirectPlayer1 && player1Direction == storm::OptimizationDirection::Minimize) {
958 if (hasMinPlayer1Choice && hasMaxPlayer1Choice && lowerPlayer1Choice != upperPlayer1Choice) {
959 if (!isProb0Min || lowerChoiceUnderUpperIsProb0) {
960 minStrategyPair.getPlayer1Strategy().setChoice(state, upperPlayer1Choice);
961 }
962 }
963 }
964 }
965}
966
967template<typename ValueType>
969 public:
970 ExplicitGameExporter() : showNonStrategyAlternatives(false) {
971 // Intentionally left empty.
972 }
973
974 void exportToJson(std::string const& filename, std::vector<uint64_t> const& player1Groups, std::vector<uint64_t> const& player2Groups,
975 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& initialStates,
976 storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates,
977 ExplicitQuantitativeResultMinMax<ValueType> const& quantitativeResult, storage::ExplicitGameStrategyPair const* minStrategyPair,
978 storage::ExplicitGameStrategyPair const* maxStrategyPair) {
979 // Export game as json.
980 std::ofstream outfile;
981 storm::io::openFile(filename, outfile);
982 exportGame(outfile, player1Groups, player2Groups, transitionMatrix, initialStates, constraintStates, targetStates, quantitativeResult, minStrategyPair,
983 maxStrategyPair);
984 storm::io::closeFile(outfile);
985 }
986
988 showNonStrategyAlternatives = value;
989 }
990
991 private:
992 struct NodeData {
993 NodeData(uint64_t id, uint64_t player, bool initial, bool target) : id(id), player(player), initial(initial), target(target) {
994 // Intentionally left empty.
995 }
996
997 uint64_t id;
998 uint64_t player; // 0 = probabilistic player, 1 = player 1, 2 = player 2
999 bool initial;
1000 bool target;
1001 };
1002
1003 struct EdgeData {
1004 EdgeData(uint64_t id, uint64_t source, uint64_t target, ValueType probability, uint64_t label, bool min, bool max)
1005 : id(id), source(source), target(target), probability(probability), label(label), min(min), max(max) {
1006 // Intentionally left empty.
1007 }
1008
1009 uint64_t id;
1010 uint64_t source;
1011 uint64_t target;
1012 ValueType probability;
1013 uint64_t label;
1014 bool min;
1015 bool max;
1016 };
1017
1018 void exportEdge(std::ofstream& out, EdgeData const& data, bool& first) {
1019 if (!first) {
1020 out << ",\n";
1021 } else {
1022 first = false;
1023 }
1024 out << "\t\t{\n";
1025 out << "\t\t\t\"data\": {\n";
1026 out << "\t\t\t\t\"id\": \"" << data.id << "\",\n";
1027 if (data.probability != storm::utility::zero<ValueType>()) {
1028 out << "\t\t\t\t\"name\": \"" << data.probability << "\",\n";
1029 } else {
1030 out << "\t\t\t\t\"name\": \"" << data.label << "\",\n";
1031 }
1032 out << "\t\t\t\t\"source\": \"" << data.source << "\",\n";
1033 out << "\t\t\t\t\"target\": \"" << data.target << "\"\n";
1034 out << "\t\t\t},\n";
1035 out << "\t\t\t\"classes\": \"";
1036 if (data.min && data.max) {
1037 out << "minMaxEdge";
1038 } else if (data.min) {
1039 out << "minEdge";
1040 } else if (data.max) {
1041 out << "maxEdge";
1042 } else {
1043 out << "edge";
1044 }
1045 out << "\"\n";
1046 out << "\t\t}";
1047 }
1048
1049 void exportNode(std::ofstream& out, NodeData const& data, ExplicitQuantitativeResultMinMax<ValueType> const* quantitativeResult, bool& first) {
1050 if (!first) {
1051 out << ",\n";
1052 } else {
1053 first = false;
1054 }
1055 out << "\t\t{\n";
1056 out << "\t\t\t\"data\": {\n";
1057 out << "\t\t\t\t\"id\": \"" << data.id << "\",\n";
1058 out << "\t\t\t\t\"name\": \"" << data.id;
1059 if (quantitativeResult && data.player == 1) {
1060 out << " [" << quantitativeResult->getMin().getValues()[data.id] << ", " << quantitativeResult->getMax().getValues()[data.id] << "]";
1061 }
1062 out << "\"\n";
1063 out << "\t\t\t},\n";
1064 out << "\t\t\t\"group\": \"nodes\",\n";
1065 out << "\t\t\t\"classes\": \"";
1066 if (data.player == 1) {
1067 if (data.initial) {
1068 out << "initialNode";
1069 } else if (data.target) {
1070 out << "targetNode";
1071 } else {
1072 out << "node";
1073 }
1074 } else if (data.player == 2) {
1075 out << "pl2node";
1076 } else if (data.player == 0) {
1077 out << "plpnode";
1078 }
1079 out << "\"\n";
1080 out << "\t\t}";
1081 }
1082
1083 void exportGame(std::ofstream& out, std::vector<uint64_t> const& player1Groups, std::vector<uint64_t> const& player2Groups,
1084 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& initialStates,
1085 storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates,
1086 ExplicitQuantitativeResultMinMax<ValueType> const& quantitativeResult, storage::ExplicitGameStrategyPair const* minStrategyPair,
1087 storage::ExplicitGameStrategyPair const* maxStrategyPair) {
1088 // To export the game as JSON, we build some data structures through a traversal and then emit them.
1089 std::vector<NodeData> nodes;
1090 std::vector<EdgeData> edges;
1091
1092 std::vector<uint64_t> stack;
1093 for (auto state : initialStates) {
1094 stack.push_back(state);
1095 }
1096 storm::storage::BitVector reachablePlayer1(player1Groups.size() - 1);
1097
1098 uint64_t edgeId = 0;
1099 while (!stack.empty()) {
1100 uint64_t currentState = stack.back();
1101 stack.pop_back();
1102
1103 nodes.emplace_back(currentState, 1, initialStates.get(currentState), targetStates.get(currentState));
1104
1105 for (uint64_t player2State = player1Groups[currentState]; player2State < player1Groups[currentState + 1]; ++player2State) {
1106 bool emit = (minStrategyPair || maxStrategyPair) ? this->showNonStrategyAlternatives : true;
1107 bool min = false;
1108 bool max = false;
1109
1110 if (minStrategyPair && minStrategyPair->getPlayer1Strategy().hasDefinedChoice(currentState) &&
1111 minStrategyPair->getPlayer1Strategy().getChoice(currentState) == player2State) {
1112 emit = true;
1113 min = true;
1114 }
1115 if (maxStrategyPair && maxStrategyPair->getPlayer1Strategy().hasDefinedChoice(currentState) &&
1116 maxStrategyPair->getPlayer1Strategy().getChoice(currentState) == player2State) {
1117 emit = true;
1118 max = true;
1119 }
1120
1121 if (emit) {
1122 nodes.emplace_back(player2State, 2, false, false);
1123 edges.emplace_back(edgeId++, currentState, player2State, storm::utility::zero<ValueType>(), player2State - player1Groups[currentState], min,
1124 max);
1125
1126 for (uint64_t playerPState = player2Groups[player2State]; playerPState < player2Groups[player2State + 1]; ++playerPState) {
1127 emit = (minStrategyPair || maxStrategyPair) ? this->showNonStrategyAlternatives : true;
1128 min = false;
1129 max = false;
1130
1131 if (minStrategyPair && minStrategyPair->getPlayer2Strategy().hasDefinedChoice(player2State) &&
1132 minStrategyPair->getPlayer2Strategy().getChoice(player2State) == playerPState) {
1133 emit = true;
1134 min = true;
1135 }
1136 if (maxStrategyPair && maxStrategyPair->getPlayer2Strategy().hasDefinedChoice(player2State) &&
1137 maxStrategyPair->getPlayer2Strategy().getChoice(player2State) == playerPState) {
1138 emit = true;
1139 max = true;
1140 }
1141
1142 if (emit) {
1143 nodes.emplace_back(playerPState, 0, false, false);
1144 edges.emplace_back(edgeId++, player2State, playerPState, storm::utility::zero<ValueType>(),
1145 playerPState - player2Groups[player2State], min, max);
1146
1147 for (auto const& entry : transitionMatrix.getRow(playerPState)) {
1148 auto player1Successor = entry.getColumn();
1149 if (!reachablePlayer1.get(player1Successor)) {
1150 reachablePlayer1.set(player1Successor);
1151 stack.push_back(player1Successor);
1152 }
1153
1154 edges.emplace_back(edgeId++, playerPState, player1Successor, entry.getValue(), 0, false, false);
1155 }
1156 }
1157 }
1158 }
1159 }
1160 }
1161
1162 // Finally, export the data structures we built.
1163
1164 // Export nodes.
1165 out << "{\n\t\"nodes\": [\n";
1166 bool first = true;
1167 for (auto const& node : nodes) {
1168 exportNode(out, node, &quantitativeResult, first);
1169 }
1170 out << "\n\t],\n";
1171
1172 // Export edges.
1173 first = true;
1174 out << "\t\"edges\": [\n";
1175 for (auto const& edge : edges) {
1176 exportEdge(out, edge, first);
1177 }
1178 out << "\n\t]\n}\n";
1179 }
1180
1181 bool showNonStrategyAlternatives;
1182};
1183
1184template<typename ValueType>
1185void postProcessStrategies(uint64_t iteration, storm::OptimizationDirection const& player1Direction, storage::ExplicitGameStrategyPair& minStrategyPair,
1186 storage::ExplicitGameStrategyPair& maxStrategyPair, std::vector<uint64_t> const& player1Groups,
1187 std::vector<uint64_t> const& player2Groups, storm::storage::SparseMatrix<ValueType> const& transitionMatrix,
1188 storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates,
1189 storm::storage::BitVector const& targetStates, ExplicitQuantitativeResultMinMax<ValueType> const& quantitativeResult,
1190 bool redirectPlayer1, bool redirectPlayer2, bool sanityCheck) {
1191 if (redirectPlayer1 || redirectPlayer2) {
1192 for (uint64_t state = 0; state < player1Groups.size() - 1; ++state) {
1193 STORM_LOG_ASSERT(targetStates.get(state) || minStrategyPair.getPlayer1Strategy().hasDefinedChoice(state),
1194 "Expected lower player 1 choice in state " << state << ".");
1195 STORM_LOG_ASSERT(targetStates.get(state) || maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(state),
1196 "Expected upper player 1 choice in state " << state << ".");
1197
1198 bool hasMinPlayer1Choice = false;
1199 uint64_t lowerPlayer1Choice = 0;
1200 ValueType lowerValueUnderMinChoicePlayer1 = storm::utility::zero<ValueType>();
1201 bool hasMaxPlayer1Choice = false;
1202 uint64_t upperPlayer1Choice = 0;
1203 ValueType lowerValueUnderMaxChoicePlayer1 = storm::utility::zero<ValueType>();
1204
1205 if (minStrategyPair.getPlayer1Strategy().hasDefinedChoice(state)) {
1206 hasMinPlayer1Choice = true;
1207 lowerPlayer1Choice = minStrategyPair.getPlayer1Strategy().getChoice(state);
1208
1209 STORM_LOG_ASSERT(minStrategyPair.getPlayer2Strategy().hasDefinedChoice(lowerPlayer1Choice),
1210 "Expected lower player 2 choice for state " << state << " (lower player 1 choice " << lowerPlayer1Choice << ").");
1211 uint64_t lowerPlayer2Choice = minStrategyPair.getPlayer2Strategy().getChoice(lowerPlayer1Choice);
1212
1213 ValueType lowerValueUnderLowerChoicePlayer2 =
1214 transitionMatrix.multiplyRowWithVector(lowerPlayer2Choice, quantitativeResult.getMin().getValues());
1215 lowerValueUnderMinChoicePlayer1 = lowerValueUnderLowerChoicePlayer2;
1216
1217 if (maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(lowerPlayer1Choice)) {
1218 uint64_t upperPlayer2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(lowerPlayer1Choice);
1219
1220 if (lowerPlayer2Choice != upperPlayer2Choice) {
1221 ValueType lowerValueUnderUpperChoicePlayer2 =
1222 transitionMatrix.multiplyRowWithVector(upperPlayer2Choice, quantitativeResult.getMin().getValues());
1223
1224 if (redirectPlayer2 && lowerValueUnderUpperChoicePlayer2 <= lowerValueUnderLowerChoicePlayer2) {
1225 lowerValueUnderMinChoicePlayer1 = lowerValueUnderUpperChoicePlayer2;
1226 minStrategyPair.getPlayer2Strategy().setChoice(lowerPlayer1Choice, upperPlayer2Choice);
1227 }
1228 }
1229 }
1230 }
1231
1232 if (maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(state)) {
1233 upperPlayer1Choice = maxStrategyPair.getPlayer1Strategy().getChoice(state);
1234
1235 if (upperPlayer1Choice != lowerPlayer1Choice && minStrategyPair.getPlayer2Strategy().hasDefinedChoice(upperPlayer1Choice)) {
1236 hasMaxPlayer1Choice = true;
1237
1238 uint64_t lowerPlayer2Choice = minStrategyPair.getPlayer2Strategy().getChoice(upperPlayer1Choice);
1239
1240 ValueType lowerValueUnderLowerChoicePlayer2 =
1241 transitionMatrix.multiplyRowWithVector(lowerPlayer2Choice, quantitativeResult.getMin().getValues());
1242 lowerValueUnderMaxChoicePlayer1 = lowerValueUnderLowerChoicePlayer2;
1243
1244 STORM_LOG_ASSERT(maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(upperPlayer1Choice),
1245 "Expected upper player 2 choice for state " << state << " (upper player 1 choice " << upperPlayer1Choice << ").");
1246 uint64_t upperPlayer2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(upperPlayer1Choice);
1247
1248 if (lowerPlayer2Choice != upperPlayer2Choice) {
1249 ValueType lowerValueUnderUpperChoicePlayer2 =
1250 transitionMatrix.multiplyRowWithVector(upperPlayer2Choice, quantitativeResult.getMin().getValues());
1251
1252 if (redirectPlayer2 && lowerValueUnderUpperChoicePlayer2 <= lowerValueUnderLowerChoicePlayer2) {
1253 minStrategyPair.getPlayer2Strategy().setChoice(upperPlayer1Choice, upperPlayer2Choice);
1254 }
1255 }
1256 }
1257 }
1258
1259 if (redirectPlayer1 && player1Direction == storm::OptimizationDirection::Minimize) {
1260 if (hasMinPlayer1Choice && hasMaxPlayer1Choice && lowerPlayer1Choice != upperPlayer1Choice) {
1261 if (lowerValueUnderMaxChoicePlayer1 <= lowerValueUnderMinChoicePlayer1) {
1262 minStrategyPair.getPlayer1Strategy().setChoice(state, upperPlayer1Choice);
1263 }
1264 }
1265 }
1266 }
1267 }
1268
1269 if (sanityCheck) {
1270 storm::utility::ConstantsComparator<ValueType> sanityComparator(storm::utility::convertNumber<ValueType>(1e-6), true);
1271
1274 storm::storage::SparseMatrixBuilder<ValueType> dtmcMatrixBuilder(player1Groups.size() - 1, player1Groups.size() - 1);
1275 for (uint64_t state = 0; state < player1Groups.size() - 1; ++state) {
1276 if (targetStates.get(state)) {
1277 dtmcMatrixBuilder.addNextValue(state, state, storm::utility::one<ValueType>());
1278 } else {
1279 STORM_LOG_ASSERT(minStrategyPair.getPlayer1Strategy().hasDefinedChoice(state), "Expected min player 1 choice in state " << state << ".");
1280 STORM_LOG_ASSERT(minStrategyPair.getPlayer2Strategy().hasDefinedChoice(minStrategyPair.getPlayer1Strategy().getChoice(state)),
1281 "Expected max player 2 choice in state " << state << " with player 2 choice "
1282 << maxStrategyPair.getPlayer1Strategy().getChoice(state) << ".");
1283 uint64_t player2Choice = minStrategyPair.getPlayer2Strategy().getChoice(minStrategyPair.getPlayer1Strategy().getChoice(state));
1284 for (auto const& entry : transitionMatrix.getRow(player2Choice)) {
1285 dtmcMatrixBuilder.addNextValue(state, entry.getColumn(), entry.getValue());
1286 }
1287 }
1288 }
1289 auto dtmcMatrix = dtmcMatrixBuilder.build();
1291 Environment(), storm::solver::SolveGoal<ValueType>(), dtmcMatrix, dtmcMatrix.transpose(), constraintStates, targetStates, false);
1292
1293 ValueType maxDiff = storm::utility::zero<ValueType>();
1294 uint64_t maxState = 0;
1295 for (uint64_t state = 0; state < player1Groups.size() - 1; ++state) {
1296 ValueType diff = storm::utility::abs(ValueType(sanityValues[state] - quantitativeResult.getMin().getValues()[state]));
1297 if (diff > maxDiff) {
1298 maxState = state;
1299 maxDiff = diff;
1300 }
1301 }
1302 STORM_LOG_TRACE("Got maximal deviation of " << maxDiff << ".");
1303 STORM_LOG_WARN_COND(sanityComparator.isZero(maxDiff),
1304 "Deviation " << maxDiff << " between computed value (" << quantitativeResult.getMin().getValues()[maxState]
1305 << ") and sanity check value (" << sanityValues[maxState] << ") in state " << maxState
1306 << " appears to be too high. (Obtained bounds were [" << quantitativeResult.getMin().getValues()[maxState] << ", "
1307 << quantitativeResult.getMax().getValues()[maxState] << "].)");
1308
1311 dtmcMatrixBuilder = storm::storage::SparseMatrixBuilder<ValueType>(player1Groups.size() - 1, player1Groups.size() - 1);
1312 for (uint64_t state = 0; state < player1Groups.size() - 1; ++state) {
1313 if (targetStates.get(state)) {
1314 dtmcMatrixBuilder.addNextValue(state, state, storm::utility::one<ValueType>());
1315 } else {
1316 STORM_LOG_ASSERT(maxStrategyPair.getPlayer1Strategy().hasDefinedChoice(state), "Expected max player 1 choice in state " << state << ".");
1317 STORM_LOG_ASSERT(maxStrategyPair.getPlayer2Strategy().hasDefinedChoice(maxStrategyPair.getPlayer1Strategy().getChoice(state)),
1318 "Expected max player 2 choice in state " << state << " with player 2 choice "
1319 << maxStrategyPair.getPlayer1Strategy().getChoice(state) << ".");
1320 uint64_t player2Choice = maxStrategyPair.getPlayer2Strategy().getChoice(maxStrategyPair.getPlayer1Strategy().getChoice(state));
1321
1322 for (auto const& entry : transitionMatrix.getRow(player2Choice)) {
1323 dtmcMatrixBuilder.addNextValue(state, entry.getColumn(), entry.getValue());
1324 }
1325 }
1326 }
1327 dtmcMatrix = dtmcMatrixBuilder.build();
1329 Environment(), storm::solver::SolveGoal<ValueType>(), dtmcMatrix, dtmcMatrix.transpose(), constraintStates, targetStates, false);
1330
1331 maxDiff = storm::utility::zero<ValueType>();
1332 maxState = 0;
1333 for (uint64_t state = 0; state < player1Groups.size() - 1; ++state) {
1334 ValueType diff = storm::utility::abs(ValueType(sanityValues[state] - quantitativeResult.getMax().getValues()[state]));
1335 if (diff > maxDiff) {
1336 maxState = state;
1337 maxDiff = diff;
1338 }
1339 }
1340 STORM_LOG_TRACE("Got maximal deviation of " << maxDiff << ".");
1341 STORM_LOG_WARN_COND(sanityComparator.isZero(maxDiff),
1342 "Deviation " << maxDiff << " between computed value (" << quantitativeResult.getMax().getValues()[maxState]
1343 << ") and sanity check value (" << sanityValues[maxState] << ") in state " << maxState
1344 << " appears to be too high. (Obtained bounds were [" << quantitativeResult.getMin().getValues()[maxState] << ", "
1345 << quantitativeResult.getMax().getValues()[maxState] << "].)");
1346 }
1347}
1348
1349template<storm::dd::DdType Type, typename ModelType>
1350std::unique_ptr<storm::modelchecker::CheckResult> GameBasedMdpModelChecker<Type, ModelType>::performExplicitAbstractionSolutionStep(
1353 storm::dd::Bdd<Type> const& initialStatesBdd, storm::dd::Bdd<Type> const& constraintStatesBdd, storm::dd::Bdd<Type> const& targetStatesBdd,
1354 storm::gbar::abstraction::MenuGameRefiner<Type, ValueType> const& refiner, boost::optional<PreviousExplicitResult<ValueType>>& previousResult) {
1355 STORM_LOG_TRACE("Using sparse solving.");
1356
1357 // (0) Start by transforming the necessary symbolic elements to explicit ones.
1358 storm::utility::Stopwatch translationWatch(true);
1360
1361 std::vector<std::set<storm::expressions::Variable>> labelingVariableSets = {game.getPlayer1Variables(), game.getPlayer2Variables()};
1363 game.getRowVariables(), game.getColumnVariables(), game.getNondeterminismVariables(), odd, odd, labelingVariableSets);
1364 auto& transitionMatrix = matrixAndLabeling.matrix;
1365 auto& player1Labeling = matrixAndLabeling.labelings.front();
1366 auto& player2Labeling = matrixAndLabeling.labelings.back();
1367
1368 // Create the player 2 row grouping from the labeling.
1369 std::vector<uint64_t> tmpPlayer2RowGrouping;
1370 for (uint64_t player1State = 0; player1State < transitionMatrix.getRowGroupCount(); ++player1State) {
1371 uint64_t lastLabel = std::numeric_limits<uint64_t>::max();
1372 for (uint64_t row = transitionMatrix.getRowGroupIndices()[player1State]; row < transitionMatrix.getRowGroupIndices()[player1State + 1]; ++row) {
1373 if (player1Labeling[row] != lastLabel) {
1374 tmpPlayer2RowGrouping.emplace_back(row);
1375 lastLabel = player1Labeling[row];
1376 }
1377 }
1378 }
1379 tmpPlayer2RowGrouping.emplace_back(player1Labeling.size());
1380
1381 std::vector<uint64_t> player1RowGrouping = transitionMatrix.swapRowGroupIndices(std::move(tmpPlayer2RowGrouping));
1382 auto const& player2RowGrouping = transitionMatrix.getRowGroupIndices();
1383
1384 // Create the player 1 groups and backward transitions (for both players).
1385 std::vector<uint64_t> player1Groups(player1RowGrouping.size());
1386 storm::storage::SparseMatrix<ValueType> player1BackwardTransitions = transitionMatrix.transpose(true);
1387 std::vector<uint64_t> player2BackwardTransitions(transitionMatrix.getRowGroupCount());
1388
1389 uint64_t player2State = 0;
1390 for (uint64_t player1State = 0; player1State < player1RowGrouping.size() - 1; ++player1State) {
1391 while (player1RowGrouping[player1State + 1] > player2RowGrouping[player2State]) {
1392 player2BackwardTransitions[player2State] = player1State;
1393 ++player2State;
1394 }
1395
1396 player1Groups[player1State + 1] = player2State;
1397 }
1398
1399 // Lift the player 1 labeling from rows to row groups (player 2 states).
1400 for (uint64_t player1State = 0; player1State < player1Groups.size() - 1; ++player1State) {
1401 for (uint64_t player2State = player1Groups[player1State]; player2State < player1Groups[player1State + 1]; ++player2State) {
1402 player1Labeling[player2State] = player1Labeling[player2RowGrouping[player2State]];
1403 }
1404 }
1405 player1Labeling.resize(player2RowGrouping.size() - 1);
1406
1407 // Create explicit representations of important state sets.
1408 storm::storage::BitVector initialStates = initialStatesBdd.toVector(odd);
1409 storm::storage::BitVector constraintStates = constraintStatesBdd.toVector(odd);
1410 storm::storage::BitVector targetStates = targetStatesBdd.toVector(odd);
1411 translationWatch.stop();
1412 totalTranslationWatch.add(translationWatch);
1413 STORM_LOG_INFO("Translation to explicit representation completed in " << translationWatch.getTimeInMilliseconds() << "ms.");
1414
1415 // Prepare the two strategies.
1416 storage::ExplicitGameStrategyPair minStrategyPair(initialStates.size(), transitionMatrix.getRowGroupCount());
1417 storage::ExplicitGameStrategyPair maxStrategyPair(initialStates.size(), transitionMatrix.getRowGroupCount());
1418
1419 // (1) compute all states with probability 0/1 wrt. to the two different player 2 goals (min/max).
1420 storm::utility::Stopwatch qualitativeWatch(true);
1421 ExplicitQualitativeGameResultMinMax qualitativeResult =
1422 computeProb01States(previousResult, odd, player1Direction, transitionMatrix, player1Groups, player1BackwardTransitions, player2BackwardTransitions,
1423 constraintStates, targetStates, minStrategyPair, maxStrategyPair);
1424 qualitativeWatch.stop();
1425 totalSolutionWatch.add(qualitativeWatch);
1426 STORM_LOG_INFO("Qualitative computation completed in " << qualitativeWatch.getTimeInMilliseconds() << "ms.");
1427
1428 std::unique_ptr<storm::modelchecker::CheckResult> result = checkForResultAfterQualitativeCheck<ValueType>(checkTask, initialStates, qualitativeResult);
1429 if (result) {
1430 return result;
1431 }
1432
1433 // (2) compute the states for which we have to determine quantitative information.
1434 storm::storage::BitVector maybeMin = ~(qualitativeResult.getProb0Min().getStates() | qualitativeResult.getProb1Min().getStates());
1435 storm::storage::BitVector maybeMax = ~(qualitativeResult.getProb0Max().getStates() | qualitativeResult.getProb1Max().getStates());
1436
1437 // (3) if the initial states are not maybe states, then we can refine at this point.
1438 storm::storage::BitVector initialMaybeStates = initialStates & (maybeMin | maybeMax);
1439 bool qualitativeRefinement = false;
1440 if (initialMaybeStates.empty()) {
1441 // In this case, we know the result for the initial states for both player 2 minimizing and maximizing.
1442 STORM_LOG_TRACE("No initial state is a 'maybe' state.");
1443
1444 STORM_LOG_INFO("Obtained qualitative bounds [0, 1] on the actual value for the initial states (after "
1445 << totalWatch.getTimeInMilliseconds() << "ms in iteration " << this->iteration << "). Refining abstraction based on qualitative check.");
1446
1447 // Post-process strategies for better refinements.
1448 storm::utility::Stopwatch strategyProcessingWatch(true);
1449 postProcessStrategies(player1Direction, minStrategyPair, maxStrategyPair, player1Groups, player2RowGrouping, transitionMatrix, constraintStates,
1450 targetStates, qualitativeResult, this->fixPlayer1Strategy, this->fixPlayer2Strategy, this->debug);
1451 strategyProcessingWatch.stop();
1452 totalStrategyProcessingWatch.add(strategyProcessingWatch);
1453 STORM_LOG_DEBUG("Postprocessed strategies in " << strategyProcessingWatch.getTimeInMilliseconds() << "ms.");
1454
1455 // If we get here, the initial states were all identified as prob0/1 states, but the value (0 or 1)
1456 // depends on whether player 2 is minimizing or maximizing. Therefore, we need to find a place to refine.
1457 storm::utility::Stopwatch refinementWatch(true);
1458 qualitativeRefinement = refiner.refine(game, odd, transitionMatrix, player1Groups, player1Labeling, player2Labeling, initialStates, constraintStates,
1459 targetStates, qualitativeResult, minStrategyPair, maxStrategyPair);
1460 refinementWatch.stop();
1461 totalRefinementWatch.add(refinementWatch);
1462 STORM_LOG_INFO("Qualitative refinement completed in " << refinementWatch.getTimeInMilliseconds() << "ms.");
1463 }
1464
1465 ExplicitQuantitativeResultMinMax<ValueType> quantitativeResult;
1466
1467 // (4) if we arrived at this point and no refinement was made, we need to compute the quantitative solution.
1468 if (!qualitativeRefinement) {
1469 // At this point, we know that we cannot answer the query without further numeric computation.
1470 STORM_LOG_TRACE("Starting numerical solution step.");
1471
1472 // (7) Solve the min values and check whether we can give the answer already.
1473 storm::utility::Stopwatch quantitativeWatch(true);
1474 quantitativeResult.setMin(computeQuantitativeResult<ValueType>(env, player1Direction, storm::OptimizationDirection::Minimize, transitionMatrix,
1475 player1Groups, qualitativeResult, maybeMin, minStrategyPair, odd, nullptr, nullptr,
1476 this->reuseQuantitativeResults ? previousResult : boost::none));
1477
1478 // Dispose of previous result as we now reused it.
1479 if (previousResult) {
1480 previousResult.get().clear();
1481 }
1482 quantitativeWatch.stop();
1483 result = checkForResultAfterQuantitativeCheck<ValueType>(checkTask, storm::OptimizationDirection::Minimize,
1484 quantitativeResult.getMin().getRange(initialStates));
1485 if (result) {
1486 totalSolutionWatch.add(quantitativeWatch);
1487 return result;
1488 }
1489
1490 // (8) Solve the max values and check whether we can give the answer already.
1491 quantitativeWatch.start();
1492 quantitativeResult.setMax(computeQuantitativeResult(env, player1Direction, storm::OptimizationDirection::Maximize, transitionMatrix, player1Groups,
1493 qualitativeResult, maybeMax, maxStrategyPair, odd, &quantitativeResult.getMin(), &minStrategyPair));
1494 quantitativeWatch.stop();
1495 result = checkForResultAfterQuantitativeCheck<ValueType>(checkTask, storm::OptimizationDirection::Maximize,
1496 quantitativeResult.getMax().getRange(initialStates));
1497 totalSolutionWatch.add(quantitativeWatch);
1498 if (result) {
1499 return result;
1500 }
1501
1502 ValueType minVal = quantitativeResult.getMin().getRange(initialStates).first;
1503 ValueType maxVal = quantitativeResult.getMax().getRange(initialStates).second;
1504 ValueType difference = maxVal - minVal;
1505 if (std::is_same<ValueType, double>::value) {
1506 std::stringstream differenceStream;
1507 differenceStream.setf(std::ios::fixed, std::ios::floatfield);
1508 differenceStream.precision(15);
1509 differenceStream << difference;
1510 STORM_LOG_INFO("Obtained quantitative bounds [" << minVal << ", " << maxVal << "] (difference " << differenceStream.str()
1511 << ") on the actual value for the initial states in " << quantitativeWatch.getTimeInMilliseconds()
1512 << "ms (after " << totalWatch.getTimeInMilliseconds() << "ms in iteration " << this->iteration
1513 << ").");
1514 } else {
1515 STORM_LOG_INFO("Obtained quantitative bounds [" << minVal << ", " << maxVal << "] (approx. [" << storm::utility::convertNumber<double>(minVal)
1516 << ", " << storm::utility::convertNumber<double>(maxVal) << "], difference " << difference
1517 << ") on the actual value for the initial states in " << quantitativeWatch.getTimeInMilliseconds()
1518 << "ms (after " << totalWatch.getTimeInMilliseconds() << "ms in iteration " << this->iteration
1519 << ").");
1520 }
1521
1522 // (9) Check whether the lower and upper bounds are close enough to terminate with an answer.
1523 result = checkForResultAfterQuantitativeCheck<ValueType>(quantitativeResult.getMin().getRange(initialStates).first,
1524 quantitativeResult.getMax().getRange(initialStates).second, comparator);
1525 if (result) {
1526 return result;
1527 }
1528
1529 // Post-process strategies for better refinements.
1530 storm::utility::Stopwatch strategyProcessingWatch(true);
1531 postProcessStrategies(this->iteration, player1Direction, minStrategyPair, maxStrategyPair, player1Groups, player2RowGrouping, transitionMatrix,
1532 initialStates, constraintStates, targetStates, quantitativeResult, this->fixPlayer1Strategy, this->fixPlayer2Strategy,
1533 this->debug);
1534 strategyProcessingWatch.stop();
1535 totalStrategyProcessingWatch.add(strategyProcessingWatch);
1536 STORM_LOG_DEBUG("Postprocessed strategies in " << strategyProcessingWatch.getTimeInMilliseconds() << "ms.");
1537
1538 // Make sure that all strategies are still valid strategies.
1539 STORM_LOG_ASSERT(minStrategyPair.getNumberOfUndefinedPlayer1States() <= targetStates.getNumberOfSetBits(),
1540 "Expected at most " << targetStates.getNumberOfSetBits() << " (number of target states) player 1 states with undefined choice but got "
1541 << minStrategyPair.getNumberOfUndefinedPlayer1States() << ".");
1542 STORM_LOG_ASSERT(maxStrategyPair.getNumberOfUndefinedPlayer1States() <= targetStates.getNumberOfSetBits(),
1543 "Expected at most " << targetStates.getNumberOfSetBits() << " (number of target states) player 1 states with undefined choice but got "
1544 << maxStrategyPair.getNumberOfUndefinedPlayer1States() << ".");
1545
1546 // (10) If we arrived at this point, it means that we have all qualitative and quantitative
1547 // information about the game, but we could not yet answer the query. In this case, we need to refine.
1548 storm::utility::Stopwatch refinementWatch(true);
1549 refiner.refine(game, odd, transitionMatrix, player1Groups, player1Labeling, player2Labeling, initialStates, constraintStates, targetStates,
1550 quantitativeResult, minStrategyPair, maxStrategyPair);
1551 refinementWatch.stop();
1552 totalRefinementWatch.add(refinementWatch);
1553 STORM_LOG_INFO("Quantitative refinement completed in " << refinementWatch.getTimeInMilliseconds() << "ms.");
1554
1555 if (this->reuseQuantitativeResults) {
1556 PreviousExplicitResult<ValueType> nextPreviousResult;
1557 nextPreviousResult.values = std::move(quantitativeResult.getMin());
1558 nextPreviousResult.odd = odd;
1559 previousResult = std::move(nextPreviousResult);
1560 STORM_LOG_TRACE("Prepared next previous result to reuse values.");
1561 }
1562 }
1563
1564 return nullptr;
1565}
1566
1567template<storm::dd::DdType Type, typename ModelType>
1568std::vector<storm::expressions::Expression> GameBasedMdpModelChecker<Type, ModelType>::getInitialPredicates(
1569 storm::expressions::Expression const& constraintExpression, storm::expressions::Expression const& targetStateExpression) {
1570 std::vector<storm::expressions::Expression> initialPredicates;
1571 if (preprocessedModel.isJaniModel()) {
1572 storm::expressions::VariableSetPredicateSplitter splitter(preprocessedModel.asJaniModel().getAllLocationExpressionVariables());
1573
1574 std::vector<storm::expressions::Expression> splitExpressions = splitter.split(targetStateExpression);
1575 initialPredicates.insert(initialPredicates.end(), splitExpressions.begin(), splitExpressions.end());
1576
1577 splitExpressions = splitter.split(constraintExpression);
1578 initialPredicates.insert(initialPredicates.end(), splitExpressions.begin(), splitExpressions.end());
1579 } else {
1580 if (!targetStateExpression.isTrue() && !targetStateExpression.isFalse()) {
1581 initialPredicates.push_back(targetStateExpression);
1582 }
1583 if (!constraintExpression.isTrue() && !constraintExpression.isFalse()) {
1584 initialPredicates.push_back(constraintExpression);
1585 }
1586 }
1587 return initialPredicates;
1588}
1589
1590template<storm::dd::DdType Type, typename ModelType>
1591storm::OptimizationDirection GameBasedMdpModelChecker<Type, ModelType>::getPlayer1Direction(
1593 if (preprocessedModel.getModelType() == storm::storage::SymbolicModelDescription::ModelType::DTMC) {
1594 return storm::OptimizationDirection::Maximize;
1595 } else if (checkTask.isOptimizationDirectionSet()) {
1596 return checkTask.getOptimizationDirection();
1597 } else if (checkTask.isBoundSet() && preprocessedModel.getModelType() != storm::storage::SymbolicModelDescription::ModelType::DTMC) {
1598 return storm::logic::isLowerBound(checkTask.getBoundComparisonType()) ? storm::OptimizationDirection::Minimize : storm::OptimizationDirection::Maximize;
1599 }
1600 STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "Could not derive player 1 optimization direction.");
1601 return storm::OptimizationDirection::Maximize;
1602}
1603
1604template<storm::dd::DdType Type>
1606 if (prob0) {
1608 "Unable to proceed without strategy.");
1609 } else {
1610 STORM_LOG_ASSERT(result.hasPlayer1Strategy() && ((result.getPlayer1States() && !targetStates).isZero() || !result.getPlayer1Strategy().isZero()),
1611 "Unable to proceed without strategy.");
1612 }
1613
1615 "Unable to proceed without strategy.");
1616
1617 return true;
1618}
1619
1620template<storm::dd::DdType Type>
1622 bool result = true;
1623 result &= checkQualitativeStrategies(true, qualitativeResult.prob0Min, targetStates);
1624 result &= checkQualitativeStrategies(false, qualitativeResult.prob1Min, targetStates);
1625 result &= checkQualitativeStrategies(true, qualitativeResult.prob0Max, targetStates);
1626 result &= checkQualitativeStrategies(false, qualitativeResult.prob1Max, targetStates);
1627 return result;
1628}
1629
1630template<storm::dd::DdType Type, typename ModelType>
1631ExplicitQualitativeGameResultMinMax GameBasedMdpModelChecker<Type, ModelType>::computeProb01States(
1632 boost::optional<PreviousExplicitResult<ValueType>> const& previousResult, storm::dd::Odd const& odd, storm::OptimizationDirection player1Direction,
1633 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint64_t> const& player1Groups,
1634 storm::storage::SparseMatrix<ValueType> const& player1BackwardTransitions, std::vector<uint64_t> const& player2BackwardTransitions,
1635 storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates, storage::ExplicitGameStrategyPair& minStrategyPair,
1636 storage::ExplicitGameStrategyPair& maxStrategyPair) {
1637 ExplicitQualitativeGameResultMinMax result;
1638
1639 // ExplicitQualitativeGameResult problematicStates = storm::utility::graph::performProb0(transitionMatrix, player1Groups,
1640 // player1BackwardTransitions, player2BackwardTransitions, constraintStates, targetStates, storm::OptimizationDirection::Minimize,
1641 // storm::OptimizationDirection::Minimize);
1642
1643 result.prob0Min =
1644 storm::utility::graph::performProb0(transitionMatrix, player1Groups, player1BackwardTransitions, player2BackwardTransitions, constraintStates,
1645 targetStates, player1Direction, storm::OptimizationDirection::Minimize, &minStrategyPair);
1646 result.prob1Min =
1647 storm::utility::graph::performProb1(transitionMatrix, player1Groups, player1BackwardTransitions, player2BackwardTransitions, constraintStates,
1648 targetStates, player1Direction, storm::OptimizationDirection::Minimize, &minStrategyPair);
1649 result.prob0Max =
1650 storm::utility::graph::performProb0(transitionMatrix, player1Groups, player1BackwardTransitions, player2BackwardTransitions, constraintStates,
1651 targetStates, player1Direction, storm::OptimizationDirection::Maximize, &maxStrategyPair);
1652 result.prob1Max =
1653 storm::utility::graph::performProb1(transitionMatrix, player1Groups, player1BackwardTransitions, player2BackwardTransitions, constraintStates,
1654 targetStates, player1Direction, storm::OptimizationDirection::Maximize, &maxStrategyPair);
1655
1656 STORM_LOG_INFO("[" << player1Direction << ", " << storm::OptimizationDirection::Minimize << "]: " << result.prob0Min.player1States.getNumberOfSetBits()
1657 << " 'no', " << result.prob1Min.player1States.getNumberOfSetBits() << " 'yes'.");
1658 STORM_LOG_INFO("[" << player1Direction << ", " << storm::OptimizationDirection::Maximize << "]: " << result.prob0Max.player1States.getNumberOfSetBits()
1659 << " 'no', " << result.prob1Max.player1States.getNumberOfSetBits() << " 'yes'.");
1660
1661 return result;
1662}
1663
1664template<storm::dd::DdType Type, typename ModelType>
1665SymbolicQualitativeGameResultMinMax<Type> GameBasedMdpModelChecker<Type, ModelType>::computeProb01States(
1666 boost::optional<SymbolicQualitativeGameResultMinMax<Type>> const& previousQualitativeResult,
1668 storm::dd::Bdd<Type> const& transitionMatrixBdd, storm::dd::Bdd<Type> const& constraintStates, storm::dd::Bdd<Type> const& targetStates) {
1669 SymbolicQualitativeGameResultMinMax<Type> result;
1670
1671 if (reuseQualitativeResults) {
1672 // Depending on the player 1 direction, we choose a different order of operations.
1673 if (player1Direction == storm::OptimizationDirection::Minimize) {
1674 // (1) min/min: compute prob0 using the game functions
1675 result.prob0Min = storm::utility::graph::performProb0(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1676 storm::OptimizationDirection::Minimize, true, true);
1677
1678 // (2) min/min: compute prob1 using the MDP functions
1679 storm::dd::Bdd<Type> candidates = game.getReachableStates() && !result.prob0Min.player1States;
1681 game, transitionMatrixBdd, previousQualitativeResult ? previousQualitativeResult.get().prob1Min.player1States : targetStates, candidates);
1682
1683 // (3) min/min: compute prob1 using the game functions
1684 result.prob1Min = storm::utility::graph::performProb1(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1685 storm::OptimizationDirection::Minimize, true, true, boost::make_optional(prob1MinMinMdp));
1686
1687 // (4) min/max: compute prob 0 using the game functions
1688 result.prob0Max = storm::utility::graph::performProb0(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1689 storm::OptimizationDirection::Maximize, true, true);
1690
1691 // (5) min/max: compute prob 1 using the game functions
1692 // We know that only previous prob1 states can now be prob 1 states again, because the upper bound
1693 // values can only decrease over iterations.
1694 boost::optional<storm::dd::Bdd<Type>> prob1Candidates;
1695 if (previousQualitativeResult) {
1696 prob1Candidates = previousQualitativeResult.get().prob1Max.player1States;
1697 }
1698 result.prob1Max = storm::utility::graph::performProb1(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1699 storm::OptimizationDirection::Maximize, true, true, prob1Candidates);
1700 } else {
1701 // (1) max/max: compute prob0 using the game functions
1702 result.prob0Max = storm::utility::graph::performProb0(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1703 storm::OptimizationDirection::Maximize, true, true);
1704
1705 // (2) max/max: compute prob1 using the MDP functions, reuse prob1 states of last iteration to constrain the candidate states.
1706 storm::dd::Bdd<Type> candidates = game.getReachableStates() && !result.prob0Max.player1States;
1707 if (previousQualitativeResult) {
1708 candidates &= previousQualitativeResult.get().prob1Max.player1States;
1709 }
1710 storm::dd::Bdd<Type> prob1MaxMaxMdp = storm::utility::graph::performProb1E(game, transitionMatrixBdd, constraintStates, targetStates, candidates);
1711
1712 // (3) max/max: compute prob1 using the game functions, reuse prob1 states from the MDP precomputation
1713 result.prob1Max = storm::utility::graph::performProb1(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1714 storm::OptimizationDirection::Maximize, true, true, boost::make_optional(prob1MaxMaxMdp));
1715
1716 // (4) max/min: compute prob0 using the game functions
1717 result.prob0Min = storm::utility::graph::performProb0(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1718 storm::OptimizationDirection::Minimize, true, true);
1719
1720 // (5) max/min: compute prob1 using the game functions, use prob1 from max/max as the candidate set
1721 result.prob1Min = storm::utility::graph::performProb1(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1722 storm::OptimizationDirection::Minimize, true, true, boost::make_optional(prob1MaxMaxMdp));
1723 }
1724 } else {
1725 result.prob0Min = storm::utility::graph::performProb0(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1726 storm::OptimizationDirection::Minimize, true, true);
1727 result.prob1Min = storm::utility::graph::performProb1(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1728 storm::OptimizationDirection::Minimize, true, true);
1729 result.prob0Max = storm::utility::graph::performProb0(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1730 storm::OptimizationDirection::Maximize, true, true);
1731 result.prob1Max = storm::utility::graph::performProb1(game, transitionMatrixBdd, constraintStates, targetStates, player1Direction,
1732 storm::OptimizationDirection::Maximize, true, true);
1733 }
1734
1735 STORM_LOG_INFO("[" << player1Direction << ", " << storm::OptimizationDirection::Minimize << "]: " << result.prob0Min.player1States.getNonZeroCount()
1736 << " 'no', " << result.prob1Min.player1States.getNonZeroCount() << " 'yes'.");
1737 STORM_LOG_INFO("[" << player1Direction << ", " << storm::OptimizationDirection::Maximize << "]: " << result.prob0Max.player1States.getNonZeroCount()
1738 << " 'no', " << result.prob1Max.player1States.getNonZeroCount() << " 'yes'.");
1739
1740 STORM_LOG_ASSERT(checkQualitativeStrategies(result, targetStates), "Qualitative strategies appear to be broken.");
1741 return result;
1742}
1743
1744template<storm::dd::DdType Type, typename ModelType>
1745void GameBasedMdpModelChecker<Type, ModelType>::printStatistics(storm::gbar::abstraction::MenuGameAbstractor<Type, ValueType> const& abstractor,
1746 storm::gbar::abstraction::MenuGame<Type, ValueType> const& game, uint64_t refinements,
1747 uint64_t peakPlayer1States, uint64_t peakTransitions) const {
1749 storm::gbar::abstraction::AbstractionInformation<Type> const& abstractionInformation = abstractor.getAbstractionInformation();
1750
1751 std::streamsize originalPrecision = std::cout.precision();
1752 std::cout << std::fixed << std::setprecision(2);
1753
1754 std::cout << '\n';
1755 std::cout << "Statistics:\n";
1756 std::cout << " * size of final game: " << game.getReachableStates().getNonZeroCount() << " player 1 states, "
1757 << game.getTransitionMatrix().getNonZeroCount() << " transitions\n";
1758 std::cout << " * peak size of game: " << peakPlayer1States << " player 1 states, " << peakTransitions << " transitions\n";
1759 std::cout << " * refinements: " << refinements << '\n';
1760 std::cout << " * predicates: " << abstractionInformation.getNumberOfPredicates() << "\n\n";
1761
1762 uint64_t totalAbstractionTimeMillis = totalAbstractionWatch.getTimeInMilliseconds();
1763 uint64_t totalTranslationTimeMillis = totalTranslationWatch.getTimeInMilliseconds();
1764 uint64_t totalStrategyProcessingTimeMillis = totalStrategyProcessingWatch.getTimeInMilliseconds();
1765 uint64_t totalSolutionTimeMillis = totalSolutionWatch.getTimeInMilliseconds();
1766 uint64_t totalRefinementTimeMillis = totalRefinementWatch.getTimeInMilliseconds();
1767 uint64_t setupTime = setupWatch.getTimeInMilliseconds();
1768 uint64_t totalTimeMillis = totalWatch.getTimeInMilliseconds();
1769
1770 std::cout << "Time breakdown:\n";
1771 std::cout << " * setup: " << setupTime << "ms (" << 100 * static_cast<double>(setupTime) / totalTimeMillis << "%)\n";
1772 std::cout << " * abstraction: " << totalAbstractionTimeMillis << "ms (" << 100 * static_cast<double>(totalAbstractionTimeMillis) / totalTimeMillis
1773 << "%)\n";
1775 std::cout << " * translation: " << totalTranslationTimeMillis << "ms ("
1776 << 100 * static_cast<double>(totalTranslationTimeMillis) / totalTimeMillis << "%)\n";
1777 if (fixPlayer1Strategy || fixPlayer2Strategy) {
1778 std::cout << " * strategy processing: " << totalStrategyProcessingTimeMillis << "ms ("
1779 << 100 * static_cast<double>(totalStrategyProcessingTimeMillis) / totalTimeMillis << "%)\n";
1780 }
1781 }
1782 std::cout << " * solution: " << totalSolutionTimeMillis << "ms (" << 100 * static_cast<double>(totalSolutionTimeMillis) / totalTimeMillis << "%)\n";
1783 std::cout << " * refinement: " << totalRefinementTimeMillis << "ms (" << 100 * static_cast<double>(totalRefinementTimeMillis) / totalTimeMillis
1784 << "%)\n";
1785 std::cout << " ---------------------------------------------\n";
1786 std::cout << " * total: " << totalTimeMillis << "ms\n\n";
1787
1788 std::cout << std::defaultfloat << std::setprecision(originalPrecision);
1789 }
1790}
1791
1792template<storm::dd::DdType Type, typename ModelType>
1793storm::expressions::Expression GameBasedMdpModelChecker<Type, ModelType>::getExpression(storm::logic::Formula const& formula) {
1795 storm::exceptions::InvalidPropertyException, "The target states have to be given as label or an expression.");
1797 if (formula.isAtomicLabelFormula()) {
1798 result = preprocessedModel.asPrismProgram().getLabelExpression(formula.asAtomicLabelFormula().getLabel());
1799 } else if (formula.isAtomicExpressionFormula()) {
1800 result = formula.asAtomicExpressionFormula().getExpression();
1801 } else {
1802 result =
1803 formula.asBooleanLiteralFormula().isTrueFormula() ? preprocessedModel.getManager().boolean(true) : preprocessedModel.getManager().boolean(false);
1804 }
1805 return result;
1806}
1807
1808template class GameBasedMdpModelChecker<storm::dd::DdType::CUDD, storm::models::symbolic::Dtmc<storm::dd::DdType::CUDD, double>>;
1809template class GameBasedMdpModelChecker<storm::dd::DdType::CUDD, storm::models::symbolic::Mdp<storm::dd::DdType::CUDD, double>>;
1810template class GameBasedMdpModelChecker<storm::dd::DdType::Sylvan, storm::models::symbolic::Dtmc<storm::dd::DdType::Sylvan, double>>;
1811template class GameBasedMdpModelChecker<storm::dd::DdType::Sylvan, storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan, double>>;
1812
1813template class GameBasedMdpModelChecker<storm::dd::DdType::Sylvan, storm::models::symbolic::Dtmc<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
1814template class GameBasedMdpModelChecker<storm::dd::DdType::Sylvan, storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
1815} // namespace modelchecker
1816} // namespace storm::gbar
Add< LibraryType, ValueType > swapVariables(std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const &metaVariablePairs) const
Swaps the given pairs of meta variables in the ADD.
Definition Add.cpp:292
MatrixAndLabeling toLabeledMatrix(std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables, std::set< storm::expressions::Variable > const &groupMetaVariables, storm::dd::Odd const &rowOdd, storm::dd::Odd const &columnOdd, std::vector< std::set< storm::expressions::Variable > > const &labelMetaVariables=std::vector< std::set< storm::expressions::Variable > >()) const
Definition Add.cpp:752
ValueType getMax() const
Retrieves the highest function value of any encoding.
Definition Add.cpp:475
ValueType getMin() const
Retrieves the lowest function value of any encoding.
Definition Add.cpp:470
virtual uint_fast64_t getNonZeroCount() const override
Retrieves the number of encodings that are mapped to a non-zero value.
Definition Add.cpp:451
Add< LibraryType, ValueType > sumAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Sum-abstracts from the given meta variables.
Definition Add.cpp:178
virtual uint_fast64_t getNodeCount() const override
Retrieves the number of nodes necessary to represent the DD.
Definition Add.cpp:465
Bdd< LibraryType > toBdd() const
Converts the ADD to a BDD by mapping all values unequal to zero to 1.
Definition Add.cpp:1187
Bdd< LibraryType > existsAbstract(std::set< storm::expressions::Variable > const &metaVariables) const
Existentially abstracts from the given meta variables.
Definition Bdd.cpp:178
bool isZero() const
Retrieves whether this DD represents the constant zero function.
Definition Bdd.cpp:547
virtual uint_fast64_t getNonZeroCount() const override
Retrieves the number of encodings that are mapped to a non-zero value.
Definition Bdd.cpp:513
Bdd< LibraryType > swapVariables(std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const &metaVariablePairs) const
Swaps the given pairs of meta variables in the BDD.
Definition Bdd.cpp:302
Odd createOdd() const
Creates an ODD based on the current BDD.
Definition Bdd.cpp:571
storm::storage::BitVector toVector(storm::dd::Odd const &rowOdd) const
Converts the BDD to a bit vector.
Definition Bdd.cpp:497
bool isFalse() const
Checks if the expression is equal to the boolean literal false.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this expression.
bool isTrue() const
Checks if the expression is equal to the boolean literal true.
Expression boolean(bool value) const
Creates an expression that characterizes the given boolean literal.
std::size_t getNumberOfPredicates() const
Retrieves the number of predicates.
virtual storm::storage::BitVector const & getStates() const =0
ExplicitQuantitativeResult< ValueType > const & getMin() const
ExplicitQuantitativeResult< ValueType > const & getMax() const
virtual AbstractionInformation< DdType > const & getAbstractionInformation() const =0
Retrieves information about the abstraction.
This class represents a discrete-time stochastic two-player game.
Definition MenuGame.h:16
storm::dd::Bdd< Type > getBottomStates() const
Retrieves the bottom states of the model.
Definition MenuGame.cpp:68
void refine(std::vector< storm::expressions::Expression > const &predicates, bool allowInjection=true) const
Refines the abstractor with the given predicates.
boost::optional< std::pair< ValueType, ValueType > > initialStatesRange
std::pair< ValueType, ValueType > const & getInitialStatesRange() const
void exportToJson(std::string const &filename, std::vector< uint64_t > const &player1Groups, std::vector< uint64_t > const &player2Groups, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::BitVector const &initialStates, storm::storage::BitVector const &constraintStates, storm::storage::BitVector const &targetStates, ExplicitQuantitativeResultMinMax< ValueType > const &quantitativeResult, storage::ExplicitGameStrategyPair const *minStrategyPair, storage::ExplicitGameStrategyPair const *maxStrategyPair)
virtual std::unique_ptr< storm::modelchecker::CheckResult > computeReachabilityProbabilities(Environment const &env, storm::modelchecker::CheckTask< storm::logic::EventuallyFormula, ValueType > const &checkTask) override
virtual std::unique_ptr< storm::modelchecker::CheckResult > computeUntilProbabilities(Environment const &env, storm::modelchecker::CheckTask< storm::logic::UntilFormula, ValueType > const &checkTask) override
GameBasedMdpModelChecker(storm::storage::SymbolicModelDescription const &model, GameBasedMdpModelCheckerOptions const &options=GameBasedMdpModelCheckerOptions(), std::shared_ptr< storm::utility::solver::SmtSolverFactory > const &smtSolverFactory=std::make_shared< storm::utility::solver::MathsatSmtSolverFactory >())
Constructs a model checker whose underlying model is implicitly given by the provided program.
virtual bool canHandle(storm::modelchecker::CheckTask< storm::logic::Formula, ValueType > const &checkTask) const override
Overridden methods from super class.
VariableSet & getGlobalVariables()
Retrieves the variables of this automaton.
Definition Model.cpp:721
ModelType const & getModelType() const
Retrieves the type of the model.
Definition Model.cpp:121
storm::expressions::Expression getLabelExpression(Variable const &transientVariable, std::vector< std::reference_wrapper< Automaton const > > const &automata) const
Creates the expression that characterizes all states in which the provided transient boolean variable...
Definition Model.cpp:1420
Model flattenComposition(std::shared_ptr< storm::utility::solver::SmtSolverFactory > const &smtSolverFactory=std::make_shared< storm::utility::solver::SmtSolverFactory >()) const
Flatten the composition to obtain an equivalent model that contains exactly one automaton that has th...
Definition Model.cpp:442
detail::Variables< Variable > getBooleanVariables()
Retrieves the boolean variables in this set.
storm::expressions::Expression const & getExpression() const
std::string const & getLabel() const
Formula const & getRightSubformula() const
Formula const & getLeftSubformula() const
virtual bool isTrueFormula() const override
virtual bool isBooleanLiteralFormula() const
Definition Formula.cpp:60
storm::expressions::Expression toExpression(storm::expressions::ExpressionManager const &manager, std::map< std::string, storm::expressions::Expression > const &labelToExpressionMapping={}) const
Takes the formula and converts it to an equivalent expression.
Definition Formula.cpp:538
BooleanLiteralFormula & asBooleanLiteralFormula()
Definition Formula.cpp:285
AtomicExpressionFormula & asAtomicExpressionFormula()
Definition Formula.cpp:293
bool isInFragment(FragmentSpecification const &fragment) const
Definition Formula.cpp:196
AtomicLabelFormula & asAtomicLabelFormula()
Definition Formula.cpp:301
virtual bool isAtomicLabelFormula() const
Definition Formula.cpp:76
virtual bool isAtomicExpressionFormula() const
Definition Formula.cpp:72
Formula const & getSubformula() const
bool isBoundSet() const
Retrieves whether there is a bound with which the values for the states will be compared.
Definition CheckTask.h:219
ValueType getBoundThreshold() const
Retrieves the value of the bound (if set).
Definition CheckTask.h:226
storm::logic::ComparisonType const & getBoundComparisonType() const
Retrieves the comparison type of the bound (if set).
Definition CheckTask.h:235
bool isOptimizationDirectionSet() const
Retrieves whether an optimization direction was set.
Definition CheckTask.h:147
FormulaType const & getFormula() const
Retrieves the formula from this task.
Definition CheckTask.h:140
storm::OptimizationDirection const & getOptimizationDirection() const
Retrieves the optimization direction (if set).
Definition CheckTask.h:154
bool isOnlyInitialStatesRelevantSet() const
Retrieves whether only the initial states are relevant in the computation.
Definition CheckTask.h:204
static std::vector< ValueType > computeUntilProbabilities(Environment const &env, storm::solver::SolveGoal< ValueType > &&goal, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool qualitative, ModelCheckerHint const &hint=ModelCheckerHint())
storm::dd::DdManager< Type > & getManager() const
Retrieves the manager responsible for the DDs that represent this model.
Definition Model.cpp:92
storm::dd::Add< Type, ValueType > const & getTransitionMatrix() const
Retrieves the matrix representing the transitions of the model.
Definition Model.cpp:177
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:197
storm::dd::Bdd< Type > const & getInitialStates() const
Retrieves the initial states of the model.
Definition Model.cpp:107
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:223
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:192
virtual uint_fast64_t getNumberOfTransitions() const override
Returns the number of (non-zero) transitions of the model.
Definition Model.cpp:82
storm::dd::Bdd< Type > const & getReachableStates() const
Retrieves the reachable states of the model.
Definition Model.cpp:102
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:77
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.
storm::dd::Bdd< Type > getIllegalPlayer1Mask() const
Retrieves a BDD characterizing all illegal player 1 choice encodings in the model.
uint64_t getNumberOfPlayer2States() const
Retrieves the number of player 2 states in the game.
storm::dd::Bdd< Type > getIllegalPlayer2Mask() const
Retrieves a BDD characterizing all illegal player 2 choice encodings in the model.
ModelType getModelType() const
Retrieves the model type of the model.
Definition Program.cpp:247
Program substituteFormulas() const
Substitutes all formulas appearing in the expressions of the program by their defining expressions.
Definition Program.cpp:1030
std::size_t getNumberOfModules() const
Retrieves the number of modules in the program.
Definition Program.cpp:611
Program flattenModules(std::shared_ptr< storm::utility::solver::SmtSolverFactory > const &smtSolverFactory=std::shared_ptr< storm::utility::solver::SmtSolverFactory >(new storm::utility::solver::SmtSolverFactory())) const
Creates an equivalent program that contains exactly one module.
Definition Program.cpp:1951
virtual std::unique_ptr< GameSolver< ValueType > > create(Environment const &env, storm::storage::SparseMatrix< storm::storage::sparse::state_type > const &player1Matrix, storm::storage::SparseMatrix< ValueType > const &player2Matrix) const
virtual std::unique_ptr< storm::solver::SymbolicGameSolver< Type, ValueType > > create(storm::dd::Add< Type, ValueType > const &A, storm::dd::Bdd< Type > const &allRows, storm::dd::Bdd< Type > const &illegalPlayer1Mask, storm::dd::Bdd< Type > const &illegalPlayer2Mask, std::set< storm::expressions::Variable > const &rowMetaVariables, std::set< storm::expressions::Variable > const &columnMetaVariables, std::vector< std::pair< storm::expressions::Variable, storm::expressions::Variable > > const &rowColumnMetaVariablePairs, std::set< storm::expressions::Variable > const &player1Variables, std::set< storm::expressions::Variable > const &player2Variables) const
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
bool isDisjointFrom(BitVector const &other) const
Checks whether none of the bits that are set in the current bit vector are also set in the given bit ...
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
bool isSubsetOf(BitVector const &other) const
Checks whether all bits that are set in the current bit vector are also set in the given bit vector.
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
bool get(uint_fast64_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
void setChoice(uint64_t state, uint64_t choice)
uint64_t getChoice(uint64_t state) const
A class that can be used to build a sparse matrix by adding value by value.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
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.
SparseMatrix getSubmatrix(bool useGroups, storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint, bool insertDiagonalEntries=false, storm::storage::BitVector const &makeZeroColumns=storm::storage::BitVector()) const
Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not set to ...
std::vector< index_type > swapRowGroupIndices(std::vector< index_type > &&newRowGrouping)
Swaps the grouping of rows of this matrix.
value_type multiplyRowWithVector(index_type row, std::vector< value_type > const &vector) const
Multiplies a single row of the matrix with the given vector and returns the result.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
std::vector< value_type > getConstrainedRowGroupSumVector(storm::storage::BitVector const &rowGroupConstraint, storm::storage::BitVector const &columnConstraint) const
Computes a vector whose entries represent the sums of selected columns for all rows in selected row g...
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
storm::prism::Program const & asPrismProgram() const
std::vector< storm::expressions::Variable > getUndefinedConstants() const
storm::jani::Model const & asJaniModel() const
bool isZero(ValueType const &value) const
bool isEqual(ValueType const &value1, ValueType const &value2) const
A class that provides convenience operations to display run times.
Definition Stopwatch.h:14
#define STORM_LOG_INFO(message)
Definition logging.h:29
#define STORM_LOG_WARN(message)
Definition logging.h:30
#define STORM_LOG_DEBUG(message)
Definition logging.h:23
#define STORM_LOG_TRACE(message)
Definition logging.h:17
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:38
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
SFTBDDChecker::ValueType ValueType
std::unique_ptr< storm::modelchecker::CheckResult > checkForResultAfterQuantitativeCheck(storm::modelchecker::CheckTask< storm::logic::Formula, ValueType > const &checkTask, storm::OptimizationDirection const &player2Direction, std::pair< ValueType, ValueType > const &initialValueRange)
void postProcessStrategies(storm::OptimizationDirection const &player1Direction, storage::ExplicitGameStrategyPair &minStrategyPair, storage::ExplicitGameStrategyPair &maxStrategyPair, std::vector< uint64_t > const &player1Groups, std::vector< uint64_t > const &player2Groups, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::BitVector const &constraintStates, storm::storage::BitVector const &targetStates, ExplicitQualitativeGameResultMinMax const &qualitativeResult, bool redirectPlayer1, bool redirectPlayer2, bool sanityCheck)
std::unique_ptr< storm::modelchecker::CheckResult > checkForResultAfterQualitativeCheck(storm::modelchecker::CheckTask< storm::logic::Formula, ValueType > const &checkTask, storm::OptimizationDirection player2Direction, storm::dd::Bdd< Type > const &initialStates, storm::dd::Bdd< Type > const &prob0, storm::dd::Bdd< Type > const &prob1)
SymbolicQuantitativeGameResult< Type, ValueType > computeQuantitativeResult(Environment const &env, storm::OptimizationDirection player1Direction, storm::OptimizationDirection player2Direction, storm::gbar::abstraction::MenuGame< Type, ValueType > const &game, SymbolicQualitativeGameResultMinMax< Type > const &qualitativeResult, storm::dd::Add< Type, ValueType > const &initialStatesAdd, storm::dd::Bdd< Type > const &maybeStates, boost::optional< SymbolicQuantitativeGameResult< Type, ValueType > > const &startInfo=boost::none)
bool checkQualitativeStrategies(bool prob0, SymbolicQualitativeGameResult< Type > const &result, storm::dd::Bdd< Type > const &targetStates)
SymbolicQuantitativeGameResult< Type, ValueType > solveMaybeStates(Environment const &env, storm::OptimizationDirection const &player1Direction, storm::OptimizationDirection const &player2Direction, storm::gbar::abstraction::MenuGame< Type, ValueType > const &game, storm::dd::Bdd< Type > const &maybeStates, storm::dd::Bdd< Type > const &prob1States, boost::optional< SymbolicQuantitativeGameResult< Type, ValueType > > const &startInfo=boost::none)
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18
bool isLowerBound(ComparisonType t)
bool isStrict(ComparisonType t)
FragmentSpecification reachability()
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)
SettingsType const & getModule()
Get module.
ExplicitGameProb01Result performProb0(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, std::vector< uint64_t > const &player1Groups, storm::storage::SparseMatrix< ValueType > const &player1BackwardTransitions, std::vector< uint64_t > const &player2BackwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, storm::OptimizationDirection const &player1Direction, storm::OptimizationDirection const &player2Direction, storm::storage::ExplicitGameStrategyPair *strategyPair)
Computes the set of states that have probability 0 given the strategies of the two players.
Definition graph.cpp:1308
storm::storage::BitVector performProb1A(storm::models::sparse::NondeterministicModel< T, RM > const &model, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 1 of satisfying phi until psi under all possible re...
Definition graph.cpp:997
storm::storage::BitVector performProb1(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &, storm::storage::BitVector const &psiStates, storm::storage::BitVector const &statesWithProbabilityGreater0)
Computes the set of states of the given model for which all paths lead to the given set of target sta...
Definition graph.cpp:383
storm::storage::BitVector performProb1E(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability 1 of satisfying phi until psi under at least one po...
Definition graph.cpp:757
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:83
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
Definition vector.h:189
ValueType max(ValueType const &first, ValueType const &second)
ValueType min(ValueType const &first, ValueType const &second)
ValueType abs(ValueType const &number)
LabParser.cpp.
Definition cli.cpp:18
Converts the ADD to a row-grouped (sparse) matrix.
Definition Add.h:673
std::vector< std::vector< uint64_t > > labelings
Definition Add.h:685
storm::storage::SparseMatrix< ValueType > matrix
Definition Add.h:684
storm::storage::BitVector const & getPlayer1States() const
Definition graph.h:760
storm::storage::BitVector const & getPlayer2States() const
Definition graph.h:764
storm::dd::Bdd< Type > const & getPlayer2States() const
Definition graph.h:703
storm::dd::Bdd< Type > const & getPlayer1Strategy() const
Definition graph.h:679
storm::dd::Bdd< Type > const & getPlayer2Strategy() const
Definition graph.h:691
storm::dd::Bdd< Type > const & getPlayer1States() const
Definition graph.h:699