diff --git a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp index 5447d7fbd..e89759f81 100644 --- a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp @@ -355,7 +355,7 @@ namespace storm { bool requireInitialScheduler = false; if (!requirements.empty()) { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { - STORM_LOG_DEBUG("Computing valid scheduler hint, because the solver requires it."); + STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); requireInitialScheduler = true; requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); } diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index fb70009a3..3c213b1cf 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -205,7 +205,7 @@ namespace storm { storm::solver::MinMaxLinearEquationSolverRequirements requirements = minMaxLinearEquationSolverFactory.getRequirements(type, dir); if (!(hint.isExplicitModelCheckerHint() && hint.asExplicitModelCheckerHint().getNoEndComponentsInMaybeStates()) && !requirements.empty()) { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { - STORM_LOG_DEBUG("Computing valid scheduler hint, because the solver requires it."); + STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); result.schedulerHint = computeValidSchedulerHint(type, transitionMatrix, backwardTransitions, maybeStates, phiStates, targetStates); requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); } diff --git a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp index 74a52cf90..c53724306 100644 --- a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp @@ -9,7 +9,6 @@ #include "storm/utility/graph.h" #include "storm/utility/constants.h" - #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" @@ -17,11 +16,24 @@ #include "storm/exceptions/InvalidPropertyException.h" #include "storm/exceptions/InvalidArgumentException.h" +#include "storm/exceptions/UncheckedRequirementException.h" namespace storm { namespace modelchecker { namespace helper { + template + storm::dd::Bdd computeValidSchedulerHint(storm::solver::MinMaxLinearEquationSolverSystemType const& type, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& maybeStates, storm::dd::Bdd const& targetStates) { + + storm::dd::Bdd result; + + if (type == storm::solver::MinMaxLinearEquationSolverSystemType::UntilProbabilities) { + result = storm::utility::graph::computeSchedulerProbGreater0E(model, transitionMatrix.notZero(), maybeStates, targetStates); + } + + return result; + } + template std::unique_ptr SymbolicMdpPrctlHelper::computeUntilProbabilities(OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, bool qualitative, storm::solver::SymbolicGeneralMinMaxLinearEquationSolverFactory const& linearEquationSolverFactory) { // We need to identify the states which have to be taken out of the matrix, i.e. all states that have @@ -66,7 +78,24 @@ namespace storm { // Now solve the resulting equation system. std::unique_ptr> solver = linearEquationSolverFactory.create(submatrix, maybeStates, model.getIllegalMask() && maybeStates, model.getRowVariables(), model.getColumnVariables(), model.getNondeterminismVariables(), model.getRowColumnMetaVariablePairs()); - storm::dd::Add result = solver->solveEquations(dir == OptimizationDirection::Minimize, model.getManager().template getAddZero(), subvector); + + // Check requirements of solver. + storm::solver::MinMaxLinearEquationSolverRequirements requirements = solver->getRequirements(storm::solver::MinMaxLinearEquationSolverSystemType::UntilProbabilities, dir); + boost::optional> initialScheduler; + if (!requirements.empty()) { + if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { + STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); + initialScheduler = computeValidSchedulerHint(storm::solver::MinMaxLinearEquationSolverSystemType::UntilProbabilities, model, transitionMatrix, maybeStates, statesWithProbability01.second); + requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); + } + STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Could not establish requirements of solver."); + } + if (initialScheduler) { + solver->setInitialScheduler(initialScheduler.get()); + } + solver->setRequirementsChecked(); + + storm::dd::Add result = solver->solveEquations(dir, model.getManager().template getAddZero(), subvector); return std::unique_ptr(new storm::modelchecker::SymbolicQuantitativeCheckResult(model.getReachableStates(), statesWithProbability01.second.template toAdd() + result)); } else { @@ -123,7 +152,7 @@ namespace storm { submatrix *= maybeStatesAdd.swapVariables(model.getRowColumnMetaVariablePairs()); std::unique_ptr> solver = linearEquationSolverFactory.create(submatrix, maybeStates, model.getIllegalMask() && maybeStates, model.getRowVariables(), model.getColumnVariables(), model.getNondeterminismVariables(), model.getRowColumnMetaVariablePairs()); - storm::dd::Add result = solver->multiply(dir == OptimizationDirection::Minimize, model.getManager().template getAddZero(), &subvector, stepBound); + storm::dd::Add result = solver->multiply(dir, model.getManager().template getAddZero(), &subvector, stepBound); return std::unique_ptr(new storm::modelchecker::SymbolicQuantitativeCheckResult(model.getReachableStates(), psiStates.template toAdd() + result)); } else { @@ -138,7 +167,7 @@ namespace storm { // Perform the matrix-vector multiplication. std::unique_ptr> solver = linearEquationSolverFactory.create(transitionMatrix, model.getReachableStates(), model.getIllegalMask(), model.getRowVariables(), model.getColumnVariables(), model.getNondeterminismVariables(), model.getRowColumnMetaVariablePairs()); - storm::dd::Add result = solver->multiply(dir == OptimizationDirection::Minimize, rewardModel.getStateRewardVector(), nullptr, stepBound); + storm::dd::Add result = solver->multiply(dir, rewardModel.getStateRewardVector(), nullptr, stepBound); return std::unique_ptr(new SymbolicQuantitativeCheckResult(model.getReachableStates(), result)); } @@ -153,7 +182,7 @@ namespace storm { // Perform the matrix-vector multiplication. std::unique_ptr> solver = linearEquationSolverFactory.create(model.getTransitionMatrix(), model.getReachableStates(), model.getIllegalMask(), model.getRowVariables(), model.getColumnVariables(), model.getNondeterminismVariables(), model.getRowColumnMetaVariablePairs()); - storm::dd::Add result = solver->multiply(dir == OptimizationDirection::Minimize, model.getManager().template getAddZero(), &totalRewardVector, stepBound); + storm::dd::Add result = solver->multiply(dir, model.getManager().template getAddZero(), &totalRewardVector, stepBound); return std::unique_ptr(new SymbolicQuantitativeCheckResult(model.getReachableStates(), result)); } @@ -208,7 +237,13 @@ namespace storm { // Now solve the resulting equation system. std::unique_ptr> solver = linearEquationSolverFactory.create(submatrix, maybeStates, model.getIllegalMask() && maybeStates, model.getRowVariables(), model.getColumnVariables(), model.getNondeterminismVariables(), model.getRowColumnMetaVariablePairs()); - storm::dd::Add result = solver->solveEquations(dir == OptimizationDirection::Minimize, model.getManager().template getAddZero(), subvector); + + // Check requirements of solver. + storm::solver::MinMaxLinearEquationSolverRequirements requirements = solver->getRequirements(storm::solver::MinMaxLinearEquationSolverSystemType::ReachabilityRewards, dir); + STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Could not establish requirements of solver."); + solver->setRequirementsChecked(); + + storm::dd::Add result = solver->solveEquations(dir, model.getManager().template getAddZero(), subvector); return std::unique_ptr(new storm::modelchecker::SymbolicQuantitativeCheckResult(model.getReachableStates(), infinityStates.ite(model.getManager().getConstant(storm::utility::infinity()), result))); } else { diff --git a/src/storm/solver/MinMaxLinearEquationSolver.h b/src/storm/solver/MinMaxLinearEquationSolver.h index 579790412..c77417ccd 100644 --- a/src/storm/solver/MinMaxLinearEquationSolver.h +++ b/src/storm/solver/MinMaxLinearEquationSolver.h @@ -12,6 +12,7 @@ #include "storm/storage/sparse/StateType.h" #include "storm/storage/Scheduler.h" #include "storm/solver/OptimizationDirection.h" +#include "storm/solver/MinMaxLinearEquationSolverSystemType.h" #include "storm/solver/MinMaxLinearEquationSolverRequirements.h" #include "storm/exceptions/InvalidSettingsException.h" @@ -24,12 +25,6 @@ namespace storm { namespace solver { - enum class MinMaxLinearEquationSolverSystemType { - UntilProbabilities, - ReachabilityRewards, - StochasticShortestPath - }; - /*! * A class representing the interface that all min-max linear equation solvers shall implement. */ diff --git a/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp b/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp new file mode 100644 index 000000000..07560601d --- /dev/null +++ b/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp @@ -0,0 +1,61 @@ +#include "storm/solver/MinMaxLinearEquationSolverRequirements.h" + +namespace storm { + namespace solver { + + MinMaxLinearEquationSolverRequirements::MinMaxLinearEquationSolverRequirements() : noEndComponents(false), noZeroRewardEndComponents(false), validInitialScheduler(false), globalLowerBound(false), globalUpperBound(false) { + // Intentionally left empty. + } + + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setNoEndComponents(bool value) { + noEndComponents = value; + return *this; + } + + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setNoZeroRewardEndComponents(bool value) { + noZeroRewardEndComponents = value; + return *this; + } + + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setValidInitialScheduler(bool value) { + validInitialScheduler = value; + return *this; + } + + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setGlobalLowerBound(bool value) { + globalLowerBound = value; + return *this; + } + + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setGlobalUpperBound(bool value) { + globalUpperBound = value; + return *this; + } + + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::set(Element const& element, bool value) { + switch (element) { + case Element::NoEndComponents: noEndComponents = value; break; + case Element::NoZeroRewardEndComponents: noZeroRewardEndComponents = value; break; + case Element::ValidInitialScheduler: validInitialScheduler = value; break; + case Element::GlobalLowerBound: globalLowerBound = value; break; + case Element::GlobalUpperBound: globalUpperBound = value; break; + } + return *this; + } + + bool MinMaxLinearEquationSolverRequirements::requires(Element const& element) { + switch (element) { + case Element::NoEndComponents: return noEndComponents; break; + case Element::NoZeroRewardEndComponents: return noZeroRewardEndComponents; break; + case Element::ValidInitialScheduler: return validInitialScheduler; break; + case Element::GlobalLowerBound: return globalLowerBound; break; + case Element::GlobalUpperBound: return globalUpperBound; break; + } + } + + bool MinMaxLinearEquationSolverRequirements::empty() const { + return !noEndComponents && !noZeroRewardEndComponents && !validInitialScheduler && !globalLowerBound && !globalUpperBound; + } + + } +} diff --git a/src/storm/solver/MinMaxLinearEquationSolverRequirements.h b/src/storm/solver/MinMaxLinearEquationSolverRequirements.h index 906c0a193..6cb4e025c 100644 --- a/src/storm/solver/MinMaxLinearEquationSolverRequirements.h +++ b/src/storm/solver/MinMaxLinearEquationSolverRequirements.h @@ -9,59 +9,18 @@ namespace storm { NoEndComponents, NoZeroRewardEndComponents, ValidInitialScheduler, GlobalLowerBound, GlobalUpperBound }; - MinMaxLinearEquationSolverRequirements() : noEndComponents(false), noZeroRewardEndComponents(false), validInitialScheduler(false), globalLowerBound(false), globalUpperBound(false) { - // Intentionally left empty. - } + MinMaxLinearEquationSolverRequirements(); - MinMaxLinearEquationSolverRequirements& setNoEndComponents(bool value = true) { - noEndComponents = value; - return *this; - } + MinMaxLinearEquationSolverRequirements& setNoEndComponents(bool value = true); + MinMaxLinearEquationSolverRequirements& setNoZeroRewardEndComponents(bool value = true); + MinMaxLinearEquationSolverRequirements& setValidInitialScheduler(bool value = true); + MinMaxLinearEquationSolverRequirements& setGlobalLowerBound(bool value = true); + MinMaxLinearEquationSolverRequirements& setGlobalUpperBound(bool value = true); + MinMaxLinearEquationSolverRequirements& set(Element const& element, bool value = true); - MinMaxLinearEquationSolverRequirements& setNoZeroRewardEndComponents(bool value = true) { - noZeroRewardEndComponents = value; - return *this; - } + bool requires(Element const& element); - MinMaxLinearEquationSolverRequirements& setValidInitialScheduler(bool value = true) { - validInitialScheduler = value; - return *this; - } - - MinMaxLinearEquationSolverRequirements& setGlobalLowerBound(bool value = true) { - globalLowerBound = value; - return *this; - } - - MinMaxLinearEquationSolverRequirements& setGlobalUpperBound(bool value = true) { - globalUpperBound = value; - return *this; - } - - MinMaxLinearEquationSolverRequirements& set(Element const& element, bool value = true) { - switch (element) { - case Element::NoEndComponents: noEndComponents = value; break; - case Element::NoZeroRewardEndComponents: noZeroRewardEndComponents = value; break; - case Element::ValidInitialScheduler: validInitialScheduler = value; break; - case Element::GlobalLowerBound: globalLowerBound = value; break; - case Element::GlobalUpperBound: globalUpperBound = value; break; - } - return *this; - } - - bool requires(Element const& element) { - switch (element) { - case Element::NoEndComponents: return noEndComponents; break; - case Element::NoZeroRewardEndComponents: return noZeroRewardEndComponents; break; - case Element::ValidInitialScheduler: return validInitialScheduler; break; - case Element::GlobalLowerBound: return globalLowerBound; break; - case Element::GlobalUpperBound: return globalUpperBound; break; - } - } - - bool empty() const { - return !noEndComponents && !noZeroRewardEndComponents && !validInitialScheduler && !globalLowerBound && !globalUpperBound; - } + bool empty() const; private: bool noEndComponents; diff --git a/src/storm/solver/MinMaxLinearEquationSolverSystemType.h b/src/storm/solver/MinMaxLinearEquationSolverSystemType.h new file mode 100644 index 000000000..e517b7b8f --- /dev/null +++ b/src/storm/solver/MinMaxLinearEquationSolverSystemType.h @@ -0,0 +1,13 @@ +#pragma once + +namespace storm { + namespace solver { + + enum class MinMaxLinearEquationSolverSystemType { + UntilProbabilities, + ReachabilityRewards, + StochasticShortestPath + }; + + } +} diff --git a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp index 7499f166c..9dc9e7c93 100644 --- a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp @@ -78,24 +78,30 @@ namespace storm { } template - SymbolicMinMaxLinearEquationSolver::SymbolicMinMaxLinearEquationSolver(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalMask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& choiceVariables, std::vector> const& rowColumnMetaVariablePairs, std::unique_ptr>&& linearEquationSolverFactory, SymbolicMinMaxLinearEquationSolverSettings const& settings) : A(A), allRows(allRows), illegalMaskAdd(illegalMask.ite(A.getDdManager().getConstant(storm::utility::infinity()), A.getDdManager().template getAddZero())), rowMetaVariables(rowMetaVariables), columnMetaVariables(columnMetaVariables), choiceVariables(choiceVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), linearEquationSolverFactory(std::move(linearEquationSolverFactory)), settings(settings) { + SymbolicMinMaxLinearEquationSolver::SymbolicMinMaxLinearEquationSolver(SymbolicMinMaxLinearEquationSolverSettings const& settings) : settings(settings), requirementsChecked(false) { // Intentionally left empty. } template - storm::dd::Add SymbolicMinMaxLinearEquationSolver::solveEquations(bool minimize, storm::dd::Add const& x, storm::dd::Add const& b) const { + SymbolicMinMaxLinearEquationSolver::SymbolicMinMaxLinearEquationSolver(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalMask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& choiceVariables, std::vector> const& rowColumnMetaVariablePairs, std::unique_ptr>&& linearEquationSolverFactory, SymbolicMinMaxLinearEquationSolverSettings const& settings) : A(A), allRows(allRows), illegalMaskAdd(illegalMask.ite(A.getDdManager().getConstant(storm::utility::infinity()), A.getDdManager().template getAddZero())), rowMetaVariables(rowMetaVariables), columnMetaVariables(columnMetaVariables), choiceVariables(choiceVariables), rowColumnMetaVariablePairs(rowColumnMetaVariablePairs), linearEquationSolverFactory(std::move(linearEquationSolverFactory)), settings(settings), requirementsChecked(false) { + // Intentionally left empty. + } + + template + storm::dd::Add SymbolicMinMaxLinearEquationSolver::solveEquations(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const& b) const { + STORM_LOG_WARN_COND_DEBUG(this->isRequirementsCheckedSet(), "The requirements of the solver have not been marked as checked. Please provide the appropriate check or mark the requirements as checked (if applicable)."); switch (this->getSettings().getSolutionMethod()) { case SymbolicMinMaxLinearEquationSolverSettings::SolutionMethod::ValueIteration: - return solveEquationsValueIteration(minimize, x, b); + return solveEquationsValueIteration(dir, x, b); break; case SymbolicMinMaxLinearEquationSolverSettings::SolutionMethod::PolicyIteration: - return solveEquationsPolicyIteration(minimize, x, b); + return solveEquationsPolicyIteration(dir, x, b); break; } } template - storm::dd::Add SymbolicMinMaxLinearEquationSolver::solveEquationsValueIteration(bool minimize, storm::dd::Add const& x, storm::dd::Add const& b) const { + storm::dd::Add SymbolicMinMaxLinearEquationSolver::solveEquationsValueIteration(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const& b) const { // Set up the environment. storm::dd::Add xCopy = x; uint_fast64_t iterations = 0; @@ -107,7 +113,7 @@ namespace storm { storm::dd::Add tmp = this->A.multiplyMatrix(xCopyAsColumn, this->columnMetaVariables); tmp += b; - if (minimize) { + if (dir == storm::solver::OptimizationDirection::Minimize) { tmp += illegalMaskAdd; tmp = tmp.minAbstract(this->choiceVariables); } else { @@ -132,15 +138,15 @@ namespace storm { } template - storm::dd::Add SymbolicMinMaxLinearEquationSolver::solveEquationsPolicyIteration(bool minimize, storm::dd::Add const& x, storm::dd::Add const& b) const { + storm::dd::Add SymbolicMinMaxLinearEquationSolver::solveEquationsPolicyIteration(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const& b) const { // Set up the environment. storm::dd::Add currentSolution = x; storm::dd::Add diagonal = (storm::utility::dd::getRowColumnDiagonal(x.getDdManager(), this->rowColumnMetaVariablePairs) && this->allRows).template toAdd(); uint_fast64_t iterations = 0; bool converged = false; - // Pick arbitrary initial scheduler. - storm::dd::Bdd scheduler = this->A.sumAbstract(this->columnMetaVariables).maxAbstractRepresentative(this->choiceVariables); + // Choose initial scheduler. + storm::dd::Bdd scheduler = this->hasInitialScheduler() ? this->getInitialScheduler() : this->A.sumAbstract(this->columnMetaVariables).maxAbstractRepresentative(this->choiceVariables); // And apply it to the matrix and vector. storm::dd::Add schedulerA = diagonal - scheduler.ite(this->A, scheduler.getDdManager().template getAddZero()).sumAbstract(this->choiceVariables); @@ -158,7 +164,7 @@ namespace storm { storm::dd::Add choiceValues = this->A.multiplyMatrix(schedulerX.swapVariables(this->rowColumnMetaVariablePairs), this->columnMetaVariables) + b; storm::dd::Bdd nextScheduler; - if (minimize) { + if (dir == storm::solver::OptimizationDirection::Minimize) { choiceValues += illegalMaskAdd; nextScheduler = choiceValues.minAbstractRepresentative(this->choiceVariables); } else { @@ -190,7 +196,7 @@ namespace storm { } template - storm::dd::Add SymbolicMinMaxLinearEquationSolver::multiply(bool minimize, storm::dd::Add const& x, storm::dd::Add const* b, uint_fast64_t n) const { + storm::dd::Add SymbolicMinMaxLinearEquationSolver::multiply(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const* b, uint_fast64_t n) const { storm::dd::Add xCopy = x; // Perform matrix-vector multiplication while the bound is met. @@ -201,7 +207,7 @@ namespace storm { xCopy += *b; } - if (minimize) { + if (dir == storm::solver::OptimizationDirection::Minimize) { // This is a hack and only here because of the lack of a suitable minAbstract/maxAbstract function // that can properly deal with a restriction of the choices. xCopy += illegalMaskAdd; @@ -214,16 +220,66 @@ namespace storm { return xCopy; } + template + void SymbolicMinMaxLinearEquationSolver::setInitialScheduler(storm::dd::Bdd const& scheduler) { + this->initialScheduler = scheduler; + } + + template + storm::dd::Bdd const& SymbolicMinMaxLinearEquationSolver::getInitialScheduler() const { + return initialScheduler.get(); + } + + template + bool SymbolicMinMaxLinearEquationSolver::hasInitialScheduler() const { + return static_cast(initialScheduler); + } + + template + MinMaxLinearEquationSolverRequirements SymbolicMinMaxLinearEquationSolver::getRequirements(MinMaxLinearEquationSolverSystemType const& equationSystemType, boost::optional const& direction) const { + MinMaxLinearEquationSolverRequirements requirements; + + if (equationSystemType == MinMaxLinearEquationSolverSystemType::UntilProbabilities) { + if (this->getSettings().getSolutionMethod() == SymbolicMinMaxLinearEquationSolverSettings::SolutionMethod::PolicyIteration) { + if (!direction || direction.get() == OptimizationDirection::Maximize) { + requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler); + } + } + } else if (equationSystemType == MinMaxLinearEquationSolverSystemType::ReachabilityRewards) { + if (!direction || direction.get() == OptimizationDirection::Minimize) { + requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler); + } + } + + return requirements; + } + + template + void SymbolicMinMaxLinearEquationSolver::setRequirementsChecked(bool value) { + this->requirementsChecked = value; + } + + template + bool SymbolicMinMaxLinearEquationSolver::isRequirementsCheckedSet() const { + return this->requirementsChecked; + } + template SymbolicMinMaxLinearEquationSolverSettings const& SymbolicMinMaxLinearEquationSolver::getSettings() const { return settings; } + template + MinMaxLinearEquationSolverRequirements SymbolicMinMaxLinearEquationSolverFactory::getRequirements(MinMaxLinearEquationSolverSystemType const& equationSystemType, boost::optional const& direction) const { + std::unique_ptr> solver = this->create(); + return solver->getRequirements(equationSystemType, direction); + } + template std::unique_ptr> SymbolicGeneralMinMaxLinearEquationSolverFactory::create(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalMask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& choiceVariables, std::vector> const& rowColumnMetaVariablePairs) const { return std::make_unique>(A, allRows, illegalMask, rowMetaVariables, columnMetaVariables, choiceVariables, rowColumnMetaVariablePairs, std::make_unique>(), settings); } - + template SymbolicMinMaxLinearEquationSolverSettings& SymbolicGeneralMinMaxLinearEquationSolverFactory::getSettings() { return settings; @@ -234,6 +290,11 @@ namespace storm { return settings; } + template + std::unique_ptr> SymbolicGeneralMinMaxLinearEquationSolverFactory::create() const { + return std::make_unique>(settings); + } + template class SymbolicMinMaxLinearEquationSolverSettings; template class SymbolicMinMaxLinearEquationSolverSettings; diff --git a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.h b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.h index 650934211..e9e76ee6e 100644 --- a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.h +++ b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.h @@ -4,10 +4,15 @@ #include #include #include -#include +#include + +#include "storm/solver/OptimizationDirection.h" #include "storm/solver/SymbolicLinearEquationSolver.h" +#include "storm/solver/MinMaxLinearEquationSolverSystemType.h" +#include "storm/solver/MinMaxLinearEquationSolverRequirements.h" + #include "storm/storage/expressions/Variable.h" #include "storm/storage/dd/DdType.h" @@ -54,6 +59,8 @@ namespace storm { template class SymbolicMinMaxLinearEquationSolver { public: + SymbolicMinMaxLinearEquationSolver(SymbolicMinMaxLinearEquationSolverSettings const& settings = SymbolicMinMaxLinearEquationSolverSettings()); + /*! * Constructs a symbolic min/max linear equation solver with the given meta variable sets and pairs. * @@ -75,36 +82,65 @@ namespace storm { * The solution of the set of linear equations will be written to the vector x. Note that the matrix A has * to be given upon construction time of the solver object. * - * @param minimize If set, all the value of a group of rows is the taken as the minimum over all rows and as - * the maximum otherwise. + * @param dir Determines the direction of the optimization. * @param x The initual guess for the solution vector. Its length must be equal to the number of row * groups of A. * @param b The right-hand side of the equation system. Its length must be equal to the number of row groups * of A. * @return The solution of the equation system. */ - virtual storm::dd::Add solveEquations(bool minimize, storm::dd::Add const& x, storm::dd::Add const& b) const; + virtual storm::dd::Add solveEquations(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const& b) const; /*! * Performs repeated matrix-vector multiplication, using x[0] = x and x[i + 1] = A*x[i] + b. After * performing the necessary multiplications, the result is written to the input vector x. Note that the * matrix A has to be given upon construction time of the solver object. * - * @param minimize If set, all the value of a group of rows is the taken as the minimum over all rows and as - * the maximum otherwise. + * @param dir Determines the direction of the optimization. * @param x The initial vector with which to perform matrix-vector multiplication. Its length must be equal * to the number of row groups of A. * @param b If non-null, this vector is added after each multiplication. If given, its length must be equal * to the number of row groups of A. * @return The solution of the equation system. */ - virtual storm::dd::Add multiply(bool minimize, storm::dd::Add const& x, storm::dd::Add const* b = nullptr, uint_fast64_t n = 1) const; + virtual storm::dd::Add multiply(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const* b = nullptr, uint_fast64_t n = 1) const; SymbolicMinMaxLinearEquationSolverSettings const& getSettings() const; + /*! + * Sets an initial scheduler that is required by some solvers (see requirements). + */ + void setInitialScheduler(storm::dd::Bdd const& scheduler); + + /*! + * Retrieves the initial scheduler (if there is any). + */ + storm::dd::Bdd const& getInitialScheduler() const; + + /*! + * Retrieves whether an initial scheduler was set. + */ + bool hasInitialScheduler() const; + + /*! + * Retrieves the requirements of the solver. + */ + virtual MinMaxLinearEquationSolverRequirements getRequirements(MinMaxLinearEquationSolverSystemType const& equationSystemType, boost::optional const& direction = boost::none) const; + + /*! + * Notifies the solver that the requirements for solving equations have been checked. If this has not been + * done before solving equations, the solver might issue a warning, perform the checks itself or even fail. + */ + void setRequirementsChecked(bool value = true); + + /*! + * Retrieves whether the solver is aware that the requirements were checked. + */ + bool isRequirementsCheckedSet() const; + private: - storm::dd::Add solveEquationsValueIteration(bool minimize, storm::dd::Add const& x, storm::dd::Add const& b) const; - storm::dd::Add solveEquationsPolicyIteration(bool minimize, storm::dd::Add const& x, storm::dd::Add const& b) const; + storm::dd::Add solveEquationsValueIteration(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const& b) const; + storm::dd::Add solveEquationsPolicyIteration(storm::solver::OptimizationDirection const& dir, storm::dd::Add const& x, storm::dd::Add const& b) const; protected: // The matrix defining the coefficients of the linear equation system. @@ -133,23 +169,40 @@ namespace storm { // The settings to use. SymbolicMinMaxLinearEquationSolverSettings settings; + + // A flag indicating whether the requirements were checked. + bool requirementsChecked; + + // A scheduler that specifies with which schedulers to start. + boost::optional> initialScheduler; }; template class SymbolicMinMaxLinearEquationSolverFactory { public: virtual std::unique_ptr> create(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalMask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& choiceVariables, std::vector> const& rowColumnMetaVariablePairs) const = 0; + + /*! + * Retrieves the requirements of the solver that would be created when calling create() right now. The + * requirements are guaranteed to be ordered according to their appearance in the SolverRequirement type. + */ + MinMaxLinearEquationSolverRequirements getRequirements(MinMaxLinearEquationSolverSystemType const& equationSystemType, boost::optional const& direction = boost::none) const; + + private: + virtual std::unique_ptr> create() const = 0; }; template class SymbolicGeneralMinMaxLinearEquationSolverFactory : public SymbolicMinMaxLinearEquationSolverFactory { public: - virtual std::unique_ptr> create(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalMask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& choiceVariables, std::vector> const& rowColumnMetaVariablePairs) const; + virtual std::unique_ptr> create(storm::dd::Add const& A, storm::dd::Bdd const& allRows, storm::dd::Bdd const& illegalMask, std::set const& rowMetaVariables, std::set const& columnMetaVariables, std::set const& choiceVariables, std::vector> const& rowColumnMetaVariablePairs) const override; SymbolicMinMaxLinearEquationSolverSettings& getSettings(); SymbolicMinMaxLinearEquationSolverSettings const& getSettings() const; private: + virtual std::unique_ptr> create() const override; + SymbolicMinMaxLinearEquationSolverSettings settings; }; diff --git a/src/storm/utility/graph.cpp b/src/storm/utility/graph.cpp index c341cdc22..4464ed2be 100644 --- a/src/storm/utility/graph.cpp +++ b/src/storm/utility/graph.cpp @@ -885,6 +885,26 @@ namespace storm { std::pair performProb01Min(storm::models::sparse::NondeterministicModel const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) { return performProb01Min(model.getTransitionMatrix(), model.getTransitionMatrix().getRowGroupIndices(), model.getBackwardTransitions(), phiStates, psiStates); } + + template + storm::dd::Bdd computeSchedulerProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) { + // Initialize environment for backward search. + storm::dd::DdManager const& manager = model.getManager(); + storm::dd::Bdd statesWithProbabilityGreater0E = manager.getBddZero(); + storm::dd::Bdd frontier = psiStates; + storm::dd::Bdd scheduler = manager.getBddZero(); + + uint_fast64_t iterations = 0; + while (!frontier.isZero()) { + storm::dd::Bdd statesAndChoicesWithProbabilityGreater0E = statesWithProbabilityGreater0E.inverseRelationalProduct(transitionMatrix, model.getRowVariables(), model.getColumnVariables()); + frontier = phiStates && statesAndChoicesWithProbabilityGreater0E.existsAbstract(model.getNondeterminismVariables()) && !statesWithProbabilityGreater0E; + scheduler = scheduler || (frontier && statesAndChoicesWithProbabilityGreater0E).existsAbstractRepresentative(model.getNondeterminismVariables()); + statesWithProbabilityGreater0E |= frontier; + ++iterations; + } + + return scheduler; + } template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates) { @@ -1478,6 +1498,8 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01(storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd computeSchedulerProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); template storm::dd::Bdd performProb0A(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); @@ -1510,6 +1532,8 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01(storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd computeSchedulerProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); template storm::dd::Bdd performProb0A(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); @@ -1542,6 +1566,8 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01(storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd computeSchedulerProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); template storm::dd::Bdd performProb0A(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); @@ -1570,6 +1596,8 @@ namespace storm { template std::pair, storm::dd::Bdd> performProb01(storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd computeSchedulerProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); template storm::dd::Bdd performProb0A(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); diff --git a/src/storm/utility/graph.h b/src/storm/utility/graph.h index ff8b30287..69a07770c 100644 --- a/src/storm/utility/graph.h +++ b/src/storm/utility/graph.h @@ -447,6 +447,12 @@ namespace storm { template storm::dd::Bdd performProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + /*! + * Computes a scheduler realizing a probability greater zero of satisfying phi until psi for all such states. + */ + template + storm::dd::Bdd computeSchedulerProbGreater0E(storm::models::symbolic::NondeterministicModel const& model, storm::dd::Bdd const& transitionMatrix, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates); + /*! * Computes the set of states for which there does not exist a scheduler that achieves a probability greater * than zero of satisfying phi until psi.