Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
EigenLinearEquationSolver.cpp
Go to the documentation of this file.
2
8
9namespace storm {
10namespace solver {
11
12template<typename ValueType>
16
17template<typename ValueType>
21
22template<typename ValueType>
26
27template<typename ValueType>
32
33template<typename ValueType>
35 // Take ownership of the matrix so it is destroyed after we have translated it to Eigen's format.
36 storm::storage::SparseMatrix<ValueType> localA(std::move(A));
37 this->setMatrix(localA);
38 this->clearCache();
39}
40
41template<typename ValueType>
42EigenLinearEquationSolverMethod EigenLinearEquationSolver<ValueType>::getMethod(Environment const& env, bool isExactMode) const {
43 // Adjust the method if none was specified and we are using rational numbers.
44 auto method = env.solver().eigen().getMethod();
45
46 if (isExactMode && method != EigenLinearEquationSolverMethod::SparseLU) {
47 if (env.solver().eigen().isMethodSetFromDefault()) {
48 STORM_LOG_INFO("Selecting 'SparseLU' as the solution technique to guarantee exact results.");
49 } else {
50 STORM_LOG_WARN("The selected solution method does not guarantee exact results. Falling back to SparseLU.");
51 }
52 method = EigenLinearEquationSolverMethod::SparseLU;
53 } else if (env.solver().isForceSoundness() && method != EigenLinearEquationSolverMethod::SparseLU) {
54 if (env.solver().eigen().isMethodSetFromDefault()) {
56 "Selecting 'SparseLU' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a "
57 "different method.");
58 method = EigenLinearEquationSolverMethod::SparseLU;
59 } else {
60 STORM_LOG_WARN("The selected solution method does not guarantee sound results.");
61 }
62 }
63 return method;
64}
65
66// Specialization for storm::RationalNumber
67template<>
69 std::vector<storm::RationalNumber> const& b) const {
70 auto solutionMethod = getMethod(env, true);
71 STORM_LOG_WARN_COND(solutionMethod == EigenLinearEquationSolverMethod::SparseLU, "Switching method to SparseLU.");
72 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with with rational numbers using LU factorization (Eigen library).");
73
74 // Map the input vectors to Eigen's format.
75 auto eigenX = Eigen::Matrix<storm::RationalNumber, Eigen::Dynamic, 1>::Map(x.data(), x.size());
76 auto eigenB = Eigen::Matrix<storm::RationalNumber, Eigen::Dynamic, 1>::Map(b.data(), b.size());
77
78 Eigen::SparseLU<Eigen::SparseMatrix<storm::RationalNumber>, Eigen::COLAMDOrdering<int>> solver;
79 solver.compute(*eigenA);
80 solver._solve_impl(eigenB, eigenX);
81
82 if (solver.info() != Eigen::ComputationInfo::Success) {
83 return false;
84 }
86 return true;
87}
88
89// Specialization for storm::RationalFunction
90template<>
91bool EigenLinearEquationSolver<storm::RationalFunction>::internalSolveEquations(Environment const& env, std::vector<storm::RationalFunction>& x,
92 std::vector<storm::RationalFunction> const& b) const {
93 auto solutionMethod = getMethod(env, true);
94 STORM_LOG_WARN_COND(solutionMethod == EigenLinearEquationSolverMethod::SparseLU, "Switching method to SparseLU.");
95 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with rational functions using LU factorization (Eigen library).");
96
97 // Map the input vectors to Eigen's format.
98 auto eigenX = Eigen::Matrix<storm::RationalFunction, Eigen::Dynamic, 1>::Map(x.data(), x.size());
99 auto eigenB = Eigen::Matrix<storm::RationalFunction, Eigen::Dynamic, 1>::Map(b.data(), b.size());
100
101 Eigen::SparseLU<Eigen::SparseMatrix<storm::RationalFunction>, Eigen::COLAMDOrdering<int>> solver;
102 solver.compute(*eigenA);
103 solver._solve_impl(eigenB, eigenX);
104 if (solver.info() != Eigen::ComputationInfo::Success) {
105 return false;
106 }
107 this->setSolutionBoundsExact(x);
108 return true;
109}
110
111template<typename ValueType>
112bool EigenLinearEquationSolver<ValueType>::internalSolveEquations(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
113 // Map the input vectors to Eigen's format.
114 auto eigenX = Eigen::Matrix<ValueType, Eigen::Dynamic, 1>::Map(x.data(), x.size());
115 auto eigenB = Eigen::Matrix<ValueType, Eigen::Dynamic, 1>::Map(b.data(), b.size());
116
117 auto solutionMethod = getMethod(env, env.solver().isForceExact());
118 if (solutionMethod == EigenLinearEquationSolverMethod::SparseLU) {
119 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with sparse LU factorization (Eigen library).");
120 Eigen::SparseLU<Eigen::SparseMatrix<ValueType>, Eigen::COLAMDOrdering<int>> solver;
121 solver.compute(*this->eigenA);
122 solver._solve_impl(eigenB, eigenX);
123 if (solver.info() != Eigen::ComputationInfo::Success) {
124 return false;
125 }
126 this->setSolutionBoundsExact(x);
127 } else {
128 bool converged = false;
129 uint64_t numberOfIterations = 0;
130 Eigen::Index maxIter = std::numeric_limits<Eigen::Index>::max();
131 if (env.solver().eigen().getMaximalNumberOfIterations() < static_cast<uint64_t>(maxIter)) {
132 maxIter = env.solver().eigen().getMaximalNumberOfIterations();
133 }
134 uint64_t restartThreshold = env.solver().eigen().getRestartThreshold();
135 ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().eigen().getPrecision());
136 EigenLinearEquationSolverPreconditioner preconditioner = env.solver().eigen().getPreconditioner();
137 if (solutionMethod == EigenLinearEquationSolverMethod::Bicgstab) {
138 if (preconditioner == EigenLinearEquationSolverPreconditioner::Ilu) {
139 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with BiCGSTAB with Ilu preconditioner (Eigen library).");
140
141 Eigen::BiCGSTAB<Eigen::SparseMatrix<ValueType>, Eigen::IncompleteLUT<ValueType>> solver;
142 solver.compute(*this->eigenA);
143 solver.setTolerance(precision);
144 solver.setMaxIterations(maxIter);
145 eigenX = solver.solveWithGuess(eigenB, eigenX);
146 converged = solver.info() == Eigen::ComputationInfo::Success;
147 numberOfIterations = solver.iterations();
148 } else if (preconditioner == EigenLinearEquationSolverPreconditioner::Diagonal) {
149 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with BiCGSTAB with Diagonal preconditioner (Eigen library).");
150
151 Eigen::BiCGSTAB<Eigen::SparseMatrix<ValueType>, Eigen::DiagonalPreconditioner<ValueType>> solver;
152 solver.setTolerance(precision);
153 solver.setMaxIterations(maxIter);
154 solver.compute(*this->eigenA);
155 eigenX = solver.solveWithGuess(eigenB, eigenX);
156 converged = solver.info() == Eigen::ComputationInfo::Success;
157 numberOfIterations = solver.iterations();
158 } else {
159 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with BiCGSTAB with identity preconditioner (Eigen library).");
160
161 Eigen::BiCGSTAB<Eigen::SparseMatrix<ValueType>, Eigen::IdentityPreconditioner> solver;
162 solver.setTolerance(precision);
163 solver.setMaxIterations(maxIter);
164 solver.compute(*this->eigenA);
165 eigenX = solver.solveWithGuess(eigenB, eigenX);
166 numberOfIterations = solver.iterations();
167 converged = solver.info() == Eigen::ComputationInfo::Success;
168 }
169 } else if (solutionMethod == EigenLinearEquationSolverMethod::DGmres) {
170 if (preconditioner == EigenLinearEquationSolverPreconditioner::Ilu) {
171 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with DGMRES with Ilu preconditioner (Eigen library).");
172 Eigen::DGMRES<Eigen::SparseMatrix<ValueType>, Eigen::IncompleteLUT<ValueType>> solver;
173 solver.setTolerance(precision);
174 solver.setMaxIterations(maxIter);
175 solver.set_restart(restartThreshold);
176 solver.compute(*this->eigenA);
177 eigenX = solver.solveWithGuess(eigenB, eigenX);
178 converged = solver.info() == Eigen::ComputationInfo::Success;
179 numberOfIterations = solver.iterations();
180 } else if (preconditioner == EigenLinearEquationSolverPreconditioner::Diagonal) {
181 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with DGMRES with Diagonal preconditioner (Eigen library).");
182
183 Eigen::DGMRES<Eigen::SparseMatrix<ValueType>, Eigen::DiagonalPreconditioner<ValueType>> solver;
184 solver.setTolerance(precision);
185 solver.setMaxIterations(maxIter);
186 solver.set_restart(restartThreshold);
187 solver.compute(*this->eigenA);
188 eigenX = solver.solveWithGuess(eigenB, eigenX);
189 converged = solver.info() == Eigen::ComputationInfo::Success;
190 numberOfIterations = solver.iterations();
191 } else {
192 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with DGMRES with identity preconditioner (Eigen library).");
193
194 Eigen::DGMRES<Eigen::SparseMatrix<ValueType>, Eigen::IdentityPreconditioner> solver;
195 solver.setTolerance(precision);
196 solver.setMaxIterations(maxIter);
197 solver.set_restart(restartThreshold);
198 solver.compute(*this->eigenA);
199 eigenX = solver.solveWithGuess(eigenB, eigenX);
200 converged = solver.info() == Eigen::ComputationInfo::Success;
201 numberOfIterations = solver.iterations();
202 }
203
204 } else if (solutionMethod == EigenLinearEquationSolverMethod::Gmres) {
205 if (preconditioner == EigenLinearEquationSolverPreconditioner::Ilu) {
206 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with GMRES with Ilu preconditioner (Eigen library).");
207
208 Eigen::GMRES<Eigen::SparseMatrix<ValueType>, Eigen::IncompleteLUT<ValueType>> solver;
209 solver.setTolerance(precision);
210 solver.setMaxIterations(maxIter);
211 solver.set_restart(restartThreshold);
212 solver.compute(*this->eigenA);
213 eigenX = solver.solveWithGuess(eigenB, eigenX);
214 converged = solver.info() == Eigen::ComputationInfo::Success;
215 numberOfIterations = solver.iterations();
216 } else if (preconditioner == EigenLinearEquationSolverPreconditioner::Diagonal) {
217 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with GMRES with Diagonal preconditioner (Eigen library).");
218
219 Eigen::GMRES<Eigen::SparseMatrix<ValueType>, Eigen::DiagonalPreconditioner<ValueType>> solver;
220 solver.setTolerance(precision);
221 solver.setMaxIterations(maxIter);
222 solver.set_restart(restartThreshold);
223 solver.compute(*this->eigenA);
224 eigenX = solver.solveWithGuess(eigenB, eigenX);
225 converged = solver.info() == Eigen::ComputationInfo::Success;
226 numberOfIterations = solver.iterations();
227 } else {
228 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with GMRES with identity preconditioner (Eigen library).");
229
230 Eigen::GMRES<Eigen::SparseMatrix<ValueType>, Eigen::IdentityPreconditioner> solver;
231 solver.setTolerance(precision);
232 solver.setMaxIterations(maxIter);
233 solver.set_restart(restartThreshold);
234 solver.compute(*this->eigenA);
235 eigenX = solver.solveWithGuess(eigenB, eigenX);
236 converged = solver.info() == Eigen::ComputationInfo::Success;
237 numberOfIterations = solver.iterations();
238 }
239 }
240
241 // Make sure that all results conform to the (global) bounds.
243
244 // Check if the solver converged and issue a warning otherwise.
245 if (converged) {
246 STORM_LOG_INFO("Iterative solver converged after " << numberOfIterations << " iterations.");
247 return true;
248 } else {
249 STORM_LOG_WARN("Iterative solver did not converge.");
250 return false;
251 }
252 }
253
254 return true;
255}
256
257template<typename ValueType>
261
262template<typename ValueType>
263uint64_t EigenLinearEquationSolver<ValueType>::getMatrixRowCount() const {
264 return eigenA->rows();
265}
266
267template<typename ValueType>
268uint64_t EigenLinearEquationSolver<ValueType>::getMatrixColumnCount() const {
269 return eigenA->cols();
270}
271
272template<typename ValueType>
273std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> EigenLinearEquationSolverFactory<ValueType>::create(Environment const& env) const {
274 auto solver = std::make_unique<storm::solver::EigenLinearEquationSolver<ValueType>>();
275 solver->setShowProgress(env.solver().isVerboseSet(), env.solver().getShowProgressDelay());
276 return solver;
277}
278
279template<typename ValueType>
280std::unique_ptr<LinearEquationSolverFactory<ValueType>> EigenLinearEquationSolverFactory<ValueType>::clone() const {
281 return std::make_unique<EigenLinearEquationSolverFactory<ValueType>>(*this);
282}
283
286
289
292} // namespace solver
293} // namespace storm
storm::solver::EigenLinearEquationSolverMethod const & getMethod() const
uint64_t const & getMaximalNumberOfIterations() const
uint64_t const & getRestartThreshold() const
storm::RationalNumber const & getPrecision() const
storm::solver::EigenLinearEquationSolverPreconditioner const & getPreconditioner() const
SolverEnvironment & solver()
EigenSolverEnvironment & eigen()
uint64_t getShowProgressDelay() const
static std::unique_ptr< Eigen::SparseMatrix< ValueType > > toEigenSparseMatrix(storm::storage::SparseMatrix< ValueType > const &matrix)
Converts a sparse matrix into a sparse matrix in the gmm++ format.
void setSolutionBoundsExact(std::vector< ValueType > const &x) const
virtual std::unique_ptr< LinearEquationSolverFactory< ValueType > > clone() const override
Creates a copy of this factory.
virtual std::unique_ptr< storm::solver::LinearEquationSolver< ValueType > > create(Environment const &env) const override
Creates an equation solver with the current settings, but without a matrix.
A class that uses the Eigen library to implement the LinearEquationSolver interface.
virtual void setMatrix(storm::storage::SparseMatrix< ValueType > const &A) override
virtual bool internalSolveEquations(Environment const &env, std::vector< ValueType > &x, std::vector< ValueType > const &b) const override
virtual LinearEquationSolverProblemFormat getEquationProblemFormat(Environment const &env) const override
Retrieves the format in which this solver expects to solve equations.
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_INFO(message)
Definition logging.h:34
#define STORM_LOG_WARN(message)
Definition logging.h:35
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:54
void clip(std::vector< ValueType > &x, boost::optional< ValueType > const &lowerBound, boost::optional< ValueType > const &upperBound)
Takes the input vector and ensures that all entries conform to the bounds.
Definition vector.h:892
TargetType convertNumber(SourceType const &number)