Browse Source

first version of sound value iteration

main
dehnert 8 years ago
parent
commit
d25cc4b05f
  1. 12
      src/storm/exceptions/InvalidSolverSettingsException.h
  2. 12
      src/storm/exceptions/UnmetRequirementException.h
  3. 5
      src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp
  4. 15
      src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp
  5. 4
      src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp
  6. 9
      src/storm/settings/modules/EigenEquationSolverSettings.cpp
  7. 2
      src/storm/settings/modules/EigenEquationSolverSettings.h
  8. 6
      src/storm/settings/modules/GeneralSettings.cpp
  9. 14
      src/storm/settings/modules/GeneralSettings.h
  10. 14
      src/storm/settings/modules/NativeEquationSolverSettings.cpp
  11. 4
      src/storm/settings/modules/NativeEquationSolverSettings.h
  12. 43
      src/storm/solver/EigenLinearEquationSolver.cpp
  13. 11
      src/storm/solver/EigenLinearEquationSolver.h
  14. 4
      src/storm/solver/EliminationLinearEquationSolver.cpp
  15. 4
      src/storm/solver/EliminationLinearEquationSolver.h
  16. 29
      src/storm/solver/GmmxxLinearEquationSolver.cpp
  17. 10
      src/storm/solver/GmmxxLinearEquationSolver.h
  18. 139
      src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
  19. 7
      src/storm/solver/IterativeMinMaxLinearEquationSolver.h
  20. 35
      src/storm/solver/LinearEquationSolver.cpp
  21. 37
      src/storm/solver/LinearEquationSolver.h
  22. 48
      src/storm/solver/LinearEquationSolverRequirements.cpp
  23. 35
      src/storm/solver/LinearEquationSolverRequirements.h
  24. 20
      src/storm/solver/MinMaxLinearEquationSolver.cpp
  25. 20
      src/storm/solver/MinMaxLinearEquationSolver.h
  26. 64
      src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp
  27. 37
      src/storm/solver/MinMaxLinearEquationSolverRequirements.h
  28. 97
      src/storm/solver/NativeLinearEquationSolver.cpp
  29. 12
      src/storm/solver/NativeLinearEquationSolver.h
  30. 4
      src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp

12
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

12
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

5
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."); STORM_LOG_DEBUG("Computing valid scheduler hint, because the solver requires it.");
initialScheduler = computeValidInitialSchedulerForUntilProbabilities<ValueType>(explicitRepresentation.first, explicitRepresentation.second); 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."); STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Cannot establish requirements for solver.");
} }
@ -126,6 +126,7 @@ namespace storm {
if (initialScheduler) { if (initialScheduler) {
solver->setInitialScheduler(std::move(initialScheduler.get())); solver->setInitialScheduler(std::move(initialScheduler.get()));
} }
solver->setBounds(storm::utility::zero<ValueType>(), storm::utility::one<ValueType>());
solver->setRequirementsChecked(); solver->setRequirementsChecked();
solver->solveEquations(dir, x, explicitRepresentation.second); solver->solveEquations(dir, x, explicitRepresentation.second);
@ -357,7 +358,7 @@ namespace storm {
if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) {
STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it.");
requireInitialScheduler = true; 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."); STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Cannot establish requirements for solver.");
} }

15
src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp

@ -207,7 +207,7 @@ namespace storm {
if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) {
STORM_LOG_DEBUG("Computing valid scheduler, 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); 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."); 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()); bool skipEcWithinMaybeStatesCheck = dir == storm::OptimizationDirection::Minimize || (hint.isExplicitModelCheckerHint() && hint.asExplicitModelCheckerHint<ValueType>().getNoEndComponentsInMaybeStates());
extractHintInformationForMaybeStates(result, transitionMatrix, backwardTransitions, maybeStates, selectedChoices, hint, skipEcWithinMaybeStatesCheck); 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>(); 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; return result;
} }

4
src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp

@ -88,7 +88,7 @@ namespace storm {
if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) {
STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it.");
initialScheduler = computeValidSchedulerHint(storm::solver::EquationSystemType::UntilProbabilities, model, transitionMatrix, maybeStates, statesWithProbability01.second); 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."); 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)) { if (requirements.requires(storm::solver::MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler)) {
STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it."); STORM_LOG_DEBUG("Computing valid scheduler, because the solver requires it.");
initialScheduler = computeValidSchedulerHint(storm::solver::EquationSystemType::ReachabilityRewards, model, transitionMatrix, maybeStates, targetStates); 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."); STORM_LOG_THROW(requirements.empty(), storm::exceptions::UncheckedRequirementException, "Could not establish requirements of solver.");
} }

9
src/storm/settings/modules/EigenEquationSolverSettings.cpp

@ -106,6 +106,15 @@ namespace storm {
return true; 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 modules
} // namespace settings } // namespace settings
} // namespace storm } // namespace storm

2
src/storm/settings/modules/EigenEquationSolverSettings.h

@ -110,6 +110,8 @@ namespace storm {
static const std::string restartOptionName; static const std::string restartOptionName;
}; };
std::ostream& operator<<(std::ostream& out, EigenEquationSolverSettings::LinearEquationMethod const& method);
} // namespace modules } // namespace modules
} // namespace settings } // namespace settings
} // namespace storm } // namespace storm

6
src/storm/settings/modules/GeneralSettings.cpp

