#include "src/solver/SymbolicGameSolver.h" #include "src/storage/dd/DdManager.h" #include "src/storage/dd/Bdd.h" #include "src/storage/dd/Add.h" #include "src/settings/SettingsManager.h" #include "src/settings/modules/NativeEquationSolverSettings.h" #include "src/utility/constants.h" #include "src/utility/macros.h" #include "src/exceptions/IllegalFunctionCallException.h" namespace storm { namespace solver { template SymbolicGameSolver::SymbolicGameSolver(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalPlayer1Mask, storm::dd::Bdd const& illegalPlayer2Mask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::vector> const& rowColumnMetaVariablePairs, std::set const& player1Variables, std::set const& player2Variables) : A(A), allRows(allRows), illegalPlayer1Mask(illegalPlayer1Mask.ite(A.getDdManager().getConstant(storm::utility::infinity()), A.getDdManager().template getAddZero())), illegalPlayer2Mask(illegalPlayer2Mask.ite(A.getDdManager().getConstant(storm::utility::infinity()), A.getDdManager().template getAddZero())), rowMetaVariables(rowMetaVariables), columnMetaVariables(columnMetaVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), player1Variables(player1Variables), player2Variables(player2Variables), generatePlayer1Strategy(false), generatePlayer2Strategy(false) { // Intentionally left empty. } template SymbolicGameSolver::SymbolicGameSolver(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalPlayer1Mask, storm::dd::Bdd const& illegalPlayer2Mask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::vector> const& rowColumnMetaVariablePairs, std::set const& player1Variables, std::set const& player2Variables, double precision, uint_fast64_t maximalNumberOfIterations, bool relative) : AbstractGameSolver(precision, maximalNumberOfIterations, relative), A(A), allRows(allRows), illegalPlayer1Mask(illegalPlayer1Mask.ite(A.getDdManager().getConstant(storm::utility::infinity()), A.getDdManager().template getAddZero())), illegalPlayer2Mask(illegalPlayer2Mask.ite(A.getDdManager().getConstant(storm::utility::infinity()), A.getDdManager().template getAddZero())), rowMetaVariables(rowMetaVariables), columnMetaVariables(columnMetaVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), player1Variables(player1Variables), player2Variables(player2Variables), generatePlayer1Strategy(false), generatePlayer2Strategy(false) { // Intentionally left empty. } template storm::dd::Add SymbolicGameSolver::solveGame(OptimizationDirection player1Goal, OptimizationDirection player2Goal, storm::dd::Add const& x, storm::dd::Add const& b, boost::optional> const& basePlayer1Strategy, boost::optional> const& basePlayer2Strategy) { // Set up the environment. storm::dd::Add xCopy = x; uint_fast64_t iterations = 0; bool converged = false; // Prepare some data storage in case we need to generate strategies. if (generatePlayer1Strategy) { if (basePlayer1Strategy) { player1Strategy = basePlayer1Strategy.get(); } else { player1Strategy = A.getDdManager().getBddZero(); } } boost::optional> previousPlayer2Values; if (generatePlayer2Strategy) { if (basePlayer2Strategy) { player2Strategy = basePlayer2Strategy.get(); // If we are required to generate a player 2 strategy based on another one that is not the zero strategy, // we need to determine the values, because only then we can update the strategy only if necessary. previousPlayer2Values = ((this->A * player2Strategy.get().template toAdd()).multiplyMatrix(x.swapVariables(this->rowColumnMetaVariablePairs), this->columnMetaVariables) + b).sumAbstract(this->player2Variables); } else { player2Strategy = A.getDdManager().getBddZero(); previousPlayer2Values = A.getDdManager().template getAddZero(); } } do { // Compute tmp = A * x + b. storm::dd::Add xCopyAsColumn = xCopy.swapVariables(this->rowColumnMetaVariablePairs); storm::dd::Add tmp = this->A.multiplyMatrix(xCopyAsColumn, this->columnMetaVariables); tmp += b; // Now abstract from player 2 and player 1 variables. if (player2Goal == storm::OptimizationDirection::Maximize) { storm::dd::Add newValues = tmp.maxAbstract(this->player2Variables); if (generatePlayer2Strategy) { // Update only the choices that strictly improved the value. storm::dd::Bdd maxChoices = tmp.maxAbstractRepresentative(this->player2Variables); player2Strategy.get() = newValues.greater(previousPlayer2Values.get()).ite(maxChoices, player2Strategy.get()); previousPlayer2Values = newValues; } tmp = newValues; } else { tmp = (tmp + illegalPlayer2Mask); storm::dd::Add newValues = tmp.minAbstract(this->player2Variables); if (generatePlayer2Strategy) { player2Strategy = tmp.minAbstractRepresentative(this->player2Variables); } tmp = newValues; } if (player1Goal == storm::OptimizationDirection::Maximize) { storm::dd::Add newValues = tmp.maxAbstract(this->player1Variables); if (generatePlayer1Strategy) { // Update only the choices that strictly improved the value. storm::dd::Bdd maxChoices = tmp.maxAbstractRepresentative(this->player1Variables); player1Strategy = newValues.greater(xCopy).ite(maxChoices, player1Strategy.get()); } tmp = newValues; } else { tmp = (tmp + illegalPlayer1Mask); storm::dd::Add newValues = tmp.minAbstract(this->player1Variables); if (generatePlayer1Strategy) { player1Strategy = tmp.minAbstractRepresentative(this->player1Variables); } tmp = newValues; } // Now check if the process already converged within our precision. converged = xCopy.equalModuloPrecision(tmp, precision, relative); // If the method did not converge yet, we prepare the x vector for the next iteration. if (!converged) { xCopy = tmp; } ++iterations; } while (!converged && iterations < maximalNumberOfIterations); return xCopy; } template void SymbolicGameSolver::setGeneratePlayer1Strategy(bool value) { generatePlayer1Strategy = value; } template void SymbolicGameSolver::setGeneratePlayer2Strategy(bool value) { generatePlayer2Strategy = value; } template void SymbolicGameSolver::setGeneratePlayersStrategies(bool value) { setGeneratePlayer1Strategy(value); setGeneratePlayer2Strategy(value); } template storm::dd::Bdd const& SymbolicGameSolver::getPlayer1Strategy() const { STORM_LOG_THROW(player1Strategy, storm::exceptions::IllegalFunctionCallException, "Cannot retrieve player 1 strategy because none was generated."); return player1Strategy.get(); } template storm::dd::Bdd const& SymbolicGameSolver::getPlayer2Strategy() const { STORM_LOG_THROW(player2Strategy, storm::exceptions::IllegalFunctionCallException, "Cannot retrieve player 2 strategy because none was generated."); return player2Strategy.get(); } template class SymbolicGameSolver; template class SymbolicGameSolver; } }