You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
16 KiB

  1. #include "storm/solver/EigenLinearEquationSolver.h"
  2. #include "storm/adapters/EigenAdapter.h"
  3. #include "storm/environment/solver/EigenSolverEnvironment.h"
  4. #include "storm/utility/vector.h"
  5. #include "storm/utility/macros.h"
  6. #include "storm/exceptions/InvalidSettingsException.h"
  7. namespace storm {
  8. namespace solver {
  9. template<typename ValueType>
  10. EigenLinearEquationSolver<ValueType>::EigenLinearEquationSolver() {
  11. // Intentionally left empty.
  12. }
  13. template<typename ValueType>
  14. EigenLinearEquationSolver<ValueType>::EigenLinearEquationSolver(storm::storage::SparseMatrix<ValueType> const& A) {
  15. this->setMatrix(A);
  16. }
  17. template<typename ValueType>
  18. EigenLinearEquationSolver<ValueType>::EigenLinearEquationSolver(storm::storage::SparseMatrix<ValueType>&& A) {
  19. this->setMatrix(std::move(A));
  20. }
  21. template<typename ValueType>
  22. void EigenLinearEquationSolver<ValueType>::setMatrix(storm::storage::SparseMatrix<ValueType> const& A) {
  23. eigenA = storm::adapters::EigenAdapter::toEigenSparseMatrix<ValueType>(A);
  24. this->clearCache();
  25. }
  26. template<typename ValueType>
  27. void EigenLinearEquationSolver<ValueType>::setMatrix(storm::storage::SparseMatrix<ValueType>&& A) {
  28. // Take ownership of the matrix so it is destroyed after we have translated it to Eigen's format.
  29. storm::storage::SparseMatrix<ValueType> localA(std::move(A));
  30. this->setMatrix(localA);
  31. this->clearCache();
  32. }
  33. template<typename ValueType>
  34. EigenLinearEquationSolverMethod EigenLinearEquationSolver<ValueType>::getMethod(Environment const& env, bool isExactMode) const {
  35. // Adjust the method if none was specified and we are using rational numbers.
  36. auto method = env.solver().eigen().getMethod();
  37. if (isExactMode && method != EigenLinearEquationSolverMethod::SparseLU) {
  38. if (env.solver().eigen().isMethodSetFromDefault()) {
  39. STORM_LOG_INFO("Selecting 'SparseLU' as the solution technique to guarantee exact results.");
  40. } else {
  41. STORM_LOG_WARN("The selected solution method does not guarantee exact results. Falling back to SparseLU.");
  42. }
  43. method = EigenLinearEquationSolverMethod::SparseLU;
  44. } else if (env.solver().isForceSoundness() && method != EigenLinearEquationSolverMethod::SparseLU) {
  45. if (env.solver().eigen().isMethodSetFromDefault()) {
  46. STORM_LOG_INFO("Selecting 'SparseLU' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
  47. method = EigenLinearEquationSolverMethod::SparseLU;
  48. } else {
  49. STORM_LOG_WARN("The selected solution method does not guarantee sound results.");
  50. }
  51. }
  52. return method;
  53. }
  54. #ifdef STORM_HAVE_CARL
  55. // Specialization for storm::RationalNumber
  56. template<>
  57. bool EigenLinearEquationSolver<storm::RationalNumber>::internalSolveEquations(Environment const& env, std::vector<storm::RationalNumber>& x, std::vector<storm::RationalNumber> const& b) const {
  58. auto solutionMethod = getMethod(env, true);
  59. STORM_LOG_WARN_COND(solutionMethod == EigenLinearEquationSolverMethod::SparseLU, "Switching method to SparseLU.");
  60. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with with rational numbers using LU factorization (Eigen library).");
  61. // Map the input vectors to Eigen's format.
  62. auto eigenX = Eigen::Matrix<storm::RationalNumber, Eigen::Dynamic, 1>::Map(x.data(), x.size());
  63. auto eigenB = Eigen::Matrix<storm::RationalNumber, Eigen::Dynamic, 1>::Map(b.data(), b.size());
  64. Eigen::SparseLU<Eigen::SparseMatrix<storm::RationalNumber>, Eigen::COLAMDOrdering<int>> solver;
  65. solver.compute(*eigenA);
  66. solver._solve_impl(eigenB, eigenX);
  67. return solver.info() == Eigen::ComputationInfo::Success;
  68. }
  69. // Specialization for storm::RationalFunction
  70. template<>
  71. bool EigenLinearEquationSolver<storm::RationalFunction>::internalSolveEquations(Environment const& env, std::vector<storm::RationalFunction>& x, std::vector<storm::RationalFunction> const& b) const {
  72. auto solutionMethod = getMethod(env, true);
  73. STORM_LOG_WARN_COND(solutionMethod == EigenLinearEquationSolverMethod::SparseLU, "Switching method to SparseLU.");
  74. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with rational functions using LU factorization (Eigen library).");
  75. // Map the input vectors to Eigen's format.
  76. auto eigenX = Eigen::Matrix<storm::RationalFunction, Eigen::Dynamic, 1>::Map(x.data(), x.size());
  77. auto eigenB = Eigen::Matrix<storm::RationalFunction, Eigen::Dynamic, 1>::Map(b.data(), b.size());
  78. Eigen::SparseLU<Eigen::SparseMatrix<storm::RationalFunction>, Eigen::COLAMDOrdering<int>> solver;
  79. solver.compute(*eigenA);
  80. solver._solve_impl(eigenB, eigenX);
  81. return solver.info() == Eigen::ComputationInfo::Success;
  82. }
  83. #endif
  84. template<typename ValueType>
  85. bool EigenLinearEquationSolver<ValueType>::internalSolveEquations(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
  86. // Map the input vectors to Eigen's format.
  87. auto eigenX = Eigen::Matrix<ValueType, Eigen::Dynamic, 1>::Map(x.data(), x.size());
  88. auto eigenB = Eigen::Matrix<ValueType, Eigen::Dynamic, 1>::Map(b.data(), b.size());
  89. auto solutionMethod = getMethod(env, env.solver().isForceExact());
  90. if (solutionMethod == EigenLinearEquationSolverMethod::SparseLU) {
  91. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with sparse LU factorization (Eigen library).");
  92. Eigen::SparseLU<Eigen::SparseMatrix<ValueType>, Eigen::COLAMDOrdering<int>> solver;
  93. solver.compute(*this->eigenA);
  94. solver._solve_impl(eigenB, eigenX);
  95. } else {
  96. bool converged = false;
  97. uint64_t numberOfIterations = 0;
  98. Eigen::Index maxIter = std::numeric_limits<Eigen::Index>::max();
  99. if (env.solver().eigen().getMaximalNumberOfIterations() < static_cast<uint64_t>(maxIter)) {
  100. maxIter = env.solver().eigen().getMaximalNumberOfIterations();
  101. }
  102. uint64_t restartThreshold = env.solver().eigen().getRestartThreshold();
  103. ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().eigen().getPrecision());
  104. EigenLinearEquationSolverPreconditioner preconditioner = env.solver().eigen().getPreconditioner();
  105. if (solutionMethod == EigenLinearEquationSolverMethod::Bicgstab) {
  106. if (preconditioner == EigenLinearEquationSolverPreconditioner::Ilu) {
  107. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with BiCGSTAB with Ilu preconditioner (Eigen library).");
  108. Eigen::BiCGSTAB<Eigen::SparseMatrix<ValueType>, Eigen::IncompleteLUT<ValueType>> solver;
  109. solver.compute(*this->eigenA);
  110. solver.setTolerance(precision);
  111. solver.setMaxIterations(maxIter);
  112. eigenX = solver.solveWithGuess(eigenB, eigenX);
  113. converged = solver.info() == Eigen::ComputationInfo::Success;
  114. numberOfIterations = solver.iterations();
  115. } else if (preconditioner == EigenLinearEquationSolverPreconditioner::Diagonal) {
  116. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with BiCGSTAB with Diagonal preconditioner (Eigen library).");
  117. Eigen::BiCGSTAB<Eigen::SparseMatrix<ValueType>, Eigen::DiagonalPreconditioner<ValueType>> solver;
  118. solver.setTolerance(precision);
  119. solver.setMaxIterations(maxIter);
  120. solver.compute(*this->eigenA);
  121. eigenX = solver.solveWithGuess(eigenB, eigenX);
  122. converged = solver.info() == Eigen::ComputationInfo::Success;
  123. numberOfIterations = solver.iterations();
  124. } else {
  125. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with BiCGSTAB with identity preconditioner (Eigen library).");
  126. Eigen::BiCGSTAB<Eigen::SparseMatrix<ValueType>, Eigen::IdentityPreconditioner> solver;
  127. solver.setTolerance(precision);
  128. solver.setMaxIterations(maxIter);
  129. solver.compute(*this->eigenA);
  130. eigenX = solver.solveWithGuess(eigenB, eigenX);
  131. numberOfIterations = solver.iterations();
  132. converged = solver.info() == Eigen::ComputationInfo::Success;
  133. }
  134. } else if (solutionMethod == EigenLinearEquationSolverMethod::DGmres) {
  135. if (preconditioner == EigenLinearEquationSolverPreconditioner::Ilu) {
  136. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with DGMRES with Ilu preconditioner (Eigen library).");
  137. Eigen::DGMRES<Eigen::SparseMatrix<ValueType>, Eigen::IncompleteLUT<ValueType>> solver;
  138. solver.setTolerance(precision);
  139. solver.setMaxIterations(maxIter);
  140. solver.set_restart(restartThreshold);
  141. solver.compute(*this->eigenA);
  142. eigenX = solver.solveWithGuess(eigenB, eigenX);
  143. converged = solver.info() == Eigen::ComputationInfo::Success;
  144. numberOfIterations = solver.iterations();
  145. } else if (preconditioner == EigenLinearEquationSolverPreconditioner::Diagonal) {
  146. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with DGMRES with Diagonal preconditioner (Eigen library).");
  147. Eigen::DGMRES<Eigen::SparseMatrix<ValueType>, Eigen::DiagonalPreconditioner<ValueType>> solver;
  148. solver.setTolerance(precision);
  149. solver.setMaxIterations(maxIter);
  150. solver.set_restart(restartThreshold);
  151. solver.compute(*this->eigenA);
  152. eigenX = solver.solveWithGuess(eigenB, eigenX);
  153. converged = solver.info() == Eigen::ComputationInfo::Success;
  154. numberOfIterations = solver.iterations();
  155. } else {
  156. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with DGMRES with identity preconditioner (Eigen library).");
  157. Eigen::DGMRES<Eigen::SparseMatrix<ValueType>, Eigen::IdentityPreconditioner> solver;
  158. solver.setTolerance(precision);
  159. solver.setMaxIterations(maxIter);
  160. solver.set_restart(restartThreshold);
  161. solver.compute(*this->eigenA);
  162. eigenX = solver.solveWithGuess(eigenB, eigenX);
  163. converged = solver.info() == Eigen::ComputationInfo::Success;
  164. numberOfIterations = solver.iterations();
  165. }
  166. } else if (solutionMethod == EigenLinearEquationSolverMethod::Gmres) {
  167. if (preconditioner == EigenLinearEquationSolverPreconditioner::Ilu) {
  168. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with GMRES with Ilu preconditioner (Eigen library).");
  169. Eigen::GMRES<Eigen::SparseMatrix<ValueType>, Eigen::IncompleteLUT<ValueType>> solver;
  170. solver.setTolerance(precision);
  171. solver.setMaxIterations(maxIter);
  172. solver.set_restart(restartThreshold);
  173. solver.compute(*this->eigenA);
  174. eigenX = solver.solveWithGuess(eigenB, eigenX);
  175. converged = solver.info() == Eigen::ComputationInfo::Success;
  176. numberOfIterations = solver.iterations();
  177. } else if (preconditioner == EigenLinearEquationSolverPreconditioner::Diagonal) {
  178. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with GMRES with Diagonal preconditioner (Eigen library).");
  179. Eigen::GMRES<Eigen::SparseMatrix<ValueType>, Eigen::DiagonalPreconditioner<ValueType>> solver;
  180. solver.setTolerance(precision);
  181. solver.setMaxIterations(maxIter);
  182. solver.set_restart(restartThreshold);
  183. solver.compute(*this->eigenA);
  184. eigenX = solver.solveWithGuess(eigenB, eigenX);
  185. converged = solver.info() == Eigen::ComputationInfo::Success;
  186. numberOfIterations = solver.iterations();
  187. } else {
  188. STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with GMRES with identity preconditioner (Eigen library).");
  189. Eigen::GMRES<Eigen::SparseMatrix<ValueType>, Eigen::IdentityPreconditioner> solver;
  190. solver.setTolerance(precision);
  191. solver.setMaxIterations(maxIter);
  192. solver.set_restart(restartThreshold);
  193. solver.compute(*this->eigenA);
  194. eigenX = solver.solveWithGuess(eigenB, eigenX);
  195. converged = solver.info() == Eigen::ComputationInfo::Success;
  196. numberOfIterations = solver.iterations();
  197. }
  198. }
  199. // Make sure that all results conform to the (global) bounds.
  200. storm::utility::vector::clip(x, this->lowerBound, this->upperBound);
  201. // Check if the solver converged and issue a warning otherwise.
  202. if (converged) {
  203. STORM_LOG_INFO("Iterative solver converged after " << numberOfIterations << " iterations.");
  204. return true;
  205. } else {
  206. STORM_LOG_WARN("Iterative solver did not converge.");
  207. return false;
  208. }
  209. }
  210. return true;
  211. }
  212. template<typename ValueType>
  213. LinearEquationSolverProblemFormat EigenLinearEquationSolver<ValueType>::getEquationProblemFormat(Environment const&) const {
  214. return LinearEquationSolverProblemFormat::EquationSystem;
  215. }
  216. template<typename ValueType>
  217. uint64_t EigenLinearEquationSolver<ValueType>::getMatrixRowCount() const {
  218. return eigenA->rows();
  219. }
  220. template<typename ValueType>
  221. uint64_t EigenLinearEquationSolver<ValueType>::getMatrixColumnCount() const {
  222. return eigenA->cols();
  223. }
  224. template<typename ValueType>
  225. std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> EigenLinearEquationSolverFactory<ValueType>::create(Environment const&) const {
  226. return std::make_unique<storm::solver::EigenLinearEquationSolver<ValueType>>();
  227. }
  228. template<typename ValueType>
  229. std::unique_ptr<LinearEquationSolverFactory<ValueType>> EigenLinearEquationSolverFactory<ValueType>::clone() const {
  230. return std::make_unique<EigenLinearEquationSolverFactory<ValueType>>(*this);
  231. }
  232. template class EigenLinearEquationSolver<double>;
  233. template class EigenLinearEquationSolverFactory<double>;
  234. #ifdef STORM_HAVE_CARL
  235. template class EigenLinearEquationSolver<storm::RationalNumber>;
  236. template class EigenLinearEquationSolver<storm::RationalFunction>;
  237. template class EigenLinearEquationSolverFactory<storm::RationalNumber>;
  238. template class EigenLinearEquationSolverFactory<storm::RationalFunction>;
  239. #endif
  240. }
  241. }