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