diff --git a/src/storm/exceptions/InvalidSolverSettingsException.h b/src/storm/exceptions/InvalidSolverSettingsException.h new file mode 100644 index 000000000..0580fb0cc --- /dev/null +++ b/src/storm/exceptions/InvalidSolverSettingsException.h @@ -0,0 +1,12 @@ +#pragma once + +#include "storm/exceptions/BaseException.h" +#include "storm/exceptions/ExceptionMacros.h" + +namespace storm { + namespace exceptions { + + STORM_NEW_EXCEPTION(InvalidSolverSettingsException) + + } // namespace exceptions +} // namespace storm diff --git a/src/storm/exceptions/UnmetRequirementException.h b/src/storm/exceptions/UnmetRequirementException.h new file mode 100644 index 000000000..3a7b70312 --- /dev/null +++ b/src/storm/exceptions/UnmetRequirementException.h @@ -0,0 +1,12 @@ +#pragma once + +#include "storm/exceptions/BaseException.h" +#include "storm/exceptions/ExceptionMacros.h" + +namespace storm { + namespace exceptions { + + STORM_NEW_EXCEPTION(UnmetRequirementException) + + } // namespace exceptions +} // namespace storm diff --git a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp index 5c88f9466..fd808cdae 100644 --- a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp @@ -117,7 +117,7 @@ namespace storm { STORM_LOG_DEBUG("Computing valid scheduler hint, because the solver requires it."); initialScheduler = computeValidInitialSchedulerForUntilProbabilities<ValueType>(explicitRepresentation.first, explicitRepresentation.second); - requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); + requirements.clearValidInitialScheduler(); } STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Cannot establish requirements for solver."); } @@ -126,6 +126,7 @@ namespace storm { if (initialScheduler) { solver->setInitialScheduler(std::move(initialScheduler.get())); } + solver->setBounds(storm::utility::zero<ValueType>(), storm::utility::one<ValueType>()); solver->setRequirementsChecked(); solver->solveEquations(dir, x, explicitRepresentation.second); @@ -357,7 +358,7 @@ namespace storm { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); requireInitialScheduler = true; - requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); + requirements.clearValidInitialScheduler(); } STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Cannot establish requirements for solver."); } diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index ae898a14d..437631a3a 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -207,7 +207,7 @@ namespace storm { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { 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); + requirements.clearValidInitialScheduler(); } STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "There are unchecked requirements of the solver."); } @@ -215,15 +215,14 @@ namespace storm { bool skipEcWithinMaybeStatesCheck = dir == storm::OptimizationDirection::Minimize || (hint.isExplicitModelCheckerHint() && hint.asExplicitModelCheckerHint<ValueType>().getNoEndComponentsInMaybeStates()); extractHintInformationForMaybeStates(result, transitionMatrix, backwardTransitions, maybeStates, selectedChoices, hint, skipEcWithinMaybeStatesCheck); - result.lowerResultBound = storm::utility::zero<ValueType>(); - if (type == storm::solver::EquationSystemType::UntilProbabilities) { + // Only set bounds if we did not obtain them from the hint. + if (!result.hasLowerResultBound()) { + result.lowerResultBound = storm::utility::zero<ValueType>(); + } + if (!result.hasUpperResultBound() && type == storm::solver::EquationSystemType::UntilProbabilities) { result.upperResultBound = storm::utility::one<ValueType>(); - } else if (type == storm::solver::EquationSystemType::ReachabilityRewards) { - // Intentionally left empty. - } else { - STORM_LOG_ASSERT(false, "Unexpected equation system type."); } - + return result; } diff --git a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp index 395018b6e..c6231a5eb 100644 --- a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp @@ -88,7 +88,7 @@ namespace storm { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); initialScheduler = computeValidSchedulerHint(storm::solver::EquationSystemType::UntilProbabilities, model, transitionMatrix, maybeStates, statesWithProbability01.second); - requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); + requirements.clearValidInitialScheduler(); } STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Could not establish requirements of solver."); } @@ -247,7 +247,7 @@ namespace storm { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); initialScheduler = computeValidSchedulerHint(storm::solver::EquationSystemType::ReachabilityRewards, model, transitionMatrix, maybeStates, targetStates); - requirements.set(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler, false); + requirements.clearValidInitialScheduler(); } STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Could not establish requirements of solver."); } diff --git a/src/storm/settings/modules/EigenEquationSolverSettings.cpp b/src/storm/settings/modules/EigenEquationSolverSettings.cpp index ea25da11d..3047a13a4 100644 --- a/src/storm/settings/modules/EigenEquationSolverSettings.cpp +++ b/src/storm/settings/modules/EigenEquationSolverSettings.cpp @@ -106,6 +106,15 @@ namespace storm { return true; } + std::ostream& operator<<(std::ostream& out, EigenEquationSolverSettings::LinearEquationMethod const& method) { + switch (method) { + case EigenEquationSolverSettings::LinearEquationMethod::BiCGSTAB: out << "bicgstab"; break; + case EigenEquationSolverSettings::LinearEquationMethod::GMRES: out << "gmres"; break; + case EigenEquationSolverSettings::LinearEquationMethod::DGMRES: out << "dgmres"; break; + case EigenEquationSolverSettings::LinearEquationMethod::SparseLU: out << "sparselu"; break; + } + } + } // namespace modules } // namespace settings } // namespace storm diff --git a/src/storm/settings/modules/EigenEquationSolverSettings.h b/src/storm/settings/modules/EigenEquationSolverSettings.h index 8ac5fa7f0..622d71764 100644 --- a/src/storm/settings/modules/EigenEquationSolverSettings.h +++ b/src/storm/settings/modules/EigenEquationSolverSettings.h @@ -110,6 +110,8 @@ namespace storm { static const std::string restartOptionName; }; + std::ostream& operator<<(std::ostream& out, EigenEquationSolverSettings::LinearEquationMethod const& method); + } // namespace modules } // namespace settings } // namespace storm diff --git a/src/storm/settings/modules/GeneralSettings.cpp b/src/storm/settings/modules/GeneralSettings.cpp index da9a7223c..7edb76e7b 100644 --- a/src/storm/settings/modules/GeneralSettings.cpp +++ b/src/storm/settings/modules/GeneralSettings.cpp @@ -30,6 +30,7 @@ namespace storm { const std::string GeneralSettings::bisimulationOptionShortName = "bisim"; const std::string GeneralSettings::parametricOptionName = "parametric"; const std::string GeneralSettings::exactOptionName = "exact"; + const std::string GeneralSettings::soundOptionName = "sound"; GeneralSettings::GeneralSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, helpOptionName, false, "Shows all available options, arguments and descriptions.").setShortName(helpOptionShortName) @@ -43,6 +44,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, bisimulationOptionName, false, "Sets whether to perform bisimulation minimization.").setShortName(bisimulationOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, parametricOptionName, false, "Sets whether to enable parametric model checking.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, exactOptionName, false, "Sets whether to enable exact model checking.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, soundOptionName, false, "Sets whether to force sound model checking.").build()); } bool GeneralSettings::isHelpSet() const { @@ -86,6 +88,10 @@ namespace storm { return this->getOption(exactOptionName).getHasOptionBeenSet(); } + bool GeneralSettings::isSoundSet() const { + return this->getOption(soundOptionName).getHasOptionBeenSet(); + } + void GeneralSettings::finalize() { // Intentionally left empty. } diff --git a/src/storm/settings/modules/GeneralSettings.h b/src/storm/settings/modules/GeneralSettings.h index 52b519ed2..42f22fb3b 100644 --- a/src/storm/settings/modules/GeneralSettings.h +++ b/src/storm/settings/modules/GeneralSettings.h @@ -84,20 +84,20 @@ namespace storm { * @return True iff the option was set. */ bool isParametricSet() const; - + /*! - * Retrieves whether a min/max equation solving technique has been set. + * Retrieves whether the option enabling exact model checking is set. * - * @return True iff an equation solving technique has been set. + * @return True iff the option was set. */ - bool isMinMaxEquationSolvingTechniqueSet() const; + bool isExactSet() const; /*! - * Retrieves whether the option enabling exact model checking is set. + * Retrieves whether the option forcing soundnet is set. * * @return True iff the option was set. */ - bool isExactSet() const; + bool isSoundSet() const; bool check() const override; void finalize() override; @@ -121,8 +121,8 @@ namespace storm { static const std::string bisimulationOptionName; static const std::string bisimulationOptionShortName; static const std::string parametricOptionName; - static const std::string exactOptionName; + static const std::string soundOptionName; }; } // namespace modules diff --git a/src/storm/settings/modules/NativeEquationSolverSettings.cpp b/src/storm/settings/modules/NativeEquationSolverSettings.cpp index 7b17808f1..d797a968a 100644 --- a/src/storm/settings/modules/NativeEquationSolverSettings.cpp +++ b/src/storm/settings/modules/NativeEquationSolverSettings.cpp @@ -25,7 +25,7 @@ namespace storm { const std::string NativeEquationSolverSettings::powerMethodMultiplicationStyleOptionName = "powmult"; NativeEquationSolverSettings::NativeEquationSolverSettings() : ModuleSettings(moduleName) { - std::vector<std::string> methods = { "jacobi", "gaussseidel", "sor", "walkerchae", "power", "spower" }; + std::vector<std::string> methods = { "jacobi", "gaussseidel", "sor", "walkerchae", "power" }; this->addOption(storm::settings::OptionBuilder(moduleName, techniqueOptionName, true, "The method to be used for solving linear equation systems with the native engine.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(methods)).setDefaultValueString("jacobi").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); @@ -61,8 +61,6 @@ namespace storm { return NativeEquationSolverSettings::LinearEquationMethod::WalkerChae; } else if (linearEquationSystemTechniqueAsString == "power") { return NativeEquationSolverSettings::LinearEquationMethod::Power; - } else if (linearEquationSystemTechniqueAsString == "spower") { - return NativeEquationSolverSettings::LinearEquationMethod::SoundPower; } STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown solution technique '" << linearEquationSystemTechniqueAsString << "' selected."); } @@ -114,6 +112,16 @@ namespace storm { return true; } + std::ostream& operator<<(std::ostream& out, NativeEquationSolverSettings::LinearEquationMethod const& method) { + switch (method) { + case NativeEquationSolverSettings::LinearEquationMethod::Jacobi: out << "jacobi"; break; + case NativeEquationSolverSettings::LinearEquationMethod::GaussSeidel: out << "gaussseidel"; break; + case NativeEquationSolverSettings::LinearEquationMethod::SOR: out << "sor"; break; + case NativeEquationSolverSettings::LinearEquationMethod::WalkerChae: out << "walkerchae"; break; + case NativeEquationSolverSettings::LinearEquationMethod::Power: out << "power"; break; + } + } + } // namespace modules } // namespace settings } // namespace storm diff --git a/src/storm/settings/modules/NativeEquationSolverSettings.h b/src/storm/settings/modules/NativeEquationSolverSettings.h index ccedfbdc6..6f70db06a 100644 --- a/src/storm/settings/modules/NativeEquationSolverSettings.h +++ b/src/storm/settings/modules/NativeEquationSolverSettings.h @@ -15,7 +15,7 @@ namespace storm { class NativeEquationSolverSettings : public ModuleSettings { public: // An enumeration of all available methods for solving linear equations. - enum class LinearEquationMethod { Jacobi, GaussSeidel, SOR, WalkerChae, Power, SoundPower }; + enum class LinearEquationMethod { Jacobi, GaussSeidel, SOR, WalkerChae, Power }; // An enumeration of all available convergence criteria. enum class ConvergenceCriterion { Absolute, Relative }; @@ -118,6 +118,8 @@ namespace storm { static const std::string powerMethodMultiplicationStyleOptionName; }; + std::ostream& operator<<(std::ostream& out, NativeEquationSolverSettings::LinearEquationMethod const& method); + } // namespace modules } // namespace settings } // namespace storm diff --git a/src/storm/solver/EigenLinearEquationSolver.cpp b/src/storm/solver/EigenLinearEquationSolver.cpp index 5f0a001a9..002352d88 100644 --- a/src/storm/solver/EigenLinearEquationSolver.cpp +++ b/src/storm/solver/EigenLinearEquationSolver.cpp @@ -3,6 +3,7 @@ #include "storm/adapters/EigenAdapter.h" #include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/EigenEquationSolverSettings.h" #include "storm/utility/vector.h" @@ -16,12 +17,7 @@ namespace storm { EigenLinearEquationSolverSettings<ValueType>::EigenLinearEquationSolverSettings() { // Get the settings object to customize linear solving. storm::settings::modules::EigenEquationSolverSettings const& settings = storm::settings::getModule<storm::settings::modules::EigenEquationSolverSettings>(); - - // Get appropriate settings. - maximalNumberOfIterations = settings.getMaximalIterationCount(); - precision = settings.getPrecision(); - restart = settings.getRestartIterationCount(); - + // Determine the method to be used. storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod methodAsSetting = settings.getLinearEquationSystemMethod(); if (methodAsSetting == storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod::BiCGSTAB) { @@ -33,7 +29,7 @@ namespace storm { } else if (methodAsSetting == storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod::GMRES) { method = SolutionMethod::GMRES; } - + // Check which preconditioner to use. storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod preconditionAsSetting = settings.getPreconditioningMethod(); if (preconditionAsSetting == storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod::Ilu) { @@ -43,11 +39,22 @@ namespace storm { } else if (preconditionAsSetting == storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod::None) { preconditioner = Preconditioner::None; } + + // Get appropriate settings. + maximalNumberOfIterations = settings.getMaximalIterationCount(); + precision = settings.getPrecision(); + restart = settings.getRestartIterationCount(); + + // Finally force soundness and potentially overwrite some other settings. + this->setForceSoundness(storm::settings::getModule<storm::settings::modules::GeneralSettings>().isSoundSet()); } template<typename ValueType> void EigenLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) { this->method = method; + + // Make sure we switch the method if we have to guarantee soundness. + this->setForceSoundness(forceSoundness); } template<typename ValueType> @@ -69,6 +76,15 @@ namespace storm { void EigenLinearEquationSolverSettings<ValueType>::setNumberOfIterationsUntilRestart(uint64_t restart) { this->restart = restart; } + + template<typename ValueType> + void EigenLinearEquationSolverSettings<ValueType>::setForceSoundness(bool value) { + forceSoundness = value; + if (value) { + STORM_LOG_WARN_COND(method != SolutionMethod::SparseLU, "To guarantee soundness, the equation solving technique has been switched to '" << storm::settings::modules::EigenEquationSolverSettings:: LinearEquationMethod::SparseLU << "'."); + method = SolutionMethod::SparseLU; + } + } template<typename ValueType> typename EigenLinearEquationSolverSettings<ValueType>::SolutionMethod EigenLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { @@ -95,6 +111,11 @@ namespace storm { return restart; } + template<typename ValueType> + bool EigenLinearEquationSolverSettings<ValueType>::getForceSoundness() const { + return forceSoundness; + } + #ifdef STORM_HAVE_CARL EigenLinearEquationSolverSettings<storm::RationalNumber>::EigenLinearEquationSolverSettings() { // Intentionally left empty. @@ -135,7 +156,7 @@ namespace storm { } template<typename ValueType> - bool EigenLinearEquationSolver<ValueType>::solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + bool EigenLinearEquationSolver<ValueType>::internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { // Map the input vectors to Eigen's format. auto eigenX = StormEigen::Matrix<ValueType, StormEigen::Dynamic, 1>::Map(x.data(), x.size()); auto eigenB = StormEigen::Matrix<ValueType, StormEigen::Dynamic, 1>::Map(b.data(), b.size()); @@ -306,7 +327,7 @@ namespace storm { template<typename ValueType> LinearEquationSolverProblemFormat EigenLinearEquationSolver<ValueType>::getEquationProblemFormat() const { - LinearEquationSolverProblemFormat::EquationSystem; + return LinearEquationSolverProblemFormat::EquationSystem; } template<typename ValueType> @@ -327,7 +348,7 @@ namespace storm { #ifdef STORM_HAVE_CARL // Specialization for storm::RationalNumber template<> - bool EigenLinearEquationSolver<storm::RationalNumber>::solveEquations(std::vector<storm::RationalNumber>& x, std::vector<storm::RationalNumber> const& b) const { + bool EigenLinearEquationSolver<storm::RationalNumber>::internalSolveEquations(std::vector<storm::RationalNumber>& x, std::vector<storm::RationalNumber> const& b) const { STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with with rational numbers using LU factorization (Eigen library)."); // Map the input vectors to Eigen's format. @@ -342,7 +363,7 @@ namespace storm { // Specialization for storm::RationalFunction template<> - bool EigenLinearEquationSolver<storm::RationalFunction>::solveEquations(std::vector<storm::RationalFunction>& x, std::vector<storm::RationalFunction> const& b) const { + bool EigenLinearEquationSolver<storm::RationalFunction>::internalSolveEquations(std::vector<storm::RationalFunction>& x, std::vector<storm::RationalFunction> const& b) const { STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with rational functions using LU factorization (Eigen library)."); // Map the input vectors to Eigen's format. diff --git a/src/storm/solver/EigenLinearEquationSolver.h b/src/storm/solver/EigenLinearEquationSolver.h index 951258a76..0bfadd7ca 100644 --- a/src/storm/solver/EigenLinearEquationSolver.h +++ b/src/storm/solver/EigenLinearEquationSolver.h @@ -25,14 +25,17 @@ namespace storm { void setPrecision(ValueType precision); void setMaximalNumberOfIterations(uint64_t maximalNumberOfIterations); void setNumberOfIterationsUntilRestart(uint64_t restart); - + void setForceSoundness(bool value); + SolutionMethod getSolutionMethod() const; Preconditioner getPreconditioner() const; ValueType getPrecision() const; uint64_t getMaximalNumberOfIterations() const; uint64_t getNumberOfIterationsUntilRestart() const; + bool getForceSoundness() const; private: + bool forceSoundness; SolutionMethod method; Preconditioner preconditioner; double precision; @@ -67,14 +70,16 @@ namespace storm { virtual void setMatrix(storm::storage::SparseMatrix<ValueType> const& A) override; virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& A) override; - virtual bool solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; virtual void multiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result) const override; EigenLinearEquationSolverSettings<ValueType>& getSettings(); EigenLinearEquationSolverSettings<ValueType> const& getSettings() const; virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const override; - + + protected: + virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; + private: virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixColumnCount() const override; diff --git a/src/storm/solver/EliminationLinearEquationSolver.cpp b/src/storm/solver/EliminationLinearEquationSolver.cpp index 94aba88f5..7bb12d4bd 100644 --- a/src/storm/solver/EliminationLinearEquationSolver.cpp +++ b/src/storm/solver/EliminationLinearEquationSolver.cpp @@ -63,9 +63,9 @@ namespace storm { } template<typename ValueType> - bool EliminationLinearEquationSolver<ValueType>::solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + bool EliminationLinearEquationSolver<ValueType>::internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { // FIXME: This solver will not work for all input systems. More concretely, the current implementation will - // not work for systems that have a 0 on the diagonal. This is not a restriction of this technique in general + // not work for systems that have a 1 on the diagonal. This is not a restriction of this technique in general // but arbitrary matrices require pivoting, which is not currently implemented. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with elimination"); diff --git a/src/storm/solver/EliminationLinearEquationSolver.h b/src/storm/solver/EliminationLinearEquationSolver.h index 30d0277b0..b99485ce1 100644 --- a/src/storm/solver/EliminationLinearEquationSolver.h +++ b/src/storm/solver/EliminationLinearEquationSolver.h @@ -33,13 +33,15 @@ namespace storm { virtual void setMatrix(storm::storage::SparseMatrix<ValueType> const& A) override; virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& A) override; - virtual bool solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; virtual void multiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result) const override; EliminationLinearEquationSolverSettings<ValueType>& getSettings(); EliminationLinearEquationSolverSettings<ValueType> const& getSettings() const; virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const override; + + protected: + virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; private: void initializeSettings(); diff --git a/src/storm/solver/GmmxxLinearEquationSolver.cpp b/src/storm/solver/GmmxxLinearEquationSolver.cpp index f7aea79a9..2708d0a68 100644 --- a/src/storm/solver/GmmxxLinearEquationSolver.cpp +++ b/src/storm/solver/GmmxxLinearEquationSolver.cpp @@ -3,17 +3,19 @@ #include <cmath> #include <utility> +#include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/GeneralSettings.h" +#include "storm/settings/modules/GmmxxEquationSolverSettings.h" + #include "storm/adapters/GmmxxAdapter.h" #include "storm/solver/GmmxxMultiplier.h" +#include "storm/solver/NativeLinearEquationSolver.h" -#include "storm/settings/SettingsManager.h" #include "storm/utility/vector.h" #include "storm/utility/constants.h" #include "storm/exceptions/InvalidStateException.h" -#include "storm/settings/modules/GmmxxEquationSolverSettings.h" - -#include "storm/solver/NativeLinearEquationSolver.h" +#include "storm/exceptions/InvalidSolverSettingsException.h" #include "storm/utility/gmm.h" #include "storm/utility/vector.h" @@ -49,11 +51,17 @@ namespace storm { } else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod::None) { preconditioner = Preconditioner::None; } + + // Finally force soundness and potentially overwrite some other settings. + this->setForceSoundness(storm::settings::getModule<storm::settings::modules::GeneralSettings>().isSoundSet()); } template<typename ValueType> void GmmxxLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) { this->method = method; + + // Make sure we switch the method if we have to guarantee soundness. + this->setForceSoundness(forceSoundness); } template<typename ValueType> @@ -76,6 +84,12 @@ namespace storm { this->restart = restart; } + template<typename ValueType> + void GmmxxLinearEquationSolverSettings<ValueType>::setForceSoundness(bool value) { + STORM_LOG_THROW(!value, storm::exceptions::InvalidSolverSettingsException, "Solver cannot guarantee soundness, please choose a different equation solver."); + forceSoundness = value; + } + template<typename ValueType> typename GmmxxLinearEquationSolverSettings<ValueType>::SolutionMethod GmmxxLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { return method; @@ -101,6 +115,11 @@ namespace storm { return restart; } + template<typename ValueType> + bool GmmxxLinearEquationSolverSettings<ValueType>::getForceSoundness() const { + return forceSoundness; + } + template<typename ValueType> GmmxxLinearEquationSolver<ValueType>::GmmxxLinearEquationSolver(GmmxxLinearEquationSolverSettings<ValueType> const& settings) : settings(settings) { // Intentionally left empty. @@ -129,7 +148,7 @@ namespace storm { } template<typename ValueType> - bool GmmxxLinearEquationSolver<ValueType>::solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + bool GmmxxLinearEquationSolver<ValueType>::internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { auto method = this->getSettings().getSolutionMethod(); auto preconditioner = this->getSettings().getPreconditioner(); STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with Gmmxx linear equation solver with method '" << method << "' and preconditioner '" << preconditioner << "' (max. " << this->getSettings().getMaximalNumberOfIterations() << " iterations)."); diff --git a/src/storm/solver/GmmxxLinearEquationSolver.h b/src/storm/solver/GmmxxLinearEquationSolver.h index 95cb10757..453a841e4 100644 --- a/src/storm/solver/GmmxxLinearEquationSolver.h +++ b/src/storm/solver/GmmxxLinearEquationSolver.h @@ -19,7 +19,6 @@ namespace storm { enum class Preconditioner { Ilu, Diagonal, None }; - friend std::ostream& operator<<(std::ostream& out, Preconditioner const& preconditioner) { switch (preconditioner) { @@ -51,14 +50,19 @@ namespace storm { void setPrecision(ValueType precision); void setMaximalNumberOfIterations(uint64_t maximalNumberOfIterations); void setNumberOfIterationsUntilRestart(uint64_t restart); + void setForceSoundness(bool value); SolutionMethod getSolutionMethod() const; Preconditioner getPreconditioner() const; ValueType getPrecision() const; uint64_t getMaximalNumberOfIterations() const; uint64_t getNumberOfIterationsUntilRestart() const; + bool getForceSoundness() const; private: + // Whether or not we are forced to be sound. + bool forceSoundness; + // The method to use for solving linear equation systems. SolutionMethod method; @@ -88,7 +92,6 @@ namespace storm { virtual void setMatrix(storm::storage::SparseMatrix<ValueType> const& A) override; virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& A) override; - virtual bool solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; virtual void multiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result) const override; virtual void multiplyAndReduce(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices, std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result, std::vector<uint_fast64_t>* choices = nullptr) const override; virtual bool supportsGaussSeidelMultiplication() const override; @@ -102,6 +105,9 @@ namespace storm { virtual void clearCache() const override; + protected: + virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; + private: virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixColumnCount() const override; diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp index 6abfaf270..99561bcfd 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp @@ -1,12 +1,14 @@ #include "storm/solver/IterativeMinMaxLinearEquationSolver.h" #include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/MinMaxEquationSolverSettings.h" #include "storm/utility/vector.h" #include "storm/utility/macros.h" #include "storm/exceptions/InvalidSettingsException.h" #include "storm/exceptions/InvalidStateException.h" +#include "storm/exceptions/UnmetRequirementException.h" namespace storm { namespace solver { @@ -22,6 +24,9 @@ namespace storm { valueIterationMultiplicationStyle = minMaxSettings.getValueIterationMultiplicationStyle(); setSolutionMethod(minMaxSettings.getMinMaxEquationSolvingMethod()); + + // Finally force soundness and potentially overwrite some other settings. + this->setForceSoundness(storm::settings::getModule<storm::settings::modules::GeneralSettings>().isSoundSet()); } template<typename ValueType> @@ -60,6 +65,11 @@ namespace storm { this->valueIterationMultiplicationStyle = value; } + template<typename ValueType> + void IterativeMinMaxLinearEquationSolverSettings<ValueType>::setForceSoundness(bool value) { + this->forceSoundness = value; + } + template<typename ValueType> typename IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod const& IterativeMinMaxLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { return solutionMethod; @@ -84,6 +94,11 @@ namespace storm { MultiplicationStyle IterativeMinMaxLinearEquationSolverSettings<ValueType>::getValueIterationMultiplicationStyle() const { return valueIterationMultiplicationStyle; } + + template<typename ValueType> + bool IterativeMinMaxLinearEquationSolverSettings<ValueType>::getForceSoundness() const { + return forceSoundness; + } template<typename ValueType> IterativeMinMaxLinearEquationSolver<ValueType>::IterativeMinMaxLinearEquationSolver(std::unique_ptr<LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory, IterativeMinMaxLinearEquationSolverSettings<ValueType> const& settings) : StandardMinMaxLinearEquationSolver<ValueType>(std::move(linearEquationSolverFactory)), settings(settings) { @@ -104,7 +119,11 @@ namespace storm { bool IterativeMinMaxLinearEquationSolver<ValueType>::internalSolveEquations(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { switch (this->getSettings().getSolutionMethod()) { case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::ValueIteration: - return solveEquationsValueIteration(dir, x, b); + if (this->getSettings().getForceSoundness()) { + return solveEquationsSoundValueIteration(dir, x, b); + } else { + return solveEquationsValueIteration(dir, x, b); + } case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration: return solveEquationsPolicyIteration(dir, x, b); case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::Acyclic: @@ -229,20 +248,32 @@ namespace storm { template<typename ValueType> MinMaxLinearEquationSolverRequirements IterativeMinMaxLinearEquationSolver<ValueType>::getRequirements(EquationSystemType const& equationSystemType, boost::optional<storm::solver::OptimizationDirection> const& direction) const { - MinMaxLinearEquationSolverRequirements requirements; + // Start by copying the requirements of the linear equation solver. + MinMaxLinearEquationSolverRequirements requirements(this->linearEquationSolverFactory->getRequirements()); + // If we will use sound value iteration, we require no ECs and an upper bound. + if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::ValueIteration && this->getSettings().getForceSoundness()) { + requirements.requireNoEndComponents(); + requirements.requireGlobalUpperBound(); + } + + // Then add our requirements on top of that. if (equationSystemType == EquationSystemType::UntilProbabilities) { if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration) { if (!direction || direction.get() == OptimizationDirection::Maximize) { - requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler); + requirements.requireValidInitialScheduler(); } } } else if (equationSystemType == EquationSystemType::ReachabilityRewards) { if (!direction || direction.get() == OptimizationDirection::Minimize) { - requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler); + requirements.requireValidInitialScheduler(); } } + if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::ValueIteration && this->getSettings().getForceSoundness()) { + requirements.requireGlobalUpperBound(); + } + return requirements; } @@ -330,6 +361,105 @@ namespace storm { return status == Status::Converged || status == Status::TerminatedEarly; } + template<typename ValueType> + bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsSoundValueIteration(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + STORM_LOG_THROW(this->hasUpperBound(), storm::exceptions::UnmetRequirementException, "Solver requires upper bound, but none was given."); + + if (!this->linEqSolverA) { + this->linEqSolverA = this->linearEquationSolverFactory->create(*this->A); + this->linEqSolverA->setCachingEnabled(true); + } + + if (!auxiliaryRowGroupVector) { + auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount()); + } + + if (this->hasInitialScheduler()) { + // Resolve the nondeterminism according to the initial scheduler. + bool convertToEquationSystem = this->linearEquationSolverFactory->getEquationProblemFormat() == LinearEquationSolverProblemFormat::EquationSystem; + storm::storage::SparseMatrix<ValueType> submatrix = this->A->selectRowsFromRowGroups(this->getInitialScheduler(), convertToEquationSystem); + if (convertToEquationSystem) { + submatrix.convertToEquationSystem(); + } + storm::utility::vector::selectVectorValues<ValueType>(*auxiliaryRowGroupVector, this->getInitialScheduler(), this->A->getRowGroupIndices(), b); + + // Solve the resulting equation system. + auto submatrixSolver = this->linearEquationSolverFactory->create(std::move(submatrix)); + submatrixSolver->setCachingEnabled(true); + if (this->lowerBound) { + submatrixSolver->setLowerBound(this->lowerBound.get()); + } + if (this->upperBound) { + submatrixSolver->setUpperBound(this->upperBound.get()); + } + submatrixSolver->solveEquations(x, *auxiliaryRowGroupVector); + } + + // Allow aliased multiplications. + bool useGaussSeidelMultiplication = this->linEqSolverA->supportsGaussSeidelMultiplication() && settings.getValueIterationMultiplicationStyle() == storm::solver::MultiplicationStyle::GaussSeidel; + + std::vector<ValueType>* lowerX = &x; + std::vector<ValueType>* upperX = this->auxiliaryRowGroupVector.get(); + std::vector<ValueType>* tmp; + if (!useGaussSeidelMultiplication) { + auxiliaryRowGroupVector2 = std::make_unique<std::vector<ValueType>>(lowerX->size()); + tmp = auxiliaryRowGroupVector2.get(); + } + + // Proceed with the iterations as long as the method did not converge or reach the maximum number of iterations. + uint64_t iterations = 0; + + Status status = Status::InProgress; + while (status == Status::InProgress && iterations < this->getSettings().getMaximalNumberOfIterations()) { + // Compute x' = min/max(A*x + b). + if (useGaussSeidelMultiplication) { + this->linEqSolverA->multiplyAndReduceGaussSeidel(dir, this->A->getRowGroupIndices(), *lowerX, &b); + this->linEqSolverA->multiplyAndReduceGaussSeidel(dir, this->A->getRowGroupIndices(), *upperX, &b); + } else { + this->linEqSolverA->multiplyAndReduce(dir, this->A->getRowGroupIndices(), *lowerX, &b, *tmp); + std::swap(lowerX, tmp); + this->linEqSolverA->multiplyAndReduce(dir, this->A->getRowGroupIndices(), *upperX, &b, *tmp); + std::swap(upperX, tmp); + } + + // Determine whether the method converged. + if (storm::utility::vector::equalModuloPrecision<ValueType>(*lowerX, *upperX, storm::utility::convertNumber<ValueType>(2.0) * this->getSettings().getPrecision(), false)) { + status = Status::Converged; + } + + // Update environment variables. + ++iterations; + } + + if (status != Status::Converged) { + status = Status::MaximalIterationsExceeded; + } + + reportStatus(status, iterations); + + // We take the means of the lower and upper bound so we guarantee the desired precision. + storm::utility::vector::applyPointwise(*lowerX, *upperX, *lowerX, [] (ValueType const& a, ValueType const& b) { return (a + b) / storm::utility::convertNumber<ValueType>(2.0); }); + + // Since we shuffled the pointer around, we need to write the actual results to the input/output vector x. + if (&x == tmp) { + std::swap(x, *tmp); + } else if (&x == this->auxiliaryRowGroupVector.get()) { + std::swap(x, *this->auxiliaryRowGroupVector); + } + + // If requested, we store the scheduler for retrieval. + if (this->isTrackSchedulerSet()) { + this->schedulerChoices = std::vector<uint_fast64_t>(this->A->getRowGroupCount()); + this->linEqSolverA->multiplyAndReduce(dir, this->A->getRowGroupIndices(), x, &b, *this->auxiliaryRowGroupVector, &this->schedulerChoices.get()); + } + + if (!this->isCachingEnabled()) { + clearCache(); + } + + return status == Status::Converged; + } + template<typename ValueType> bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsAcyclic(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { uint64_t numGroups = this->A->getRowGroupCount(); @@ -473,6 +603,7 @@ namespace storm { template<typename ValueType> void IterativeMinMaxLinearEquationSolver<ValueType>::clearCache() const { auxiliaryRowGroupVector.reset(); + auxiliaryRowGroupVector2.reset(); rowGroupOrdering.reset(); StandardMinMaxLinearEquationSolver<ValueType>::clearCache(); } diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.h b/src/storm/solver/IterativeMinMaxLinearEquationSolver.h index 81b2d722f..b2b10ef29 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.h +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.h @@ -23,14 +23,17 @@ namespace storm { void setRelativeTerminationCriterion(bool value); void setPrecision(ValueType precision); void setValueIterationMultiplicationStyle(MultiplicationStyle value); - + void setForceSoundness(bool value); + SolutionMethod const& getSolutionMethod() const; uint64_t getMaximalNumberOfIterations() const; ValueType getPrecision() const; bool getRelativeTerminationCriterion() const; MultiplicationStyle getValueIterationMultiplicationStyle() const; + bool getForceSoundness() const; private: + bool forceSoundness; SolutionMethod solutionMethod; uint64_t maximalNumberOfIterations; ValueType precision; @@ -60,6 +63,7 @@ namespace storm { private: bool solveEquationsPolicyIteration(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const; bool solveEquationsValueIteration(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const; + bool solveEquationsSoundValueIteration(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const; bool solveEquationsAcyclic(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const; bool valueImproved(OptimizationDirection dir, ValueType const& value1, ValueType const& value2) const; @@ -72,6 +76,7 @@ namespace storm { // possibly cached data mutable std::unique_ptr<std::vector<ValueType>> auxiliaryRowGroupVector; // A.rowGroupCount() entries + mutable std::unique_ptr<std::vector<ValueType>> auxiliaryRowGroupVector2; // A.rowGroupCount() entries mutable std::unique_ptr<std::vector<uint64_t>> rowGroupOrdering; // A.rowGroupCount() entries Status updateStatusIfNotConverged(Status status, std::vector<ValueType> const& x, uint64_t iterations) const; diff --git a/src/storm/solver/LinearEquationSolver.cpp b/src/storm/solver/LinearEquationSolver.cpp index 2efc90f8f..f9da0758d 100644 --- a/src/storm/solver/LinearEquationSolver.cpp +++ b/src/storm/solver/LinearEquationSolver.cpp @@ -23,6 +23,11 @@ namespace storm { // Intentionally left empty. } + template<typename ValueType> + bool LinearEquationSolver<ValueType>::solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + return this->internalSolveEquations(x, b); + } + template<typename ValueType> void LinearEquationSolver<ValueType>::repeatedMultiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, uint_fast64_t n) const { if (!cachedRowVector) { @@ -101,6 +106,11 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This solver does not support the function 'multiplyAndReduceGaussSeidel'."); } + template<typename ValueType> + LinearEquationSolverRequirements LinearEquationSolver<ValueType>::getRequirements() const { + return LinearEquationSolverRequirements(); + } + template<typename ValueType> void LinearEquationSolver<ValueType>::setCachingEnabled(bool value) const { if(cachingEnabled && !value) { @@ -136,6 +146,26 @@ namespace storm { setUpperBound(upper); } + template<typename ValueType> + bool LinearEquationSolver<ValueType>::hasLowerBound() const { + return static_cast<bool>(lowerBound); + } + + template<typename ValueType> + bool LinearEquationSolver<ValueType>::hasUpperBound() const { + return static_cast<bool>(upperBound); + } + + template<typename ValueType> + ValueType const& LinearEquationSolver<ValueType>::getLowerBound() const { + return lowerBound.get(); + } + + template<typename ValueType> + ValueType const& LinearEquationSolver<ValueType>::getUpperBound() const { + return upperBound.get(); + } + template<typename ValueType> std::unique_ptr<LinearEquationSolver<ValueType>> LinearEquationSolverFactory<ValueType>::create(storm::storage::SparseMatrix<ValueType> const& matrix) const { std::unique_ptr<LinearEquationSolver<ValueType>> solver = this->create(); @@ -155,6 +185,11 @@ namespace storm { return this->create()->getEquationProblemFormat(); } + template<typename ValueType> + LinearEquationSolverRequirements LinearEquationSolverFactory<ValueType>::getRequirements() const { + return this->create()->getRequirements(); + } + template<typename ValueType> std::unique_ptr<LinearEquationSolver<ValueType>> GeneralLinearEquationSolverFactory<ValueType>::create() const { EquationSolverType equationSolver = storm::settings::getModule<storm::settings::modules::CoreSettings>().getEquationSolver(); diff --git a/src/storm/solver/LinearEquationSolver.h b/src/storm/solver/LinearEquationSolver.h index aa14226bf..1aa0a0fa4 100644 --- a/src/storm/solver/LinearEquationSolver.h +++ b/src/storm/solver/LinearEquationSolver.h @@ -7,6 +7,7 @@ #include "storm/solver/AbstractEquationSolver.h" #include "storm/solver/MultiplicationStyle.h" #include "storm/solver/LinearEquationSolverProblemFormat.h" +#include "storm/solver/LinearEquationSolverRequirements.h" #include "storm/solver/OptimizationDirection.h" #include "storm/utility/VectorHelper.h" @@ -47,7 +48,7 @@ namespace storm { * * @return true */ - virtual bool solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const = 0; + bool solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const; /*! * Performs on matrix-vector multiplication x' = A*x + b. @@ -127,6 +128,12 @@ namespace storm { */ virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const = 0; + /*! + * Retrieves the requirements of the solver under the current settings. Note that these requirements only + * apply to solving linear equations and not to the matrix vector multiplications. + */ + virtual LinearEquationSolverRequirements getRequirements() const; + /*! * Sets whether some of the generated data during solver calls should be cached. * This possibly increases the runtime of subsequent calls but also increases memory consumption. @@ -153,12 +160,34 @@ namespace storm { */ void setUpperBound(ValueType const& value); + /*! + * Retrieves the lower bound (if there is any). + */ + ValueType const& getLowerBound() const; + + /*! + * Retrieves the lower bound (if there is any). + */ + ValueType const& getUpperBound() const; + + /*! + * Retrieves whether this solver has a lower bound. + */ + bool hasLowerBound() const; + + /*! + * Retrieves whether this solver has an upper bound. + */ + bool hasUpperBound() const; + /*! * Sets bounds for the solution that can potentially be used by the solver. */ void setBounds(ValueType const& lower, ValueType const& upper); protected: + virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const = 0; + // auxiliary storage. If set, this vector has getMatrixRowCount() entries. mutable std::unique_ptr<std::vector<ValueType>> cachedRowVector; @@ -220,6 +249,12 @@ namespace storm { * Retrieves the problem format that the solver expects if it was created with the current settings. */ virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const; + + /*! + * Retrieves the requirements of the solver if it was created with the current settings. Note that these + * requirements only apply to solving linear equations and not to the matrix vector multiplications. + */ + LinearEquationSolverRequirements getRequirements() const; }; template<typename ValueType> diff --git a/src/storm/solver/LinearEquationSolverRequirements.cpp b/src/storm/solver/LinearEquationSolverRequirements.cpp new file mode 100644 index 000000000..3d1b34ee9 --- /dev/null +++ b/src/storm/solver/LinearEquationSolverRequirements.cpp @@ -0,0 +1,48 @@ +#include "storm/solver/LinearEquationSolverRequirements.h" + +namespace storm { + namespace solver { + + LinearEquationSolverRequirements::LinearEquationSolverRequirements() : globalLowerBound(false), globalUpperBound(false) { + // Intentionally left empty. + } + + LinearEquationSolverRequirements& LinearEquationSolverRequirements::requireGlobalLowerBound() { + globalLowerBound = true; + return *this; + } + + LinearEquationSolverRequirements& LinearEquationSolverRequirements::requireGlobalUpperBound() { + globalUpperBound = true; + return *this; + } + + bool LinearEquationSolverRequirements::requiresGlobalLowerBound() const { + return globalLowerBound; + } + + bool LinearEquationSolverRequirements::requiresGlobalUpperBound() const { + return globalUpperBound; + } + + bool LinearEquationSolverRequirements::requires(Element const& element) const { + switch (element) { + case Element::GlobalLowerBound: return globalLowerBound; break; + case Element::GlobalUpperBound: return globalUpperBound; break; + } + } + + void LinearEquationSolverRequirements::clearGlobalLowerBound() { + globalLowerBound = false; + } + + void LinearEquationSolverRequirements::clearGlobalUpperBound() { + globalUpperBound = false; + } + + bool LinearEquationSolverRequirements::empty() const { + return !globalLowerBound && !globalUpperBound; + } + + } +} diff --git a/src/storm/solver/LinearEquationSolverRequirements.h b/src/storm/solver/LinearEquationSolverRequirements.h new file mode 100644 index 000000000..d07c6a764 --- /dev/null +++ b/src/storm/solver/LinearEquationSolverRequirements.h @@ -0,0 +1,35 @@ +#pragma once + +namespace storm { + namespace solver { + + class LinearEquationSolverRequirements { + public: + // The different requirements a solver can have. + enum class Element { + // Requirements that are related to bounds for the actual solution. + GlobalLowerBound, + GlobalUpperBound + }; + + LinearEquationSolverRequirements(); + + LinearEquationSolverRequirements& requireGlobalLowerBound(); + LinearEquationSolverRequirements& requireGlobalUpperBound(); + + bool requiresGlobalLowerBound() const; + bool requiresGlobalUpperBound() const; + bool requires(Element const& element) const; + + void clearGlobalLowerBound(); + void clearGlobalUpperBound(); + + bool empty() const; + + private: + bool globalLowerBound; + bool globalUpperBound; + }; + + } +} diff --git a/src/storm/solver/MinMaxLinearEquationSolver.cpp b/src/storm/solver/MinMaxLinearEquationSolver.cpp index 5fb4fe38b..a61307047 100644 --- a/src/storm/solver/MinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/MinMaxLinearEquationSolver.cpp @@ -127,6 +127,26 @@ namespace storm { setUpperBound(upper); } + template<typename ValueType> + bool MinMaxLinearEquationSolver<ValueType>::hasUpperBound() const { + return static_cast<bool>(upperBound); + } + + template<typename ValueType> + bool MinMaxLinearEquationSolver<ValueType>::hasLowerBound() const { + return static_cast<bool>(lowerBound); + } + + template<typename ValueType> + ValueType const& MinMaxLinearEquationSolver<ValueType>::getUpperBound() const { + return upperBound.get(); + } + + template<typename ValueType> + ValueType const& MinMaxLinearEquationSolver<ValueType>::getLowerBound() const { + return lowerBound.get(); + } + template<typename ValueType> void MinMaxLinearEquationSolver<ValueType>::setInitialScheduler(std::vector<uint_fast64_t>&& choices) { initialScheduler = std::move(choices); diff --git a/src/storm/solver/MinMaxLinearEquationSolver.h b/src/storm/solver/MinMaxLinearEquationSolver.h index 2d8ee350d..c05b2f13f 100644 --- a/src/storm/solver/MinMaxLinearEquationSolver.h +++ b/src/storm/solver/MinMaxLinearEquationSolver.h @@ -150,7 +150,27 @@ namespace storm { * Sets bounds for the solution that can potentially used by the solver. */ void setBounds(ValueType const& lower, ValueType const& upper); + + /*! + * Retrieves whether the solver has an upper bound. + */ + bool hasUpperBound() const; + + /*! + * Retrieves whether the solver has a lower bound. + */ + bool hasLowerBound() const; + + /*! + * Retrieves the upper bound (if this solver has any). + */ + ValueType const& getUpperBound() const; + /*! + * Retrieves the upper bound (if this solver has any). + */ + ValueType const& getLowerBound() const; + /*! * Sets a valid initial scheduler that is required by some solvers (see requirements of solvers). */ diff --git a/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp b/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp index 07560601d..a90907ff8 100644 --- a/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp +++ b/src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp @@ -3,58 +3,74 @@ namespace storm { namespace solver { - MinMaxLinearEquationSolverRequirements::MinMaxLinearEquationSolverRequirements() : noEndComponents(false), noZeroRewardEndComponents(false), validInitialScheduler(false), globalLowerBound(false), globalUpperBound(false) { + MinMaxLinearEquationSolverRequirements::MinMaxLinearEquationSolverRequirements(LinearEquationSolverRequirements const& linearEquationSolverRequirements) : noEndComponents(false), validInitialScheduler(false), globalLowerBound(linearEquationSolverRequirements.requiresGlobalLowerBound()), globalUpperBound(linearEquationSolverRequirements.requiresGlobalUpperBound()) { // Intentionally left empty. } - MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setNoEndComponents(bool value) { - noEndComponents = value; + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireNoEndComponents() { + noEndComponents = true; return *this; } - MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setNoZeroRewardEndComponents(bool value) { - noZeroRewardEndComponents = value; + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireValidInitialScheduler() { + validInitialScheduler = true; return *this; } - MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setValidInitialScheduler(bool value) { - validInitialScheduler = value; + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireGlobalLowerBound() { + globalLowerBound = true; return *this; } - MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setGlobalLowerBound(bool value) { - globalLowerBound = value; + MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireGlobalUpperBound() { + globalUpperBound = true; return *this; } - MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setGlobalUpperBound(bool value) { - globalUpperBound = value; - return *this; + bool MinMaxLinearEquationSolverRequirements::requiresNoEndComponents() const { + return noEndComponents; } - 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::requiresValidIntialScheduler() const { + return validInitialScheduler; + } + + bool MinMaxLinearEquationSolverRequirements::requiresGlobalLowerBound() const { + return globalLowerBound; + } + + bool MinMaxLinearEquationSolverRequirements::requiresGlobalUpperBound() const { + return globalUpperBound; } - bool MinMaxLinearEquationSolverRequirements::requires(Element const& element) { + bool MinMaxLinearEquationSolverRequirements::requires(Element const& element) const { 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; } } + void MinMaxLinearEquationSolverRequirements::clearNoEndComponents() { + noEndComponents = false; + validInitialScheduler = false; + } + + void MinMaxLinearEquationSolverRequirements::clearValidInitialScheduler() { + validInitialScheduler = false; + } + + void MinMaxLinearEquationSolverRequirements::clearGlobalLowerBound() { + globalLowerBound = false; + } + + void MinMaxLinearEquationSolverRequirements::clearGlobalUpperBound() { + globalUpperBound = false; + } + bool MinMaxLinearEquationSolverRequirements::empty() const { - return !noEndComponents && !noZeroRewardEndComponents && !validInitialScheduler && !globalLowerBound && !globalUpperBound; + return !noEndComponents && !validInitialScheduler && !globalLowerBound && !globalUpperBound; } } diff --git a/src/storm/solver/MinMaxLinearEquationSolverRequirements.h b/src/storm/solver/MinMaxLinearEquationSolverRequirements.h index 6cb4e025c..4dfb9f2aa 100644 --- a/src/storm/solver/MinMaxLinearEquationSolverRequirements.h +++ b/src/storm/solver/MinMaxLinearEquationSolverRequirements.h @@ -1,30 +1,47 @@ #pragma once +#include "storm/solver/LinearEquationSolverRequirements.h" + namespace storm { namespace solver { class MinMaxLinearEquationSolverRequirements { public: + // The different requirements a solver can have. enum class Element { - NoEndComponents, NoZeroRewardEndComponents, ValidInitialScheduler, GlobalLowerBound, GlobalUpperBound + // Requirements that are related to the graph structure of the system. Note that the requirements in this + // category are to be interpreted incrementally in the following sense: whenever the system has no end + // components then automatically both requirements are fulfilled. + NoEndComponents, + ValidInitialScheduler, + + // Requirements that are related to bounds for the actual solution. + GlobalLowerBound, + GlobalUpperBound }; - MinMaxLinearEquationSolverRequirements(); + MinMaxLinearEquationSolverRequirements(LinearEquationSolverRequirements const& linearEquationSolverRequirements = LinearEquationSolverRequirements()); - 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& requireNoEndComponents(); + MinMaxLinearEquationSolverRequirements& requireValidInitialScheduler(); + MinMaxLinearEquationSolverRequirements& requireGlobalLowerBound(); + MinMaxLinearEquationSolverRequirements& requireGlobalUpperBound(); + + bool requiresNoEndComponents() const; + bool requiresValidIntialScheduler() const; + bool requiresGlobalLowerBound() const; + bool requiresGlobalUpperBound() const; + bool requires(Element const& element) const; - bool requires(Element const& element); + void clearNoEndComponents(); + void clearValidInitialScheduler(); + void clearGlobalLowerBound(); + void clearGlobalUpperBound(); bool empty() const; private: bool noEndComponents; - bool noZeroRewardEndComponents; bool validInitialScheduler; bool globalLowerBound; bool globalUpperBound; diff --git a/src/storm/solver/NativeLinearEquationSolver.cpp b/src/storm/solver/NativeLinearEquationSolver.cpp index fa2dbc237..30fe62228 100644 --- a/src/storm/solver/NativeLinearEquationSolver.cpp +++ b/src/storm/solver/NativeLinearEquationSolver.cpp @@ -3,12 +3,14 @@ #include <utility> #include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/NativeEquationSolverSettings.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" #include "storm/exceptions/InvalidStateException.h" #include "storm/exceptions/InvalidSettingsException.h" +#include "storm/exceptions/UnmetRequirementException.h" namespace storm { namespace solver { @@ -28,22 +30,26 @@ namespace storm { method = SolutionMethod::WalkerChae; } else if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationMethod::Power) { method = SolutionMethod::Power; - } else if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationMethod::SoundPower) { - method = SolutionMethod::SoundPower; } else { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "The selected solution technique is invalid for this solver."); } - + maximalNumberOfIterations = settings.getMaximalIterationCount(); precision = settings.getPrecision(); relative = settings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Relative; omega = settings.getOmega(); multiplicationStyle = settings.getPowerMethodMultiplicationStyle(); + + // Finally force soundness and potentially overwrite some other settings. + this->setForceSoundness(storm::settings::getModule<storm::settings::modules::GeneralSettings>().isSoundSet()); } template<typename ValueType> void NativeLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) { this->method = method; + + // Make sure we switch the method if we have to guarantee soundness. + this->setForceSoundness(forceSoundness); } template<typename ValueType> @@ -71,6 +77,15 @@ namespace storm { this->multiplicationStyle = value; } + template<typename ValueType> + void NativeLinearEquationSolverSettings<ValueType>::setForceSoundness(bool value) { + forceSoundness = value; + if (forceSoundness) { + STORM_LOG_WARN_COND(method != SolutionMethod::Power, "To guarantee soundness, the equation solving technique has been switched to '" << storm::settings::modules::NativeEquationSolverSettings::LinearEquationMethod::Power << "'."); + method = SolutionMethod::Power; + } + } + template<typename ValueType> typename NativeLinearEquationSolverSettings<ValueType>::SolutionMethod NativeLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { return method; @@ -101,6 +116,11 @@ namespace storm { return multiplicationStyle; } + template<typename ValueType> + bool NativeLinearEquationSolverSettings<ValueType>::getForceSoundness() const { + return forceSoundness; + } + template<typename ValueType> NativeLinearEquationSolver<ValueType>::NativeLinearEquationSolver(NativeLinearEquationSolverSettings<ValueType> const& settings) : localA(nullptr), A(nullptr), settings(settings) { // Intentionally left empty. @@ -410,11 +430,69 @@ namespace storm { template<typename ValueType> bool NativeLinearEquationSolver<ValueType>::solveEquationsSoundPower(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { - // TODO + STORM_LOG_THROW(this->hasUpperBound(), storm::exceptions::UnmetRequirementException, "Solver requires upper bound, but none was given."); + STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with NativeLinearEquationSolver (SoundPower)"); + + std::vector<ValueType>* lowerX = &x; + if (!this->cachedRowVector) { + this->cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount(), this->getUpperBound()); + } + std::vector<ValueType>* upperX = this->cachedRowVector.get(); + + bool useGaussSeidelMultiplication = this->getSettings().getPowerMethodMultiplicationStyle() == storm::solver::MultiplicationStyle::GaussSeidel; + std::vector<ValueType>* tmp; + if (!useGaussSeidelMultiplication) { + cachedRowVector2 = std::make_unique<std::vector<ValueType>>(x.size()); + tmp = cachedRowVector2.get(); + } + + bool converged = false; + uint64_t iterations = 0; + while (!converged && iterations < this->getSettings().getMaximalNumberOfIterations()) { + if (useGaussSeidelMultiplication) { + this->multiplier.multAddGaussSeidelBackward(*this->A, *lowerX, &b); + this->multiplier.multAddGaussSeidelBackward(*this->A, *upperX, &b); + } else { + this->multiplier.multAdd(*this->A, *lowerX, &b, *tmp); + std::swap(tmp, lowerX); + this->multiplier.multAdd(*this->A, *upperX, &b, *tmp); + std::swap(tmp, upperX); + } + + // Now check if the process already converged within our precision. Note that we double the target + // precision here. Doing so, we need to take the means of the lower and upper values later to guarantee + // the original precision. + converged = storm::utility::vector::equalModuloPrecision<ValueType>(*lowerX, *upperX, storm::utility::convertNumber<ValueType>(2.0) * static_cast<ValueType>(this->getSettings().getPrecision()), false); + + // Set up next iteration. + ++iterations; + } + + // We take the means of the lower and upper bound so we guarantee the desired precision. + storm::utility::vector::applyPointwise(*lowerX, *upperX, *lowerX, [] (ValueType const& a, ValueType const& b) { return (a + b) / storm::utility::convertNumber<ValueType>(2.0); }); + + // Since we shuffled the pointer around, we need to write the actual results to the input/output vector x. + if (&x == tmp) { + std::swap(x, *tmp); + } else if (&x == this->cachedRowVector.get()) { + std::swap(x, *this->cachedRowVector); + } + + if (!this->isCachingEnabled()) { + clearCache(); + } + + if (converged) { + STORM_LOG_INFO("Iterative solver converged in " << iterations << " iterations."); + } else { + STORM_LOG_WARN("Iterative solver did not converge in " << iterations << " iterations."); + } + + return converged; } template<typename ValueType> - bool NativeLinearEquationSolver<ValueType>::solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + bool NativeLinearEquationSolver<ValueType>::internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::SOR || this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::GaussSeidel) { return this->solveEquationsSOR(x, b, this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::SOR ? this->getSettings().getOmega() : storm::utility::one<ValueType>()); } else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::Jacobi) { @@ -422,7 +500,11 @@ namespace storm { } else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::WalkerChae) { return this->solveEquationsWalkerChae(x, b); } else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::Power) { - return this->solveEquationsPower(x, b); + if (this->getSettings().getForceSoundness()) { + return this->solveEquationsSoundPower(x, b); + } else { + return this->solveEquationsPower(x, b); + } } STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unknown solving technique."); @@ -495,7 +577,7 @@ namespace storm { template<typename ValueType> LinearEquationSolverProblemFormat NativeLinearEquationSolver<ValueType>::getEquationProblemFormat() const { - if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::Power || this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::SoundPower) { + if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::Power) { return LinearEquationSolverProblemFormat::FixedPointSystem; } else { return LinearEquationSolverProblemFormat::EquationSystem; @@ -505,6 +587,7 @@ namespace storm { template<typename ValueType> void NativeLinearEquationSolver<ValueType>::clearCache() const { jacobiDecomposition.reset(); + cachedRowVector2.reset(); walkerChaeData.reset(); LinearEquationSolver<ValueType>::clearCache(); } diff --git a/src/storm/solver/NativeLinearEquationSolver.h b/src/storm/solver/NativeLinearEquationSolver.h index 678aab555..c252b86b3 100644 --- a/src/storm/solver/NativeLinearEquationSolver.h +++ b/src/storm/solver/NativeLinearEquationSolver.h @@ -14,7 +14,7 @@ namespace storm { class NativeLinearEquationSolverSettings { public: enum class SolutionMethod { - Jacobi, GaussSeidel, SOR, WalkerChae, Power, SoundPower + Jacobi, GaussSeidel, SOR, WalkerChae, Power }; NativeLinearEquationSolverSettings(); @@ -25,15 +25,18 @@ namespace storm { void setRelativeTerminationCriterion(bool value); void setOmega(ValueType omega); void setPowerMethodMultiplicationStyle(MultiplicationStyle value); - + void setForceSoundness(bool value); + SolutionMethod getSolutionMethod() const; ValueType getPrecision() const; uint64_t getMaximalNumberOfIterations() const; uint64_t getRelativeTerminationCriterion() const; ValueType getOmega() const; MultiplicationStyle getPowerMethodMultiplicationStyle() const; + bool getForceSoundness() const; private: + bool forceSoundness; SolutionMethod method; double precision; bool relative; @@ -55,7 +58,6 @@ namespace storm { virtual void setMatrix(storm::storage::SparseMatrix<ValueType> const& A) override; virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& A) override; - virtual bool solveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; virtual void multiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result) const override; virtual void multiplyAndReduce(OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices, std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result, std::vector<uint_fast64_t>* choices = nullptr) const override; virtual bool supportsGaussSeidelMultiplication() const override; @@ -69,6 +71,9 @@ namespace storm { virtual void clearCache() const override; + protected: + virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override; + private: virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixColumnCount() const override; @@ -95,6 +100,7 @@ namespace storm { // cached auxiliary data mutable std::unique_ptr<std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>> jacobiDecomposition; + mutable std::unique_ptr<std::vector<ValueType>> cachedRowVector2; // A.getRowCount() rows struct WalkerChaeData { WalkerChaeData(storm::storage::SparseMatrix<ValueType> const& originalMatrix, std::vector<ValueType> const& originalB); diff --git a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp index b4449c45b..03f603337 100644 --- a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp @@ -263,12 +263,12 @@ namespace storm { if (equationSystemType == EquationSystemType::UntilProbabilities) { if (this->getSettings().getSolutionMethod() == SymbolicMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration) { if (!direction || direction.get() == OptimizationDirection::Maximize) { - requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler); + requirements.requireValidInitialScheduler(); } } } else if (equationSystemType == EquationSystemType::ReachabilityRewards) { if (!direction || direction.get() == OptimizationDirection::Minimize) { - requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler); + requirements.requireValidInitialScheduler(); } }