/src/quantlib/ql/math/matrixutilities/gmres.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2017 Klaus Spanderen |
5 | | |
6 | | This file is part of QuantLib, a free-software/open-source library |
7 | | for financial quantitative analysts and developers - http://quantlib.org/ |
8 | | |
9 | | QuantLib is free software: you can redistribute it and/or modify it |
10 | | under the terms of the QuantLib license. You should have received a |
11 | | copy of the license0/0 iee along with this program; if not, please email |
12 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
13 | | <https://www.quantlib.org/license.shtml>. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
18 | | */ |
19 | | |
20 | | /*! \file gmres.cpp |
21 | | \brief generalized minimal residual method |
22 | | */ |
23 | | |
24 | | |
25 | | #include <ql/math/functional.hpp> |
26 | | #include <ql/math/matrixutilities/gmres.hpp> |
27 | | #include <ql/math/matrixutilities/qrdecomposition.hpp> |
28 | | #include <numeric> |
29 | | #include <utility> |
30 | | |
31 | | namespace QuantLib { |
32 | | |
33 | | GMRES::GMRES(GMRES::MatrixMult A, Size maxIter, Real relTol, GMRES::MatrixMult preConditioner) |
34 | 0 | : A_(std::move(A)), M_(std::move(preConditioner)), maxIter_(maxIter), relTol_(relTol) { |
35 | |
|
36 | 0 | QL_REQUIRE(maxIter_ > 0, "maxIter must be greater than zero"); |
37 | 0 | } |
38 | | |
39 | 0 | GMRESResult GMRES::solve(const Array& b, const Array& x0) const { |
40 | 0 | GMRESResult result = solveImpl(b, x0); |
41 | |
|
42 | 0 | QL_REQUIRE(result.errors.back() < relTol_, "could not converge"); |
43 | | |
44 | 0 | return result; |
45 | 0 | } |
46 | | |
47 | | GMRESResult GMRES::solveWithRestart( |
48 | 0 | Size restart, const Array& b, const Array& x0) const { |
49 | |
|
50 | 0 | GMRESResult result = solveImpl(b, x0); |
51 | |
|
52 | 0 | std::list<Real> errors = result.errors; |
53 | |
|
54 | 0 | for (Size i=0; i < restart-1 && result.errors.back() >= relTol_;++i) { |
55 | 0 | result = solveImpl(b, result.x); |
56 | 0 | errors.insert( |
57 | 0 | errors.end(), result.errors.begin(), result.errors.end()); |
58 | 0 | } |
59 | |
|
60 | 0 | QL_REQUIRE(errors.back() < relTol_, "could not converge"); |
61 | | |
62 | 0 | result.errors = errors; |
63 | 0 | return result; |
64 | 0 | } |
65 | | |
66 | 0 | GMRESResult GMRES::solveImpl(const Array& b, const Array& x0) const { |
67 | 0 | const Real bn = Norm2(b); |
68 | 0 | if (bn == 0.0) { |
69 | 0 | GMRESResult result = { std::list<Real>(1, 0.0), b }; |
70 | 0 | return result; |
71 | 0 | } |
72 | | |
73 | 0 | Array x = ((!x0.empty()) ? x0 : Array(b.size(), 0.0)); |
74 | 0 | Array r = b - A_(x); |
75 | |
|
76 | 0 | const Real g = Norm2(r); |
77 | 0 | if (g/bn < relTol_) { |
78 | 0 | GMRESResult result = { std::list<Real>(1, g/bn), x }; |
79 | 0 | return result; |
80 | 0 | } |
81 | | |
82 | 0 | std::vector<Array> v(1, r/g); |
83 | 0 | std::vector<Array> h(1, Array(maxIter_, 0.0)); |
84 | 0 | std::vector<Real> c(maxIter_+1), s(maxIter_+1), z(maxIter_+1); |
85 | |
|
86 | 0 | z[0] = g; |
87 | |
|
88 | 0 | std::list<Real> errors(1, g/bn); |
89 | |
|
90 | 0 | for (Size j=0; j < maxIter_ && errors.back() >= relTol_; ++j) { |
91 | 0 | h.emplace_back(maxIter_, 0.0); |
92 | 0 | Array w = A_(!M_ ? v[j] : M_(v[j])); |
93 | |
|
94 | 0 | for (Size i=0; i <= j; ++i) { |
95 | 0 | h[i][j] = DotProduct(w, v[i]); |
96 | 0 | w -= h[i][j] * v[i]; |
97 | 0 | } |
98 | |
|
99 | 0 | h[j+1][j] = Norm2(w); |
100 | |
|
101 | 0 | if (h[j+1][j] < QL_EPSILON*QL_EPSILON) |
102 | 0 | break; |
103 | | |
104 | 0 | v.push_back(w / h[j+1][j]); |
105 | |
|
106 | 0 | for (Size i=0; i < j; ++i) { |
107 | 0 | const Real h0 = c[i]*h[i][j] + s[i]*h[i+1][j]; |
108 | 0 | const Real h1 =-s[i]*h[i][j] + c[i]*h[i+1][j]; |
109 | |
|
110 | 0 | h[i][j] = h0; |
111 | 0 | h[i+1][j] = h1; |
112 | 0 | } |
113 | |
|
114 | 0 | const Real nu = std::sqrt(squared(h[j][j]) + squared(h[j+1][j])); |
115 | |
|
116 | 0 | c[j] = h[j][j]/nu; |
117 | 0 | s[j] = h[j+1][j]/nu; |
118 | |
|
119 | 0 | h[j][j] = nu; |
120 | 0 | h[j+1][j] = 0.0; |
121 | |
|
122 | 0 | z[j+1] = -s[j]*z[j]; |
123 | 0 | z[j] = c[j] * z[j]; |
124 | |
|
125 | 0 | errors.push_back(std::fabs(z[j+1]/bn)); |
126 | 0 | } |
127 | |
|
128 | 0 | const Size k = v.size()-1; |
129 | |
|
130 | 0 | Array y(k, 0.0); |
131 | 0 | y[k-1]=z[k-1]/h[k-1][k-1]; |
132 | |
|
133 | 0 | for (Integer i=k-2; i >= 0; --i) { |
134 | 0 | y[i] = (z[i] - std::inner_product( |
135 | 0 | h[i].begin()+i+1, h[i].begin()+k, y.begin()+i+1, Real(0.0)))/h[i][i]; |
136 | 0 | } |
137 | |
|
138 | 0 | Array xm = std::inner_product( |
139 | 0 | v.begin(), v.begin()+k, y.begin(), Array(x.size(), Real(0.0))); |
140 | |
|
141 | 0 | xm = x + (!M_ ? xm : M_(xm)); |
142 | |
|
143 | 0 | GMRESResult result = { errors, xm }; |
144 | 0 | return result; |
145 | 0 | } |
146 | | |
147 | | } |