Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
StandardGameSolver.cpp
Go to the documentation of this file.
2
7
15#include "storm/utility/graph.h"
18
19namespace storm {
20namespace solver {
21
22template<typename ValueType>
24 storm::storage::SparseMatrix<ValueType> const& player2Matrix,
25 std::unique_ptr<LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory)
26 : linearEquationSolverFactory(std::move(linearEquationSolverFactory)),
27 localPlayer1Grouping(nullptr),
28 localPlayer1Matrix(nullptr),
29 localPlayer2Matrix(nullptr),
30 player1Grouping(nullptr),
31 player1Matrix(&player1Matrix),
32 player2Matrix(player2Matrix) {}
33
34template<typename ValueType>
37 std::unique_ptr<LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory)
38 : linearEquationSolverFactory(std::move(linearEquationSolverFactory)),
39 localPlayer1Grouping(nullptr),
40 localPlayer1Matrix(std::make_unique<storm::storage::SparseMatrix<storm::storage::sparse::state_type>>(std::move(player1Matrix))),
41 localPlayer2Matrix(std::make_unique<storm::storage::SparseMatrix<ValueType>>(std::move(player2Matrix))),
42 player1Grouping(nullptr),
43 player1Matrix(localPlayer1Matrix.get()),
44 player2Matrix(*localPlayer2Matrix) {}
45
46template<typename ValueType>
47StandardGameSolver<ValueType>::StandardGameSolver(std::vector<uint64_t> const& player1Grouping, storm::storage::SparseMatrix<ValueType> const& player2Matrix,
48 std::unique_ptr<LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory)
49 : linearEquationSolverFactory(std::move(linearEquationSolverFactory)),
50 localPlayer1Grouping(nullptr),
51 localPlayer1Matrix(nullptr),
52 localPlayer2Matrix(nullptr),
53 player1Grouping(&player1Grouping),
54 player1Matrix(nullptr),
55 player2Matrix(player2Matrix) {}
56
57template<typename ValueType>
59 std::unique_ptr<LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory)
60 : linearEquationSolverFactory(std::move(linearEquationSolverFactory)),
61 localPlayer1Grouping(std::make_unique<std::vector<uint64_t>>(std::move(player1Grouping))),
62 localPlayer1Matrix(nullptr),
63 localPlayer2Matrix(std::make_unique<storm::storage::SparseMatrix<ValueType>>(std::move(player2Matrix))),
64 player1Grouping(localPlayer1Grouping.get()),
65 player1Matrix(nullptr),
66 player2Matrix(*localPlayer2Matrix) {}
67
68template<typename ValueType>
69GameMethod StandardGameSolver<ValueType>::getMethod(Environment const& env, bool isExactMode) const {
70 auto method = env.solver().game().getMethod();
71 if (isExactMode && method != GameMethod::PolicyIteration) {
72 if (env.solver().game().isMethodSetFromDefault()) {
73 method = GameMethod::PolicyIteration;
74 STORM_LOG_INFO("Changing game method to policy-iteration to guarantee exact results. If you want to override this, specify another method.");
75 } else {
76 STORM_LOG_WARN("The selected game method does not guarantee exact results.");
77 }
78 } else if (env.solver().isForceSoundness() && method != GameMethod::PolicyIteration) {
79 if (env.solver().game().isMethodSetFromDefault()) {
80 method = GameMethod::PolicyIteration;
81 STORM_LOG_INFO("Changing game method to policy-iteration to guarantee sound results. If you want to override this, specify another method.");
82 } else {
83 STORM_LOG_WARN("The selected game method does not guarantee sound results.");
84 }
85 }
86 return method;
87}
88
89template<typename ValueType>
91 std::vector<ValueType>& x, std::vector<ValueType> const& b, std::vector<uint64_t>* player1Choices,
92 std::vector<uint64_t>* player2Choices) const {
93 auto method = getMethod(env, std::is_same<ValueType, storm::RationalNumber>::value || env.solver().isForceExact());
94 STORM_LOG_INFO("Solving stochastic two player game over " << x.size() << " states using " << toString(method) << ".");
95 switch (method) {
96 case GameMethod::ValueIteration:
97 return solveGameValueIteration(env, player1Dir, player2Dir, x, b, player1Choices, player2Choices);
98 case GameMethod::PolicyIteration:
99 return solveGamePolicyIteration(env, player1Dir, player2Dir, x, b, player1Choices, player2Choices);
100 default:
101 STORM_LOG_THROW(false, storm::exceptions::InvalidEnvironmentException, "This solver does not implement the selected solution method.");
102 }
103 return false;
104}
105
106template<typename ValueType>
107bool StandardGameSolver<ValueType>::solveGamePolicyIteration(Environment const& env, OptimizationDirection player1Dir, OptimizationDirection player2Dir,
108 std::vector<ValueType>& x, std::vector<ValueType> const& b,
109 std::vector<uint64_t>* providedPlayer1Choices,
110 std::vector<uint64_t>* providedPlayer2Choices) const {
111 // Create the initial choice selections.
112 std::vector<storm::storage::sparse::state_type>* player1Choices;
113 std::unique_ptr<std::vector<storm::storage::sparse::state_type>> localPlayer1Choices;
114 std::vector<storm::storage::sparse::state_type>* player2Choices;
115 std::unique_ptr<std::vector<storm::storage::sparse::state_type>> localPlayer2Choices;
116
117 if (providedPlayer1Choices && providedPlayer2Choices) {
118 player1Choices = providedPlayer1Choices;
119 player2Choices = providedPlayer2Choices;
120 } else {
121 localPlayer1Choices = std::make_unique<std::vector<uint64_t>>();
122 player1Choices = localPlayer1Choices.get();
123 localPlayer2Choices = std::make_unique<std::vector<uint64_t>>();
124 player2Choices = localPlayer2Choices.get();
125 }
126
127 if (this->hasSchedulerHints()) {
128 *player1Choices = this->player1ChoicesHint.get();
129 } else if (this->player1RepresentedByMatrix()) {
130 // Player 1 represented by matrix.
131 *player1Choices = std::vector<storm::storage::sparse::state_type>(this->getPlayer1Matrix().getRowGroupCount(), 0);
132 } else {
133 // Player 1 represented by grouping of player 2 states.
134 player1Choices->resize(player1Choices->size() - 1);
135 }
136 if (this->hasSchedulerHints()) {
137 *player2Choices = this->player2ChoicesHint.get();
138 } else if (!(providedPlayer1Choices && providedPlayer2Choices)) {
139 player2Choices->resize(this->player2Matrix.getRowGroupCount());
140 }
141
142 if (!auxiliaryP2RowGroupVector) {
143 auxiliaryP2RowGroupVector = std::make_unique<std::vector<ValueType>>(this->player2Matrix.getRowGroupCount());
144 }
145 if (!auxiliaryP1RowGroupVector) {
146 auxiliaryP1RowGroupVector = std::make_unique<std::vector<ValueType>>(this->player1RepresentedByMatrix() ? this->player1Matrix->getRowGroupCount()
147 : this->player1Grouping->size() - 1);
148 }
149 std::vector<ValueType>& subB = *auxiliaryP1RowGroupVector;
150
151 uint64_t maxIter = env.solver().game().getMaximalNumberOfIterations();
152
153 // The linear equation solver should be at least as precise as this solver.
154 std::unique_ptr<storm::Environment> environmentOfSolverStorage;
157 bool changePrecision = precOfSolver.first && precOfSolver.first.get() > env.solver().game().getPrecision();
158 bool changeRelative = precOfSolver.second && !precOfSolver.second.get() && env.solver().game().getRelativeTerminationCriterion();
159 if (changePrecision || changeRelative) {
160 environmentOfSolverStorage = std::make_unique<storm::Environment>(env);
161 boost::optional<storm::RationalNumber> newPrecision;
162 boost::optional<bool> newRelative;
163 if (changePrecision) {
164 newPrecision = env.solver().game().getPrecision();
165 }
166 if (changeRelative) {
167 newRelative = true;
168 }
169 environmentOfSolverStorage->solver().setLinearEquationSolverPrecision(newPrecision, newRelative);
170 }
171 }
172 storm::Environment const& environmentOfSolver = environmentOfSolverStorage ? *environmentOfSolverStorage : env;
173
174 // Solve the equation system induced by the two schedulers.
175 storm::storage::SparseMatrix<ValueType> submatrix;
176 getInducedMatrixVector(x, b, *player1Choices, *player2Choices, submatrix, subB);
177
178 storm::storage::BitVector targetStates;
179 storm::storage::BitVector zeroStates;
180 if (!this->hasUniqueSolution()) {
181 // If there is no unique solution, we need to compute the states with probability 0 and set their values explicitly.
183 zeroStates = ~storm::utility::graph::performProbGreater0(submatrix.transpose(), storm::storage::BitVector(targetStates.size(), true), targetStates);
184 }
185 bool asEquationSystem = false;
186 if (this->linearEquationSolverFactory->getEquationProblemFormat(environmentOfSolver) == LinearEquationSolverProblemFormat::EquationSystem) {
187 submatrix.convertToEquationSystem();
188 asEquationSystem = true;
189 }
190 if (!this->hasUniqueSolution()) {
191 for (uint64_t state : zeroStates) {
192 for (auto& element : submatrix.getRow(state)) {
193 if (element.getColumn() == state) {
194 element.setValue(asEquationSystem ? storm::utility::one<ValueType>() : storm::utility::zero<ValueType>());
195 } else {
196 element.setValue(storm::utility::zero<ValueType>());
197 }
198 }
199 subB[state] = storm::utility::zero<ValueType>();
200 }
201 }
202 auto submatrixSolver = linearEquationSolverFactory->create(environmentOfSolver, std::move(submatrix));
203 if (this->lowerBound) {
204 submatrixSolver->setLowerBound(this->lowerBound.get());
205 }
206 if (this->upperBound) {
207 submatrixSolver->setUpperBound(this->upperBound.get());
208 }
209 submatrixSolver->setCachingEnabled(true);
210
212 uint64_t iterations = 0;
213 do {
214 submatrixSolver->solveEquations(environmentOfSolver, x, subB);
215
216 // Check whether we can improve local choices.
217 bool schedulerImproved =
218 extractChoices(environmentOfSolver, player1Dir, player2Dir, x, b, *auxiliaryP2RowGroupVector, *player1Choices, *player2Choices);
219
220 // If the scheduler did not improve, we are done.
221 if (!schedulerImproved) {
223 } else {
224 // Update the solver.
225 getInducedMatrixVector(x, b, *player1Choices, *player2Choices, submatrix, subB);
226
227 if (!this->hasUniqueSolution()) {
228 // If there is no unique solution, we need to compute the states with probability 0 and set their values explicitly.
230 zeroStates =
231 ~storm::utility::graph::performProbGreater0(submatrix.transpose(), storm::storage::BitVector(targetStates.size(), true), targetStates);
232 }
233 if (this->linearEquationSolverFactory->getEquationProblemFormat(environmentOfSolver) == LinearEquationSolverProblemFormat::EquationSystem) {
234 submatrix.convertToEquationSystem();
235 }
236 if (!this->hasUniqueSolution()) {
237 for (uint64_t state : zeroStates) {
238 for (auto& element : submatrix.getRow(state)) {
239 if (element.getColumn() == state) {
240 element.setValue(asEquationSystem ? storm::utility::one<ValueType>() : storm::utility::zero<ValueType>());
241 } else {
242 element.setValue(storm::utility::zero<ValueType>());
243 }
244 }
245 subB[state] = storm::utility::zero<ValueType>();
246 }
247 }
248
249 submatrixSolver->setMatrix(std::move(submatrix));
250 }
251
252 // Update environment variables.
253 ++iterations;
254 status = this->updateStatus(status, x, SolverGuarantee::None, iterations, maxIter);
255 } while (status == SolverStatus::InProgress);
256
257 this->reportStatus(status, iterations);
258
259 // If requested, we store the scheduler for retrieval.
260 if (this->isTrackSchedulersSet() && !(providedPlayer1Choices && providedPlayer2Choices)) {
261 this->player1SchedulerChoices = std::move(*player1Choices);
262 this->player2SchedulerChoices = std::move(*player2Choices);
263 }
264
265 if (!this->isCachingEnabled()) {
266 clearCache();
267 }
268
269 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
270}
271
272template<typename ValueType>
273bool StandardGameSolver<ValueType>::valueImproved(OptimizationDirection dir, storm::utility::ConstantsComparator<ValueType> const& comparator,
274 ValueType const& value1, ValueType const& value2) const {
275 if (dir == OptimizationDirection::Minimize) {
276 return comparator.isLess(value2, value1);
277 } else {
278 return comparator.isLess(value1, value2);
279 }
280}
281
282template<typename ValueType>
283bool StandardGameSolver<ValueType>::solveGameValueIteration(Environment const& env, OptimizationDirection player1Dir, OptimizationDirection player2Dir,
284 std::vector<ValueType>& x, std::vector<ValueType> const& b, std::vector<uint64_t>* player1Choices,
285 std::vector<uint64_t>* player2Choices) const {
286 if (!multiplierPlayer2Matrix) {
287 multiplierPlayer2Matrix = storm::solver::MultiplierFactory<ValueType>().create(env, player2Matrix);
288 }
289
290 if (!auxiliaryP2RowGroupVector) {
291 auxiliaryP2RowGroupVector = std::make_unique<std::vector<ValueType>>(player2Matrix.getRowGroupCount());
292 }
293
294 if (!auxiliaryP1RowGroupVector) {
295 auxiliaryP1RowGroupVector = std::make_unique<std::vector<ValueType>>(this->getNumberOfPlayer1States());
296 }
297
298 ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().game().getPrecision());
299 bool relative = env.solver().game().getRelativeTerminationCriterion();
300 uint64_t maxIter = env.solver().game().getMaximalNumberOfIterations();
301
302 std::vector<ValueType>& reducedPlayer2Result = *auxiliaryP2RowGroupVector;
303
304 bool trackingSchedulersInProvidedStorage = player1Choices && player2Choices;
305 bool trackSchedulers = this->isTrackSchedulersSet() || trackingSchedulersInProvidedStorage;
306 bool trackSchedulersInValueIteration = trackSchedulers && !this->hasUniqueSolution();
307 if (this->hasSchedulerHints()) {
308 // Solve the equation system induced by the two schedulers.
309 storm::storage::SparseMatrix<ValueType> submatrix;
310 getInducedMatrixVector(x, b, this->player1ChoicesHint.get(), this->player2ChoicesHint.get(), submatrix, *auxiliaryP1RowGroupVector);
311 if (this->linearEquationSolverFactory->getEquationProblemFormat(env) == LinearEquationSolverProblemFormat::EquationSystem) {
312 submatrix.convertToEquationSystem();
313 }
314 auto submatrixSolver = linearEquationSolverFactory->create(env, std::move(submatrix));
315 if (this->lowerBound) {
316 submatrixSolver->setLowerBound(this->lowerBound.get());
317 }
318 if (this->upperBound) {
319 submatrixSolver->setUpperBound(this->upperBound.get());
320 }
321 submatrixSolver->solveEquations(env, x, *auxiliaryP1RowGroupVector);
322
323 // If requested, we store the scheduler for retrieval. Initialize the schedulers to the hint we have.
324 if (trackSchedulersInValueIteration && !trackingSchedulersInProvidedStorage) {
325 this->player1SchedulerChoices = this->player1ChoicesHint.get();
326 this->player2SchedulerChoices = this->player2ChoicesHint.get();
327 }
328 } else if (trackSchedulersInValueIteration && !trackingSchedulersInProvidedStorage) {
329 // If requested, we store the scheduler for retrieval. Create empty schedulers here so we can fill them
330 // during VI.
331 this->player1SchedulerChoices = std::vector<uint_fast64_t>(this->getNumberOfPlayer1States(), 0);
332 this->player2SchedulerChoices = std::vector<uint_fast64_t>(this->getNumberOfPlayer2States(), 0);
333 }
334
335 std::vector<ValueType>* newX = auxiliaryP1RowGroupVector.get();
336 std::vector<ValueType>* currentX = &x;
337
338 // Proceed with the iterations as long as the method did not converge or reach the maximum number of iterations.
339 uint64_t iterations = 0;
340
342 while (status == SolverStatus::InProgress) {
343 multiplyAndReduce(
344 env, player1Dir, player2Dir, *currentX, &b, *multiplierPlayer2Matrix, reducedPlayer2Result, *newX,
345 trackSchedulersInValueIteration ? (trackingSchedulersInProvidedStorage ? player1Choices : &this->player1SchedulerChoices.get()) : nullptr,
346 trackSchedulersInValueIteration ? (trackingSchedulersInProvidedStorage ? player2Choices : &this->player2SchedulerChoices.get()) : nullptr);
347
348 // Determine whether the method converged.
349 if (storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *newX, precision, relative)) {
351 }
352
353 // Update environment variables.
354 std::swap(currentX, newX);
355 ++iterations;
356 status = this->updateStatus(status, *currentX, SolverGuarantee::None, iterations, maxIter);
357 }
358
359 this->reportStatus(status, iterations);
360
361 // If we performed an odd number of iterations, we need to swap the x and currentX, because the newest result
362 // is currently stored in currentX, but x is the output vector.
363 if (currentX == auxiliaryP1RowGroupVector.get()) {
364 std::swap(x, *currentX);
365 }
366
367 // If requested, we store the scheduler for retrieval.
368 if (trackSchedulers && this->hasUniqueSolution()) {
369 if (trackingSchedulersInProvidedStorage) {
370 extractChoices(env, player1Dir, player2Dir, x, b, *auxiliaryP2RowGroupVector, *player1Choices, *player2Choices);
371 } else {
372 this->player1SchedulerChoices = std::vector<uint_fast64_t>(this->getNumberOfPlayer1States(), 0);
373 this->player2SchedulerChoices = std::vector<uint_fast64_t>(this->getNumberOfPlayer2States(), 0);
374 extractChoices(env, player1Dir, player2Dir, x, b, *auxiliaryP2RowGroupVector, this->player1SchedulerChoices.get(),
375 this->player2SchedulerChoices.get());
376 }
377 }
378
379 if (!this->isCachingEnabled()) {
380 clearCache();
381 }
382
383 return (status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly);
384}
385
386template<typename ValueType>
388 std::vector<ValueType>& x, std::vector<ValueType> const* b, uint_fast64_t n) const {
389 if (!multiplierPlayer2Matrix) {
390 multiplierPlayer2Matrix = storm::solver::MultiplierFactory<ValueType>().create(env, player2Matrix);
391 }
392
393 if (!auxiliaryP2RowGroupVector) {
394 auxiliaryP2RowGroupVector = std::make_unique<std::vector<ValueType>>(player2Matrix.getRowGroupCount());
395 }
396 std::vector<ValueType>& reducedPlayer2Result = *auxiliaryP2RowGroupVector;
397
398 for (uint_fast64_t iteration = 0; iteration < n; ++iteration) {
399 multiplyAndReduce(env, player1Dir, player2Dir, x, b, *multiplierPlayer2Matrix, reducedPlayer2Result, x);
400 }
401
402 if (!this->isCachingEnabled()) {
403 clearCache();
404 }
405}
406
407template<typename ValueType>
408void StandardGameSolver<ValueType>::multiplyAndReduce(Environment const& env, OptimizationDirection player1Dir, OptimizationDirection player2Dir,
409 std::vector<ValueType>& x, std::vector<ValueType> const* b,
410 storm::solver::Multiplier<ValueType> const& multiplier, std::vector<ValueType>& player2ReducedResult,
411 std::vector<ValueType>& player1ReducedResult, std::vector<uint64_t>* player1SchedulerChoices,
412 std::vector<uint64_t>* player2SchedulerChoices) const {
413 multiplier.multiplyAndReduce(env, player2Dir, x, b, player2ReducedResult, UncertaintyResolutionMode::Unset, player2SchedulerChoices);
414
415 if (this->player1RepresentedByMatrix()) {
416 // Player 1 represented by matrix.
417 uint_fast64_t player1State = 0;
418 for (auto& result : player1ReducedResult) {
419 storm::storage::SparseMatrix<storm::storage::sparse::state_type>::const_rows relevantRows = this->getPlayer1Matrix().getRowGroup(player1State);
420 STORM_LOG_ASSERT(relevantRows.getNumberOfEntries() != 0, "There is a choice of player 1 that does not lead to any player 2 choice.");
421 auto it = relevantRows.begin();
422 auto ite = relevantRows.end();
423
424 // Set the first value.
425 result = player2ReducedResult[it->getColumn()];
426 ++it;
427
428 // Now iterate through the different values and pick the extremal one.
429 if (player1Dir == OptimizationDirection::Minimize) {
430 for (; it != ite; ++it) {
431 result = std::min(result, player2ReducedResult[it->getColumn()]);
432 }
433 } else {
434 for (; it != ite; ++it) {
435 result = std::max(result, player2ReducedResult[it->getColumn()]);
436 }
437 }
438 ++player1State;
439 }
440 } else {
441 // Player 1 represented by grouping of player 2 states (vector).
442 storm::utility::vector::reduceVectorMinOrMax(player1Dir, player2ReducedResult, player1ReducedResult, this->getPlayer1Grouping(),
443 player1SchedulerChoices);
444 }
445}
446
447template<typename ValueType>
448bool StandardGameSolver<ValueType>::extractChoices(Environment const& env, OptimizationDirection player1Dir, OptimizationDirection player2Dir,
449 std::vector<ValueType> const& x, std::vector<ValueType> const& b,
450 std::vector<ValueType>& player2ChoiceValues, std::vector<uint_fast64_t>& player1Choices,
451 std::vector<uint_fast64_t>& player2Choices) const {
452 storm::utility::ConstantsComparator<ValueType> comparator(
455 : storm::utility::convertNumber<ValueType>(env.solver().getPrecisionOfLinearEquationSolver(env.solver().getLinearEquationSolverType()).first.get()),
456 false);
457
458 // get the choices of player 2 and the corresponding values.
459 bool schedulerImproved = false;
460 auto currentValueIt = player2ChoiceValues.begin();
461 for (uint_fast64_t p2Group = 0; p2Group < this->player2Matrix.getRowGroupCount(); ++p2Group, ++currentValueIt) {
462 uint_fast64_t firstRowInGroup = this->player2Matrix.getRowGroupIndices()[p2Group];
463 uint_fast64_t rowGroupSize = this->player2Matrix.getRowGroupIndices()[p2Group + 1] - firstRowInGroup;
464
465 // We need to check whether the scheduler improved. Therefore, we first have to evaluate the current choice.
466 uint_fast64_t currentP2Choice = player2Choices[p2Group];
467 *currentValueIt = storm::utility::zero<ValueType>();
468 for (auto const& entry : this->player2Matrix.getRow(firstRowInGroup + currentP2Choice)) {
469 *currentValueIt += entry.getValue() * x[entry.getColumn()];
470 }
471 *currentValueIt += b[firstRowInGroup + currentP2Choice];
472
473 // Now check other choices improve the value.
474 for (uint_fast64_t p2Choice = 0; p2Choice < rowGroupSize; ++p2Choice) {
475 if (p2Choice == currentP2Choice) {
476 continue;
477 }
479 for (auto const& entry : this->player2Matrix.getRow(firstRowInGroup + p2Choice)) {
480 choiceValue += entry.getValue() * x[entry.getColumn()];
481 }
482 choiceValue += b[firstRowInGroup + p2Choice];
483
484 if (valueImproved(player2Dir, comparator, *currentValueIt, choiceValue)) {
485 schedulerImproved = true;
486 player2Choices[p2Group] = p2Choice;
487 *currentValueIt = std::move(choiceValue);
488 }
489 }
490 }
491
492 // Now extract the choices of player 1.
493 if (this->player1RepresentedByMatrix()) {
494 // Player 1 represented by matrix.
495 for (uint_fast64_t p1Group = 0; p1Group < this->getPlayer1Matrix().getRowGroupCount(); ++p1Group) {
496 uint_fast64_t firstRowInGroup = this->getPlayer1Matrix().getRowGroupIndices()[p1Group];
497 uint_fast64_t rowGroupSize = this->getPlayer1Matrix().getRowGroupIndices()[p1Group + 1] - firstRowInGroup;
498 uint_fast64_t currentChoice = player1Choices[p1Group];
499 ValueType currentValue = player2ChoiceValues[this->getPlayer1Matrix().getRow(firstRowInGroup + currentChoice).begin()->getColumn()];
500 for (uint_fast64_t p1Choice = 0; p1Choice < rowGroupSize; ++p1Choice) {
501 // If the choice is the currently selected one, we can skip it.
502 if (p1Choice == currentChoice) {
503 continue;
504 }
505 ValueType const& choiceValue = player2ChoiceValues[this->getPlayer1Matrix().getRow(firstRowInGroup + p1Choice).begin()->getColumn()];
506 if (valueImproved(player1Dir, comparator, currentValue, choiceValue)) {
507 schedulerImproved = true;
508 player1Choices[p1Group] = p1Choice;
509 currentValue = choiceValue;
510 }
511 }
512 }
513 } else {
514 // Player 1 represented by grouping of player 2 states (vector).
515 for (uint64_t player1State = 0; player1State < this->getPlayer1Grouping().size() - 1; ++player1State) {
516 uint64_t currentChoice = player1Choices[player1State];
517 ValueType currentValue = player2ChoiceValues[this->getPlayer1Grouping()[player1State] + currentChoice];
518 uint64_t numberOfPlayer2Successors = this->getPlayer1Grouping()[player1State + 1] - this->getPlayer1Grouping()[player1State];
519 for (uint64_t player2State = 0; player2State < numberOfPlayer2Successors; ++player2State) {
520 // If the choice is the currently selected one, we can skip it.
521 if (currentChoice == player2State) {
522 continue;
523 }
524
525 ValueType const& choiceValue = player2ChoiceValues[this->getPlayer1Grouping()[player1State] + player2State];
526 if (valueImproved(player1Dir, comparator, currentValue, choiceValue)) {
527 schedulerImproved = true;
528 player1Choices[player1State] = player2State;
529 currentValue = choiceValue;
530 }
531 }
532 }
533 }
534
535 return schedulerImproved;
536}
537
538template<typename ValueType>
539void StandardGameSolver<ValueType>::getInducedMatrixVector(std::vector<ValueType>&, std::vector<ValueType> const& b,
540 std::vector<uint_fast64_t> const& player1Choices, std::vector<uint_fast64_t> const& player2Choices,
541 storm::storage::SparseMatrix<ValueType>& inducedMatrix,
542 std::vector<ValueType>& inducedVector) const {
543 // Get the rows of the player 2 matrix that are selected by the schedulers.
544 // Note that rows can be selected more than once and in an arbitrary order.
545 std::vector<storm::storage::sparse::state_type> selectedRows;
546 if (this->player1RepresentedByMatrix()) {
547 // Player 1 is represented by a matrix.
548 selectedRows.reserve(this->getPlayer1Matrix().getRowGroupCount());
549 uint_fast64_t player1State = 0;
550 for (auto const& player1Choice : player1Choices) {
551 auto const& player1Row = this->getPlayer1Matrix().getRow(player1State, player1Choice);
552 STORM_LOG_ASSERT(player1Row.getNumberOfEntries() == 1, "It is assumed that rows of player one have one entry, but this is not the case.");
553 uint_fast64_t player2State = player1Row.begin()->getColumn();
554 selectedRows.push_back(player2Matrix.getRowGroupIndices()[player2State] + player2Choices[player2State]);
555 ++player1State;
556 }
557 } else {
558 // Player 1 is represented by the grouping of player 2 states (vector).
559 selectedRows.reserve(this->player2Matrix.getRowGroupCount());
560 for (uint64_t player1State = 0; player1State < this->getPlayer1Grouping().size() - 1; ++player1State) {
561 uint64_t player2State = this->getPlayer1Grouping()[player1State] + player1Choices[player1State];
562 selectedRows.emplace_back(player2Matrix.getRowGroupIndices()[player2State] + player2Choices[player2State]);
563 }
564 }
565
566 // Get the matrix and the vector induced by this selection and add entries on the diagonal in the process.
567 inducedMatrix = player2Matrix.selectRowsFromRowIndexSequence(selectedRows, true);
568 inducedVector.resize(inducedMatrix.getRowCount());
569 storm::utility::vector::selectVectorValues<ValueType>(inducedVector, selectedRows, b);
570}
571
572template<typename ValueType>
573bool StandardGameSolver<ValueType>::player1RepresentedByMatrix() const {
574 return player1Matrix != nullptr;
575}
576
577template<typename ValueType>
578storm::storage::SparseMatrix<storm::storage::sparse::state_type> const& StandardGameSolver<ValueType>::getPlayer1Matrix() const {
579 STORM_LOG_ASSERT(player1RepresentedByMatrix(), "Player 1 is represented by a matrix.");
580 return *player1Matrix;
581}
582
583template<typename ValueType>
584std::vector<uint64_t> const& StandardGameSolver<ValueType>::getPlayer1Grouping() const {
585 STORM_LOG_ASSERT(!player1RepresentedByMatrix(), "Player 1 is represented by a matrix.");
586 return *player1Grouping;
587}
588
589template<typename ValueType>
590uint64_t StandardGameSolver<ValueType>::getNumberOfPlayer1States() const {
591 if (this->player1RepresentedByMatrix()) {
592 return this->getPlayer1Matrix().getRowGroupCount();
593 } else {
594 return this->getPlayer1Grouping().size() - 1;
595 }
596}
597
598template<typename ValueType>
599uint64_t StandardGameSolver<ValueType>::getNumberOfPlayer2States() const {
600 return this->player2Matrix.getRowGroupCount();
601}
602
603template<typename ValueType>
605 multiplierPlayer2Matrix.reset();
606 auxiliaryP2RowVector.reset();
607 auxiliaryP2RowGroupVector.reset();
608 auxiliaryP1RowGroupVector.reset();
610}
611
612template class StandardGameSolver<double>;
614} // namespace solver
615} // namespace storm
SolverEnvironment & solver()
storm::RationalNumber const & getPrecision() const
uint64_t const & getMaximalNumberOfIterations() const
bool const & isMethodSetFromDefault() const
bool const & getRelativeTerminationCriterion() const
storm::solver::GameMethod const & getMethod() const
storm::solver::EquationSolverType const & getLinearEquationSolverType() const
GameSolverEnvironment & game()
std::pair< boost::optional< storm::RationalNumber >, boost::optional< bool > > getPrecisionOfLinearEquationSolver(storm::solver::EquationSolverType const &solverType) const
virtual void clearCache() const
bool isCachingEnabled() const
Retrieves whether some of the generated data during solver calls should be cached.
std::unique_ptr< Multiplier< ValueType, SolutionType > > create(Environment const &env, storm::storage::SparseMatrix< ValueType > const &matrix)
void multiplyAndReduce(Environment const &env, OptimizationDirection const &dir, std::vector< SolutionType > const &x, std::vector< ValueType > const *b, std::vector< SolutionType > &result, UncertaintyResolutionMode const &uncertaintyResolutionMode=UncertaintyResolutionMode::Unset, std::vector< uint_fast64_t > *choices=nullptr) const
Performs a matrix-vector multiplication x' = A*x + b and then minimizes/maximizes over the row groups...
virtual void clearCache() const override
virtual bool solveGame(Environment const &env, OptimizationDirection player1Dir, OptimizationDirection player2Dir, std::vector< ValueType > &x, std::vector< ValueType > const &b, std::vector< uint64_t > *player1Choices=nullptr, std::vector< uint64_t > *player2Choices=nullptr) const override
Solves the equation system defined by the game matrices.
virtual void repeatedMultiply(Environment const &env, OptimizationDirection player1Dir, OptimizationDirection player2Dir, std::vector< ValueType > &x, std::vector< ValueType > const *b, uint_fast64_t n=1) const override
Performs (repeated) matrix-vector multiplication with the given parameters, i.e.
StandardGameSolver(storm::storage::SparseMatrix< storm::storage::sparse::state_type > const &player1Matrix, storm::storage::SparseMatrix< ValueType > const &player2Matrix, std::unique_ptr< LinearEquationSolverFactory< ValueType > > &&linearEquationSolverFactory)
size_t size() const
Retrieves the number of bits this bit vector can store.
This class represents a number of consecutive rows of the matrix.
const_iterator end() const
Retrieves an iterator that points past the last entry of the rows.
index_type getNumberOfEntries() const
Retrieves the number of entries in the rows.
const_iterator begin() const
Retrieves an iterator that points to the beginning of the rows.
A class that holds a possibly non-square matrix in the compressed row storage format.
void convertToEquationSystem()
Transforms the matrix into an equation system.
const_rows getRow(index_type row) const
Returns an object representing the given row.
SparseMatrix selectRowsFromRowIndexSequence(std::vector< index_type > const &rowIndexSequence, bool insertDiagonalEntries=true) const
Selects the rows that are given by the sequence of row indices, allowing to select rows arbitrarily o...
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
bool isLess(ValueType const &value1, ValueType const &value2) const
#define STORM_LOG_INFO(message)
Definition logging.h:34
#define STORM_LOG_WARN(message)
Definition logging.h:35
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
SFTBDDChecker::ValueType ValueType
std::string toString(GurobiSolverMethod const &method)
Yields a string representation of the GurobiSolverMethod.
storm::storage::BitVector performProbGreater0(storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, bool useStepBound, uint_fast64_t maximalSteps)
Performs a backward depth-first search trough the underlying graph structure of the given model to de...
Definition graph.cpp:316
bool equalModuloPrecision(T const &val1, T const &val2, T const &precision, bool relativeError=true)
Compares the given elements and determines whether they are equal modulo the given precision.
Definition vector.h:732
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:185
void reduceVectorMinOrMax(storm::solver::OptimizationDirection dir, std::vector< T > const &source, std::vector< T > &target, std::vector< uint_fast64_t > const &rowGrouping, std::vector< uint_fast64_t > *choices=nullptr)
Reduces the given source vector by selecting either the smallest or the largest out of each row group...
Definition vector.h:712
storm::storage::BitVector filterGreaterZero(std::vector< T > const &values)
Retrieves a bit vector containing all the indices for which the value at this position is greater tha...
Definition vector.h:509
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
TargetType convertNumber(SourceType const &number)
static const bool IsExact