@ -30,6 +30,7 @@ namespace storm {
const std::string GeneralSettings::bisimulationOptionShortName = "bisim"; const std::string GeneralSettings::bisimulationOptionShortName = "bisim";
const std::string GeneralSettings::parametricOptionName = "parametric"; const std::string GeneralSettings::parametricOptionName = "parametric";
const std::string GeneralSettings::exactOptionName = "exact"; const std::string GeneralSettings::exactOptionName = "exact";
const std::string GeneralSettings::soundOptionName = "sound";
GeneralSettings::GeneralSettings() : ModuleSettings(moduleName) { GeneralSettings::GeneralSettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, helpOptionName, false, "Shows all available options, arguments and descriptions.").setShortName(helpOptionShortName) 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, 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, 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, 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 { bool GeneralSettings::isHelpSet() const {
@ -86,6 +88,10 @@ namespace storm {
return this->getOption(exactOptionName).getHasOptionBeenSet(); return this->getOption(exactOptionName).getHasOptionBeenSet();
} }
bool GeneralSettings::isSoundSet() const {
return this->getOption(soundOptionName).getHasOptionBeenSet();
}
void GeneralSettings::finalize() { void GeneralSettings::finalize() {
// Intentionally left empty. // Intentionally left empty.
} }

14
src/storm/settings/modules/GeneralSettings.h

@ -84,20 +84,20 @@ namespace storm {
* @return True iff the option was set. * @return True iff the option was set.
*/ */
bool isParametricSet() const; 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. * @return True iff the option was set.
*/ */
bool isExactSet() const;
bool isSoundSet() const;
bool check() const override; bool check() const override;
void finalize() override; void finalize() override;
@ -121,8 +121,8 @@ namespace storm {
static const std::string bisimulationOptionName; static const std::string bisimulationOptionName;
static const std::string bisimulationOptionShortName; static const std::string bisimulationOptionShortName;
static const std::string parametricOptionName; static const std::string parametricOptionName;
static const std::string exactOptionName; static const std::string exactOptionName;
static const std::string soundOptionName;
}; };
} // namespace modules } // namespace modules

14
src/storm/settings/modules/NativeEquationSolverSettings.cpp

@ -25,7 +25,7 @@ namespace storm {
const std::string NativeEquationSolverSettings::powerMethodMultiplicationStyleOptionName = "powmult"; const std::string NativeEquationSolverSettings::powerMethodMultiplicationStyleOptionName = "powmult";
NativeEquationSolverSettings::NativeEquationSolverSettings() : ModuleSettings(moduleName) { 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, 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()); 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; return NativeEquationSolverSettings::LinearEquationMethod::WalkerChae;
} else if (linearEquationSystemTechniqueAsString == "power") { } else if (linearEquationSystemTechniqueAsString == "power") {
return NativeEquationSolverSettings::LinearEquationMethod::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."); STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown solution technique '" << linearEquationSystemTechniqueAsString << "' selected.");
} }
@ -114,6 +112,16 @@ namespace storm {
return true; 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 modules
} // namespace settings } // namespace settings
} // namespace storm } // namespace storm

4
src/storm/settings/modules/NativeEquationSolverSettings.h

@ -15,7 +15,7 @@ namespace storm {
class NativeEquationSolverSettings : public ModuleSettings { class NativeEquationSolverSettings : public ModuleSettings {
public: public:
// An enumeration of all available methods for solving linear equations. // 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. // An enumeration of all available convergence criteria.
enum class ConvergenceCriterion { Absolute, Relative }; enum class ConvergenceCriterion { Absolute, Relative };
@ -118,6 +118,8 @@ namespace storm {
static const std::string powerMethodMultiplicationStyleOptionName; static const std::string powerMethodMultiplicationStyleOptionName;
}; };
std::ostream& operator<<(std::ostream& out, NativeEquationSolverSettings::LinearEquationMethod const& method);
} // namespace modules } // namespace modules
} // namespace settings } // namespace settings
} // namespace storm } // namespace storm

43
src/storm/solver/EigenLinearEquationSolver.cpp

