Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}