#include "src/solver/NativeNondeterministicLinearEquationSolver.h" #include #include "src/settings/Settings.h" #include "src/utility/vector.h" bool NativeNondeterministicLinearEquationSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("NativeNondeterminsticLinearEquationSolver", "maxiter", "i", "The maximal number of iterations to perform before iterative solving is aborted.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(10000).build()).build()); instance->addOption(storm::settings::OptionBuilder("NativeNondeterminsticLinearEquationSolver", "precision", "", "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); instance->addOption(storm::settings::OptionBuilder("NativeNondeterminsticLinearEquationSolver", "absolute", "", "Whether the relative or the absolute error is considered for deciding convergence.").build()); return true; }); namespace storm { namespace solver { template NativeNondeterministicLinearEquationSolver::NativeNondeterministicLinearEquationSolver() { // Get the settings object to customize solving. storm::settings::Settings* settings = storm::settings::Settings::getInstance(); // Get appropriate settings. maximalNumberOfIterations = settings->getOptionByLongName("maxiter").getArgument(0).getValueAsUnsignedInteger(); precision = settings->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); relative = !settings->isSet("absolute"); } template NativeNondeterministicLinearEquationSolver::NativeNondeterministicLinearEquationSolver(double precision, uint_fast64_t maximalNumberOfIterations, bool relative) : precision(precision), relative(relative), maximalNumberOfIterations(maximalNumberOfIterations) { // Intentionally left empty. } template NondeterministicLinearEquationSolver* NativeNondeterministicLinearEquationSolver::clone() const { return new NativeNondeterministicLinearEquationSolver(*this); } template void NativeNondeterministicLinearEquationSolver::solveEquationSystem(bool minimize, storm::storage::SparseMatrix const& A, std::vector& x, std::vector const& b, std::vector* multiplyResult, std::vector* newX) const { // Set up the environment for the power method. If scratch memory was not provided, we need to create it. bool multiplyResultMemoryProvided = true; if (multiplyResult == nullptr) { multiplyResult = new std::vector(A.getRowCount()); multiplyResultMemoryProvided = false; } std::vector* currentX = &x; bool xMemoryProvided = true; if (newX == nullptr) { newX = new std::vector(x.size()); xMemoryProvided = false; } std::vector* swap = nullptr; uint_fast64_t iterations = 0; bool converged = false; // Keep track of which of the vectors for x is the auxiliary copy. std::vector* copyX = newX; // Proceed with the iterations as long as the method did not converge or reach the // user-specified maximum number of iterations. while (!converged && iterations < maximalNumberOfIterations) { // Compute x' = A*x + b. A.multiplyWithVector(*currentX, *multiplyResult); storm::utility::vector::addVectorsInPlace(*multiplyResult, b); // Reduce the vector x' by applying min/max for all non-deterministic choices as given by the topmost // element of the min/max operator stack. if (minimize) { storm::utility::vector::reduceVectorMin(*multiplyResult, *newX, A.getRowGroupIndices()); } else { storm::utility::vector::reduceVectorMax(*multiplyResult, *newX, A.getRowGroupIndices()); } // Determine whether the method converged. converged = storm::utility::vector::equalModuloPrecision(*currentX, *newX, precision, relative); // Update environment variables. std::swap(currentX, newX); ++iterations; } // Check if the solver converged and issue a warning otherwise. if (converged) { LOG4CPLUS_INFO(logger, "Iterative solver converged after " << iterations << " iterations."); } else { LOG4CPLUS_WARN(logger, "Iterative solver did not converge after " << iterations << " iterations."); } // If we performed an odd number of iterations, we need to swap the x and currentX, because the newest result // is currently stored in currentX, but x is the output vector. if (currentX == copyX) { std::swap(x, *currentX); } if (!xMemoryProvided) { delete copyX; } if (!multiplyResultMemoryProvided) { delete multiplyResult; } } template void NativeNondeterministicLinearEquationSolver::performMatrixVectorMultiplication(bool minimize, storm::storage::SparseMatrix const& A, std::vector& x, std::vector* b, uint_fast64_t n, std::vector* multiplyResult) const { // If scratch memory was not provided, we need to create it. bool multiplyResultMemoryProvided = true; if (multiplyResult == nullptr) { multiplyResult = new std::vector(A.getRowCount()); multiplyResultMemoryProvided = false; } // Now perform matrix-vector multiplication as long as we meet the bound of the formula. for (uint_fast64_t i = 0; i < n; ++i) { A.multiplyWithVector(x, *multiplyResult); // Add b if it is non-null. if (b != nullptr) { storm::utility::vector::addVectorsInPlace(*multiplyResult, *b); } // Reduce the vector x' by applying min/max for all non-deterministic choices as given by the topmost // element of the min/max operator stack. if (minimize) { storm::utility::vector::reduceVectorMin(*multiplyResult, x, A.getRowGroupIndices()); } else { storm::utility::vector::reduceVectorMax(*multiplyResult, x, A.getRowGroupIndices()); } } if (!multiplyResultMemoryProvided) { delete multiplyResult; } } // Explicitly instantiate the solver. template class NativeNondeterministicLinearEquationSolver; } // namespace solver } // namespace storm