#include "src/solver/NativeLinearEquationSolver.h" #include #include "src/settings/SettingsManager.h" #include "src/utility/vector.h" #include "src/exceptions/InvalidStateException.h" namespace storm { namespace solver { template NativeLinearEquationSolver::NativeLinearEquationSolver(SolutionMethod method, double precision, uint_fast64_t maximalNumberOfIterations, bool relative) : method(method), precision(precision), relative(relative), maximalNumberOfIterations(maximalNumberOfIterations) { // Intentionally left empty. } template NativeLinearEquationSolver::NativeLinearEquationSolver() { // Get the settings object to customize linear solving. storm::settings::modules::NativeEquationSolverSettings const& settings = storm::settings::nativeEquationSolverSettings(); // Get appropriate settings. maximalNumberOfIterations = settings.getMaximalIterationCount(); precision = settings.getPrecision(); relative = settings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Relative; // Determine the method to be used. storm::settings::modules::NativeEquationSolverSettings::LinearEquationTechnique methodAsSetting = settings.getLinearEquationSystemTechnique(); if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationTechnique::Jacobi) { method = SolutionMethod::Jacobi; } } template LinearEquationSolver* NativeLinearEquationSolver::clone() const { return new NativeLinearEquationSolver(*this); } template void NativeLinearEquationSolver::solveEquationSystem(storm::storage::SparseMatrix const& A, std::vector& x, std::vector const& b, std::vector* multiplyResult) const { // Get a Jacobi decomposition of the matrix A. std::pair, storm::storage::SparseMatrix> jacobiDecomposition = A.getJacobiDecomposition(); // To avoid copying the contents of the vector in the loop, we create a temporary x to swap with. bool multiplyResultProvided = true; std::vector* nextX = multiplyResult; if (nextX == nullptr) { nextX = new std::vector(x.size()); multiplyResultProvided = false; } std::vector const* copyX = nextX; std::vector* currentX = &x; // Target vector for precision calculation. std::vector tmpX(x.size()); // Set up additional environment variables. uint_fast64_t iterationCount = 0; bool converged = false; while (!converged && iterationCount < maximalNumberOfIterations) { // Compute D^-1 * (b - LU * x) and store result in nextX. jacobiDecomposition.first.multiplyWithVector(*currentX, tmpX); storm::utility::vector::scaleVectorInPlace(tmpX, -storm::utility::constantOne()); storm::utility::vector::addVectorsInPlace(tmpX, b); jacobiDecomposition.second.multiplyWithVector(tmpX, *nextX); // Swap the two pointers as a preparation for the next iteration. std::swap(nextX, currentX); // Now check if the process already converged within our precision. converged = storm::utility::vector::equalModuloPrecision(*currentX, *nextX, precision, relative); // Increase iteration count so we can abort if convergence is too slow. ++iterationCount; } // If the last iteration did not write to the original x we have to swap the contents, because the // output has to be written to the input parameter x. if (currentX == copyX) { std::swap(x, *currentX); } // If the vector for the temporary multiplication result was not provided, we need to delete it. if (!multiplyResultProvided) { delete copyX; } } template void NativeLinearEquationSolver::performMatrixVectorMultiplication(storm::storage::SparseMatrix const& A, std::vector& x, std::vector* b, uint_fast64_t n, std::vector* multiplyResult) const { // Set up some temporary variables so that we can just swap pointers instead of copying the result after // each iteration. std::vector* currentX = &x; bool multiplyResultProvided = true; std::vector* nextX = multiplyResult; if (nextX == nullptr) { nextX = new std::vector(x.size()); multiplyResultProvided = false; } std::vector const* copyX = nextX; // Now perform matrix-vector multiplication as long as we meet the bound. for (uint_fast64_t i = 0; i < n; ++i) { A.multiplyWithVector(*currentX, *nextX); std::swap(nextX, currentX); // If requested, add an offset to the current result vector. if (b != nullptr) { storm::utility::vector::addVectorsInPlace(*currentX, *b); } } // If we performed an odd number of repetitions, we need to swap the contents of currentVector and x, // because the output is supposed to be stored in the input vector x. if (currentX == copyX) { std::swap(x, *currentX); } // If the vector for the temporary multiplication result was not provided, we need to delete it. if (!multiplyResultProvided) { delete copyX; } } template std::string NativeLinearEquationSolver::methodToString() const { switch (method) { case SolutionMethod::Jacobi: return "jacobi"; } } // Explicitly instantiate the linear equation solver. template class NativeLinearEquationSolver; } }