@ -3,6 +3,7 @@
#include "storm/adapters/EigenAdapter.h" #include "storm/adapters/EigenAdapter.h"
#include "storm/settings/SettingsManager.h" #include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/GeneralSettings.h"
#include "storm/settings/modules/EigenEquationSolverSettings.h" #include "storm/settings/modules/EigenEquationSolverSettings.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
@ -16,12 +17,7 @@ namespace storm {
EigenLinearEquationSolverSettings<ValueType>::EigenLinearEquationSolverSettings() { EigenLinearEquationSolverSettings<ValueType>::EigenLinearEquationSolverSettings() {
// Get the settings object to customize linear solving. // Get the settings object to customize linear solving.
storm::settings::modules::EigenEquationSolverSettings const& settings = storm::settings::getModule<storm::settings::modules::EigenEquationSolverSettings>(); 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. // Determine the method to be used.
storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod methodAsSetting = settings.getLinearEquationSystemMethod(); storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod methodAsSetting = settings.getLinearEquationSystemMethod();
if (methodAsSetting == storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod::BiCGSTAB) { if (methodAsSetting == storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod::BiCGSTAB) {
@ -33,7 +29,7 @@ namespace storm {
} else if (methodAsSetting == storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod::GMRES) { } else if (methodAsSetting == storm::settings::modules::EigenEquationSolverSettings::LinearEquationMethod::GMRES) {
method = SolutionMethod::GMRES; method = SolutionMethod::GMRES;
} }
// Check which preconditioner to use. // Check which preconditioner to use.
storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod preconditionAsSetting = settings.getPreconditioningMethod(); storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod preconditionAsSetting = settings.getPreconditioningMethod();
if (preconditionAsSetting == storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod::Ilu) { if (preconditionAsSetting == storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod::Ilu) {
@ -43,11 +39,22 @@ namespace storm {
} else if (preconditionAsSetting == storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod::None) { } else if (preconditionAsSetting == storm::settings::modules::EigenEquationSolverSettings::PreconditioningMethod::None) {
preconditioner = Preconditioner::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> template<typename ValueType>
void EigenLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) { void EigenLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) {
this->method = method; this->method = method;
// Make sure we switch the method if we have to guarantee soundness.
this->setForceSoundness(forceSoundness);
} }
template<typename ValueType> template<typename ValueType>
@ -69,6 +76,15 @@ namespace storm {
void EigenLinearEquationSolverSettings<ValueType>::setNumberOfIterationsUntilRestart(uint64_t restart) { void EigenLinearEquationSolverSettings<ValueType>::setNumberOfIterationsUntilRestart(uint64_t restart) {
this->restart = 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> template<typename ValueType>
typename EigenLinearEquationSolverSettings<ValueType>::SolutionMethod EigenLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { typename EigenLinearEquationSolverSettings<ValueType>::SolutionMethod EigenLinearEquationSolverSettings<ValueType>::getSolutionMethod() const {
@ -95,6 +111,11 @@ namespace storm {
return restart; return restart;
} }
template<typename ValueType>
bool EigenLinearEquationSolverSettings<ValueType>::getForceSoundness() const {
return forceSoundness;
}
#ifdef STORM_HAVE_CARL #ifdef STORM_HAVE_CARL
EigenLinearEquationSolverSettings<storm::RationalNumber>::EigenLinearEquationSolverSettings() { EigenLinearEquationSolverSettings<storm::RationalNumber>::EigenLinearEquationSolverSettings() {
// Intentionally left empty. // Intentionally left empty.
@ -135,7 +156,7 @@ namespace storm {
} }
template<typename ValueType> 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. // Map the input vectors to Eigen's format.
auto eigenX = StormEigen::Matrix<ValueType, StormEigen::Dynamic, 1>::Map(x.data(), x.size()); 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()); auto eigenB = StormEigen::Matrix<ValueType, StormEigen::Dynamic, 1>::Map(b.data(), b.size());
@ -306,7 +327,7 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
LinearEquationSolverProblemFormat EigenLinearEquationSolver<ValueType>::getEquationProblemFormat() const { LinearEquationSolverProblemFormat EigenLinearEquationSolver<ValueType>::getEquationProblemFormat() const {
LinearEquationSolverProblemFormat::EquationSystem;
return LinearEquationSolverProblemFormat::EquationSystem;
} }
template<typename ValueType> template<typename ValueType>
@ -327,7 +348,7 @@ namespace storm {
#ifdef STORM_HAVE_CARL #ifdef STORM_HAVE_CARL
// Specialization for storm::RationalNumber // Specialization for storm::RationalNumber
template<> 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)."); 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. // Map the input vectors to Eigen's format.
@ -342,7 +363,7 @@ namespace storm {
// Specialization for storm::RationalFunction // Specialization for storm::RationalFunction
template<> 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)."); 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. // Map the input vectors to Eigen's format.

11
src/storm/solver/EigenLinearEquationSolver.h

@ -25,14 +25,17 @@ namespace storm {
void setPrecision(ValueType precision); void setPrecision(ValueType precision);
void setMaximalNumberOfIterations(uint64_t maximalNumberOfIterations); void setMaximalNumberOfIterations(uint64_t maximalNumberOfIterations);
void setNumberOfIterationsUntilRestart(uint64_t restart); void setNumberOfIterationsUntilRestart(uint64_t restart);
void setForceSoundness(bool value);
SolutionMethod getSolutionMethod() const; SolutionMethod getSolutionMethod() const;
Preconditioner getPreconditioner() const; Preconditioner getPreconditioner() const;
ValueType getPrecision() const; ValueType getPrecision() const;
uint64_t getMaximalNumberOfIterations() const; uint64_t getMaximalNumberOfIterations() const;
uint64_t getNumberOfIterationsUntilRestart() const; uint64_t getNumberOfIterationsUntilRestart() const;
bool getForceSoundness() const;
private: private:
bool forceSoundness;
SolutionMethod method; SolutionMethod method;
Preconditioner preconditioner; Preconditioner preconditioner;
double precision; 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> const& A) override;
virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& 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 multiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result) const override;
EigenLinearEquationSolverSettings<ValueType>& getSettings(); EigenLinearEquationSolverSettings<ValueType>& getSettings();
EigenLinearEquationSolverSettings<ValueType> const& getSettings() const; EigenLinearEquationSolverSettings<ValueType> const& getSettings() const;
virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const override; virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const override;
protected:
virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override;
private: private:
virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixRowCount() const override;
virtual uint64_t getMatrixColumnCount() const override; virtual uint64_t getMatrixColumnCount() const override;

4
src/storm/solver/EliminationLinearEquationSolver.cpp

@ -63,9 +63,9 @@ namespace storm {
} }
template<typename ValueType> 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 // 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. // but arbitrary matrices require pivoting, which is not currently implemented.
STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with elimination"); STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with elimination");

4
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> const& A) override;
virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& 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 multiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, std::vector<ValueType>& result) const override;
EliminationLinearEquationSolverSettings<ValueType>& getSettings(); EliminationLinearEquationSolverSettings<ValueType>& getSettings();
EliminationLinearEquationSolverSettings<ValueType> const& getSettings() const; EliminationLinearEquationSolverSettings<ValueType> const& getSettings() const;
virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const override; virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const override;
protected:
virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override;
private: private:
void initializeSettings(); void initializeSettings();

29
src/storm/solver/GmmxxLinearEquationSolver.cpp

@ -3,17 +3,19 @@
#include <cmath> #include <cmath>
#include <utility> #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/adapters/GmmxxAdapter.h"
#include "storm/solver/GmmxxMultiplier.h" #include "storm/solver/GmmxxMultiplier.h"
#include "storm/solver/NativeLinearEquationSolver.h"
#include "storm/settings/SettingsManager.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
#include "storm/utility/constants.h" #include "storm/utility/constants.h"
#include "storm/exceptions/InvalidStateException.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/gmm.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
@ -49,11 +51,17 @@ namespace storm {
} else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod::None) { } else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod::None) {
preconditioner = Preconditioner::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> template<typename ValueType>
void GmmxxLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) { void GmmxxLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) {
this->method = method; this->method = method;
// Make sure we switch the method if we have to guarantee soundness.
this->setForceSoundness(forceSoundness);
} }
template<typename ValueType> template<typename ValueType>
@ -76,6 +84,12 @@ namespace storm {
this->restart = restart; 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> template<typename ValueType>
typename GmmxxLinearEquationSolverSettings<ValueType>::SolutionMethod GmmxxLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { typename GmmxxLinearEquationSolverSettings<ValueType>::SolutionMethod GmmxxLinearEquationSolverSettings<ValueType>::getSolutionMethod() const {
return method; return method;
@ -101,6 +115,11 @@ namespace storm {
return restart; return restart;
} }
template<typename ValueType>
bool GmmxxLinearEquationSolverSettings<ValueType>::getForceSoundness() const {
return forceSoundness;
}
template<typename ValueType> template<typename ValueType>
GmmxxLinearEquationSolver<ValueType>::GmmxxLinearEquationSolver(GmmxxLinearEquationSolverSettings<ValueType> const& settings) : settings(settings) { GmmxxLinearEquationSolver<ValueType>::GmmxxLinearEquationSolver(GmmxxLinearEquationSolverSettings<ValueType> const& settings) : settings(settings) {
// Intentionally left empty. // Intentionally left empty.
@ -129,7 +148,7 @@ namespace storm {
} }
template<typename ValueType> 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 method = this->getSettings().getSolutionMethod();
auto preconditioner = this->getSettings().getPreconditioner(); 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)."); 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).");

10
src/storm/solver/GmmxxLinearEquationSolver.h

@ -19,7 +19,6 @@ namespace storm {
enum class Preconditioner { enum class Preconditioner {
Ilu, Diagonal, None Ilu, Diagonal, None
}; };
friend std::ostream& operator<<(std::ostream& out, Preconditioner const& preconditioner) { friend std::ostream& operator<<(std::ostream& out, Preconditioner const& preconditioner) {
switch (preconditioner) { switch (preconditioner) {
@ -51,14 +50,19 @@ namespace storm {
void setPrecision(ValueType precision); void setPrecision(ValueType precision);
void setMaximalNumberOfIterations(uint64_t maximalNumberOfIterations); void setMaximalNumberOfIterations(uint64_t maximalNumberOfIterations);
void setNumberOfIterationsUntilRestart(uint64_t restart); void setNumberOfIterationsUntilRestart(uint64_t restart);
void setForceSoundness(bool value);
SolutionMethod getSolutionMethod() const; SolutionMethod getSolutionMethod() const;
Preconditioner getPreconditioner() const; Preconditioner getPreconditioner() const;
ValueType getPrecision() const; ValueType getPrecision() const;
uint64_t getMaximalNumberOfIterations() const; uint64_t getMaximalNumberOfIterations() const;
uint64_t getNumberOfIterationsUntilRestart() const; uint64_t getNumberOfIterationsUntilRestart() const;
bool getForceSoundness() const;
private: private:
// Whether or not we are forced to be sound.
bool forceSoundness;
// The method to use for solving linear equation systems. // The method to use for solving linear equation systems.
SolutionMethod method; 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> const& A) override;
virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& 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 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 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; virtual bool supportsGaussSeidelMultiplication() const override;
@ -102,6 +105,9 @@ namespace storm {
virtual void clearCache() const override; virtual void clearCache() const override;
protected:
virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override;
private: private:
virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixRowCount() const override;
virtual uint64_t getMatrixColumnCount() const override; virtual uint64_t getMatrixColumnCount() const override;

139
src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp

@ -1,12 +1,14 @@
#include "storm/solver/IterativeMinMaxLinearEquationSolver.h" #include "storm/solver/IterativeMinMaxLinearEquationSolver.h"
#include "storm/settings/SettingsManager.h" #include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/GeneralSettings.h"
#include "storm/settings/modules/MinMaxEquationSolverSettings.h" #include "storm/settings/modules/MinMaxEquationSolverSettings.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
#include "storm/utility/macros.h" #include "storm/utility/macros.h"
#include "storm/exceptions/InvalidSettingsException.h" #include "storm/exceptions/InvalidSettingsException.h"
#include "storm/exceptions/InvalidStateException.h" #include "storm/exceptions/InvalidStateException.h"
#include "storm/exceptions/UnmetRequirementException.h"
namespace storm { namespace storm {
namespace solver { namespace solver {
@ -22,6 +24,9 @@ namespace storm {
valueIterationMultiplicationStyle = minMaxSettings.getValueIterationMultiplicationStyle(); valueIterationMultiplicationStyle = minMaxSettings.getValueIterationMultiplicationStyle();
setSolutionMethod(minMaxSettings.getMinMaxEquationSolvingMethod()); 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> template<typename ValueType>
@ -60,6 +65,11 @@ namespace storm {
this->valueIterationMultiplicationStyle = value; this->valueIterationMultiplicationStyle = value;
} }
template<typename ValueType>
void IterativeMinMaxLinearEquationSolverSettings<ValueType>::setForceSoundness(bool value) {
this->forceSoundness = value;
}
template<typename ValueType> template<typename ValueType>
typename IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod const& IterativeMinMaxLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { typename IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod const& IterativeMinMaxLinearEquationSolverSettings<ValueType>::getSolutionMethod() const {
return solutionMethod; return solutionMethod;
@ -84,6 +94,11 @@ namespace storm {
MultiplicationStyle IterativeMinMaxLinearEquationSolverSettings<ValueType>::getValueIterationMultiplicationStyle() const { MultiplicationStyle IterativeMinMaxLinearEquationSolverSettings<ValueType>::getValueIterationMultiplicationStyle() const {
return valueIterationMultiplicationStyle; return valueIterationMultiplicationStyle;
} }
template<typename ValueType>
bool IterativeMinMaxLinearEquationSolverSettings<ValueType>::getForceSoundness() const {
return forceSoundness;
}
template<typename ValueType> template<typename ValueType>
IterativeMinMaxLinearEquationSolver<ValueType>::IterativeMinMaxLinearEquationSolver(std::unique_ptr<LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory, IterativeMinMaxLinearEquationSolverSettings<ValueType> const& settings) : StandardMinMaxLinearEquationSolver<ValueType>(std::move(linearEquationSolverFactory)), settings(settings) { 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 { bool IterativeMinMaxLinearEquationSolver<ValueType>::internalSolveEquations(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
switch (this->getSettings().getSolutionMethod()) { switch (this->getSettings().getSolutionMethod()) {
case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::ValueIteration: 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: case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration:
return solveEquationsPolicyIteration(dir, x, b); return solveEquationsPolicyIteration(dir, x, b);
case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::Acyclic: case IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::Acyclic:
@ -229,20 +248,32 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
MinMaxLinearEquationSolverRequirements IterativeMinMaxLinearEquationSolver<ValueType>::getRequirements(EquationSystemType const& equationSystemType, boost::optional<storm::solver::OptimizationDirection> const& direction) const { 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 (equationSystemType == EquationSystemType::UntilProbabilities) {
if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration) { if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration) {
if (!direction || direction.get() == OptimizationDirection::Maximize) { if (!direction || direction.get() == OptimizationDirection::Maximize) {
requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler);
requirements.requireValidInitialScheduler();
} }
} }
} else if (equationSystemType == EquationSystemType::ReachabilityRewards) { } else if (equationSystemType == EquationSystemType::ReachabilityRewards) {
if (!direction || direction.get() == OptimizationDirection::Minimize) { 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; return requirements;
} }
@ -330,6 +361,105 @@ namespace storm {
return status == Status::Converged || status == Status::TerminatedEarly; 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> template<typename ValueType>
bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsAcyclic(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsAcyclic(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
uint64_t numGroups = this->A->getRowGroupCount(); uint64_t numGroups = this->A->getRowGroupCount();
@ -473,6 +603,7 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
void IterativeMinMaxLinearEquationSolver<ValueType>::clearCache() const { void IterativeMinMaxLinearEquationSolver<ValueType>::clearCache() const {
auxiliaryRowGroupVector.reset(); auxiliaryRowGroupVector.reset();
auxiliaryRowGroupVector2.reset();
rowGroupOrdering.reset(); rowGroupOrdering.reset();
StandardMinMaxLinearEquationSolver<ValueType>::clearCache(); StandardMinMaxLinearEquationSolver<ValueType>::clearCache();
} }

7
src/storm/solver/IterativeMinMaxLinearEquationSolver.h

@ -23,14 +23,17 @@ namespace storm {
void setRelativeTerminationCriterion(bool value); void setRelativeTerminationCriterion(bool value);
void setPrecision(ValueType precision); void setPrecision(ValueType precision);
void setValueIterationMultiplicationStyle(MultiplicationStyle value); void setValueIterationMultiplicationStyle(MultiplicationStyle value);
void setForceSoundness(bool value);
SolutionMethod const& getSolutionMethod() const; SolutionMethod const& getSolutionMethod() const;
uint64_t getMaximalNumberOfIterations() const; uint64_t getMaximalNumberOfIterations() const;
ValueType getPrecision() const; ValueType getPrecision() const;
bool getRelativeTerminationCriterion() const; bool getRelativeTerminationCriterion() const;
MultiplicationStyle getValueIterationMultiplicationStyle() const; MultiplicationStyle getValueIterationMultiplicationStyle() const;
bool getForceSoundness() const;
private: private:
bool forceSoundness;
SolutionMethod solutionMethod; SolutionMethod solutionMethod;
uint64_t maximalNumberOfIterations; uint64_t maximalNumberOfIterations;
ValueType precision; ValueType precision;
@ -60,6 +63,7 @@ namespace storm {
private: private:
bool solveEquationsPolicyIteration(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const; 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 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 solveEquationsAcyclic(OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
bool valueImproved(OptimizationDirection dir, ValueType const& value1, ValueType const& value2) const; bool valueImproved(OptimizationDirection dir, ValueType const& value1, ValueType const& value2) const;
@ -72,6 +76,7 @@ namespace storm {
// possibly cached data // possibly cached data
mutable std::unique_ptr<std::vector<ValueType>> auxiliaryRowGroupVector; // A.rowGroupCount() entries 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 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; Status updateStatusIfNotConverged(Status status, std::vector<ValueType> const& x, uint64_t iterations) const;

35
src/storm/solver/LinearEquationSolver.cpp

@ -23,6 +23,11 @@ namespace storm {
// Intentionally left empty. // 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> template<typename ValueType>
void LinearEquationSolver<ValueType>::repeatedMultiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, uint_fast64_t n) const { void LinearEquationSolver<ValueType>::repeatedMultiply(std::vector<ValueType>& x, std::vector<ValueType> const* b, uint_fast64_t n) const {
if (!cachedRowVector) { if (!cachedRowVector) {
@ -101,6 +106,11 @@ namespace storm {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "This solver does not support the function 'multiplyAndReduceGaussSeidel'."); 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> template<typename ValueType>
void LinearEquationSolver<ValueType>::setCachingEnabled(bool value) const { void LinearEquationSolver<ValueType>::setCachingEnabled(bool value) const {
if(cachingEnabled && !value) { if(cachingEnabled && !value) {
@ -136,6 +146,26 @@ namespace storm {
setUpperBound(upper); 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> template<typename ValueType>
std::unique_ptr<LinearEquationSolver<ValueType>> LinearEquationSolverFactory<ValueType>::create(storm::storage::SparseMatrix<ValueType> const& matrix) const { std::unique_ptr<LinearEquationSolver<ValueType>> LinearEquationSolverFactory<ValueType>::create(storm::storage::SparseMatrix<ValueType> const& matrix) const {
std::unique_ptr<LinearEquationSolver<ValueType>> solver = this->create(); std::unique_ptr<LinearEquationSolver<ValueType>> solver = this->create();
@ -155,6 +185,11 @@ namespace storm {
return this->create()->getEquationProblemFormat(); return this->create()->getEquationProblemFormat();
} }
template<typename ValueType>
LinearEquationSolverRequirements LinearEquationSolverFactory<ValueType>::getRequirements() const {
return this->create()->getRequirements();
}
template<typename ValueType> template<typename ValueType>
std::unique_ptr<LinearEquationSolver<ValueType>> GeneralLinearEquationSolverFactory<ValueType>::create() const { std::unique_ptr<LinearEquationSolver<ValueType>> GeneralLinearEquationSolverFactory<ValueType>::create() const {
EquationSolverType equationSolver = storm::settings::getModule<storm::settings::modules::CoreSettings>().getEquationSolver(); EquationSolverType equationSolver = storm::settings::getModule<storm::settings::modules::CoreSettings>().getEquationSolver();

37
src/storm/solver/LinearEquationSolver.h

@ -7,6 +7,7 @@
#include "storm/solver/AbstractEquationSolver.h" #include "storm/solver/AbstractEquationSolver.h"
#include "storm/solver/MultiplicationStyle.h" #include "storm/solver/MultiplicationStyle.h"
#include "storm/solver/LinearEquationSolverProblemFormat.h" #include "storm/solver/LinearEquationSolverProblemFormat.h"
#include "storm/solver/LinearEquationSolverRequirements.h"
#include "storm/solver/OptimizationDirection.h" #include "storm/solver/OptimizationDirection.h"
#include "storm/utility/VectorHelper.h" #include "storm/utility/VectorHelper.h"
@ -47,7 +48,7 @@ namespace storm {
* *
* @return true * @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. * Performs on matrix-vector multiplication x' = A*x + b.
@ -127,6 +128,12 @@ namespace storm {
*/ */
virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const = 0; 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. * 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. * This possibly increases the runtime of subsequent calls but also increases memory consumption.
@ -153,12 +160,34 @@ namespace storm {
*/ */
void setUpperBound(ValueType const& value); 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. * Sets bounds for the solution that can potentially be used by the solver.
*/ */
void setBounds(ValueType const& lower, ValueType const& upper); void setBounds(ValueType const& lower, ValueType const& upper);
protected: protected:
virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const = 0;
// auxiliary storage. If set, this vector has getMatrixRowCount() entries. // auxiliary storage. If set, this vector has getMatrixRowCount() entries.
mutable std::unique_ptr<std::vector<ValueType>> cachedRowVector; 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. * Retrieves the problem format that the solver expects if it was created with the current settings.
*/ */
virtual LinearEquationSolverProblemFormat getEquationProblemFormat() const; 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> template<typename ValueType>

48
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;
}
}
}

35
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;
};
}
}

20
src/storm/solver/MinMaxLinearEquationSolver.cpp

@ -127,6 +127,26 @@ namespace storm {
setUpperBound(upper); 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> template<typename ValueType>
void MinMaxLinearEquationSolver<ValueType>::setInitialScheduler(std::vector<uint_fast64_t>&& choices) { void MinMaxLinearEquationSolver<ValueType>::setInitialScheduler(std::vector<uint_fast64_t>&& choices) {
initialScheduler = std::move(choices); initialScheduler = std::move(choices);

20
src/storm/solver/MinMaxLinearEquationSolver.h

@ -150,7 +150,27 @@ namespace storm {
* Sets bounds for the solution that can potentially used by the solver. * Sets bounds for the solution that can potentially used by the solver.
*/ */
void setBounds(ValueType const& lower, ValueType const& upper); 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). * Sets a valid initial scheduler that is required by some solvers (see requirements of solvers).
*/ */

64
src/storm/solver/MinMaxLinearEquationSolverRequirements.cpp

@ -3,58 +3,74 @@
namespace storm { namespace storm {
namespace solver { 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. // Intentionally left empty.
} }
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setNoEndComponents(bool value) {
noEndComponents = value;
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireNoEndComponents() {
noEndComponents = true;
return *this; return *this;
} }
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setNoZeroRewardEndComponents(bool value) {
noZeroRewardEndComponents = value;
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireValidInitialScheduler() {
validInitialScheduler = true;
return *this; return *this;
} }
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setValidInitialScheduler(bool value) {
validInitialScheduler = value;
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireGlobalLowerBound() {
globalLowerBound = true;
return *this; return *this;
} }
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::setGlobalLowerBound(bool value) {
globalLowerBound = value;
MinMaxLinearEquationSolverRequirements& MinMaxLinearEquationSolverRequirements::requireGlobalUpperBound() {
globalUpperBound = true;
return *this; 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) { switch (element) {
case Element::NoEndComponents: return noEndComponents; break; case Element::NoEndComponents: return noEndComponents; break;
case Element::NoZeroRewardEndComponents: return noZeroRewardEndComponents; break;
case Element::ValidInitialScheduler: return validInitialScheduler; break; case Element::ValidInitialScheduler: return validInitialScheduler; break;
case Element::GlobalLowerBound: return globalLowerBound; break; case Element::GlobalLowerBound: return globalLowerBound; break;
case Element::GlobalUpperBound: return globalUpperBound; 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 { bool MinMaxLinearEquationSolverRequirements::empty() const {
return !noEndComponents && !noZeroRewardEndComponents && !validInitialScheduler && !globalLowerBound && !globalUpperBound;
return !noEndComponents && !validInitialScheduler && !globalLowerBound && !globalUpperBound;
} }
} }

37
src/storm/solver/MinMaxLinearEquationSolverRequirements.h

@ -1,30 +1,47 @@
#pragma once #pragma once
#include "storm/solver/LinearEquationSolverRequirements.h"
namespace storm { namespace storm {
namespace solver { namespace solver {
class MinMaxLinearEquationSolverRequirements { class MinMaxLinearEquationSolverRequirements {
public: public:
// The different requirements a solver can have.
enum class Element { 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; bool empty() const;
private: private:
bool noEndComponents; bool noEndComponents;
bool noZeroRewardEndComponents;
bool validInitialScheduler; bool validInitialScheduler;
bool globalLowerBound; bool globalLowerBound;
bool globalUpperBound; bool globalUpperBound;

97
src/storm/solver/NativeLinearEquationSolver.cpp

@ -3,12 +3,14 @@
#include <utility> #include <utility>
#include "storm/settings/SettingsManager.h" #include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/GeneralSettings.h"
#include "storm/settings/modules/NativeEquationSolverSettings.h" #include "storm/settings/modules/NativeEquationSolverSettings.h"
#include "storm/utility/constants.h" #include "storm/utility/constants.h"
#include "storm/utility/vector.h" #include "storm/utility/vector.h"
#include "storm/exceptions/InvalidStateException.h" #include "storm/exceptions/InvalidStateException.h"
#include "storm/exceptions/InvalidSettingsException.h" #include "storm/exceptions/InvalidSettingsException.h"
#include "storm/exceptions/UnmetRequirementException.h"
namespace storm { namespace storm {
namespace solver { namespace solver {
@ -28,22 +30,26 @@ namespace storm {
method = SolutionMethod::WalkerChae; method = SolutionMethod::WalkerChae;
} else if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationMethod::Power) { } else if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationMethod::Power) {
method = SolutionMethod::Power; method = SolutionMethod::Power;
} else if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationMethod::SoundPower) {
method = SolutionMethod::SoundPower;
} else { } else {
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "The selected solution technique is invalid for this solver."); STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "The selected solution technique is invalid for this solver.");
} }
maximalNumberOfIterations = settings.getMaximalIterationCount(); maximalNumberOfIterations = settings.getMaximalIterationCount();
precision = settings.getPrecision(); precision = settings.getPrecision();
relative = settings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Relative; relative = settings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Relative;
omega = settings.getOmega(); omega = settings.getOmega();
multiplicationStyle = settings.getPowerMethodMultiplicationStyle(); 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> template<typename ValueType>
void NativeLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) { void NativeLinearEquationSolverSettings<ValueType>::setSolutionMethod(SolutionMethod const& method) {
this->method = method; this->method = method;
// Make sure we switch the method if we have to guarantee soundness.
this->setForceSoundness(forceSoundness);
} }
template<typename ValueType> template<typename ValueType>
@ -71,6 +77,15 @@ namespace storm {
this->multiplicationStyle = value; 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> template<typename ValueType>
typename NativeLinearEquationSolverSettings<ValueType>::SolutionMethod NativeLinearEquationSolverSettings<ValueType>::getSolutionMethod() const { typename NativeLinearEquationSolverSettings<ValueType>::SolutionMethod NativeLinearEquationSolverSettings<ValueType>::getSolutionMethod() const {
return method; return method;
@ -101,6 +116,11 @@ namespace storm {
return multiplicationStyle; return multiplicationStyle;
} }
template<typename ValueType>
bool NativeLinearEquationSolverSettings<ValueType>::getForceSoundness() const {
return forceSoundness;
}
template<typename ValueType> template<typename ValueType>
NativeLinearEquationSolver<ValueType>::NativeLinearEquationSolver(NativeLinearEquationSolverSettings<ValueType> const& settings) : localA(nullptr), A(nullptr), settings(settings) { NativeLinearEquationSolver<ValueType>::NativeLinearEquationSolver(NativeLinearEquationSolverSettings<ValueType> const& settings) : localA(nullptr), A(nullptr), settings(settings) {
// Intentionally left empty. // Intentionally left empty.
@ -410,11 +430,69 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
bool NativeLinearEquationSolver<ValueType>::solveEquationsSoundPower(std::vector<ValueType>& x, std::vector<ValueType> const& b) const { 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> 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) { 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>()); 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) { } else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::Jacobi) {
@ -422,7 +500,11 @@ namespace storm {
} else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::WalkerChae) { } else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::WalkerChae) {
return this->solveEquationsWalkerChae(x, b); return this->solveEquationsWalkerChae(x, b);
} else if (this->getSettings().getSolutionMethod() == NativeLinearEquationSolverSettings<ValueType>::SolutionMethod::Power) { } 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."); STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unknown solving technique.");
@ -495,7 +577,7 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
LinearEquationSolverProblemFormat NativeLinearEquationSolver<ValueType>::getEquationProblemFormat() const { 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; return LinearEquationSolverProblemFormat::FixedPointSystem;
} else { } else {
return LinearEquationSolverProblemFormat::EquationSystem; return LinearEquationSolverProblemFormat::EquationSystem;
@ -505,6 +587,7 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
void NativeLinearEquationSolver<ValueType>::clearCache() const { void NativeLinearEquationSolver<ValueType>::clearCache() const {
jacobiDecomposition.reset(); jacobiDecomposition.reset();
cachedRowVector2.reset();
walkerChaeData.reset(); walkerChaeData.reset();
LinearEquationSolver<ValueType>::clearCache(); LinearEquationSolver<ValueType>::clearCache();
} }

12
src/storm/solver/NativeLinearEquationSolver.h

@ -14,7 +14,7 @@ namespace storm {
class NativeLinearEquationSolverSettings { class NativeLinearEquationSolverSettings {
public: public:
enum class SolutionMethod { enum class SolutionMethod {
Jacobi, GaussSeidel, SOR, WalkerChae, Power, SoundPower
Jacobi, GaussSeidel, SOR, WalkerChae, Power
}; };
NativeLinearEquationSolverSettings(); NativeLinearEquationSolverSettings();
@ -25,15 +25,18 @@ namespace storm {
void setRelativeTerminationCriterion(bool value); void setRelativeTerminationCriterion(bool value);
void setOmega(ValueType omega); void setOmega(ValueType omega);
void setPowerMethodMultiplicationStyle(MultiplicationStyle value); void setPowerMethodMultiplicationStyle(MultiplicationStyle value);
void setForceSoundness(bool value);
SolutionMethod getSolutionMethod() const; SolutionMethod getSolutionMethod() const;
ValueType getPrecision() const; ValueType getPrecision() const;
uint64_t getMaximalNumberOfIterations() const; uint64_t getMaximalNumberOfIterations() const;
uint64_t getRelativeTerminationCriterion() const; uint64_t getRelativeTerminationCriterion() const;
ValueType getOmega() const; ValueType getOmega() const;
MultiplicationStyle getPowerMethodMultiplicationStyle() const; MultiplicationStyle getPowerMethodMultiplicationStyle() const;
bool getForceSoundness() const;
private: private:
bool forceSoundness;
SolutionMethod method; SolutionMethod method;
double precision; double precision;
bool relative; 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> const& A) override;
virtual void setMatrix(storm::storage::SparseMatrix<ValueType>&& 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 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 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; virtual bool supportsGaussSeidelMultiplication() const override;
@ -69,6 +71,9 @@ namespace storm {
virtual void clearCache() const override; virtual void clearCache() const override;
protected:
virtual bool internalSolveEquations(std::vector<ValueType>& x, std::vector<ValueType> const& b) const override;
private: private:
virtual uint64_t getMatrixRowCount() const override; virtual uint64_t getMatrixRowCount() const override;
virtual uint64_t getMatrixColumnCount() const override; virtual uint64_t getMatrixColumnCount() const override;
@ -95,6 +100,7 @@ namespace storm {
// cached auxiliary data // cached auxiliary data
mutable std::unique_ptr<std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>>> jacobiDecomposition; 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 { struct WalkerChaeData {
WalkerChaeData(storm::storage::SparseMatrix<ValueType> const& originalMatrix, std::vector<ValueType> const& originalB); WalkerChaeData(storm::storage::SparseMatrix<ValueType> const& originalMatrix, std::vector<ValueType> const& originalB);

4
src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp

@ -263,12 +263,12 @@ namespace storm {
if (equationSystemType == EquationSystemType::UntilProbabilities) { if (equationSystemType == EquationSystemType::UntilProbabilities) {
if (this->getSettings().getSolutionMethod() == SymbolicMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration) { if (this->getSettings().getSolutionMethod() == SymbolicMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::PolicyIteration) {
if (!direction || direction.get() == OptimizationDirection::Maximize) { if (!direction || direction.get() == OptimizationDirection::Maximize) {
requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler);
requirements.requireValidInitialScheduler();
} }
} }
} else if (equationSystemType == EquationSystemType::ReachabilityRewards) { } else if (equationSystemType == EquationSystemType::ReachabilityRewards) {
if (!direction || direction.get() == OptimizationDirection::Minimize) { if (!direction || direction.get() == OptimizationDirection::Minimize) {
requirements.set(MinMaxLinearEquationSolverRequirements::Element::ValidInitialScheduler);
requirements.requireValidInitialScheduler();
} }
} }

Loading…
Cancel
Save