Coverage Report

Created: 2026-03-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/math/matrixutilities/sparseilupreconditioner.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
Copyright (C) 2009 Ralph Schreyer
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 license 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
#include <ql/math/matrixutilities/sparseilupreconditioner.hpp>
21
#include <ql/math/matrix.hpp>
22
23
#include <set>
24
25
namespace QuantLib {
26
27
    using namespace boost::numeric::ublas;
28
29
    SparseILUPreconditioner::SparseILUPreconditioner(const SparseMatrix& A,
30
                                                     Integer lfil)
31
0
    : L_(A.size1(),A.size2()),
32
0
      U_(A.size1(),A.size2()) {
33
34
0
        QL_REQUIRE(A.size1() == A.size2(),
35
0
                   "sparse ILU preconditioner works only with square matrices");
36
37
0
        for (SparseMatrix::size_type i=0; i < L_.size1(); ++i)
38
0
            L_(i,i) = 1.0;
39
40
0
        const Integer n = A.size1();
41
0
        std::set<Integer> lBandSet, uBandSet;
42
43
0
        compressed_matrix<Integer> levs(n,n);
44
0
        Integer lfilp = lfil + 1;
45
46
0
        for (Integer ii=0; ii<n; ++ii) {
47
0
            Array w(n);
48
0
            for(Integer k=0; k<n; ++k) {
49
0
                w[k] = A(ii,k);
50
0
            }
51
52
0
            std::vector<Integer> levii(n, 0);
53
0
            for (Integer i=0; i<n; ++i) {
54
0
                if(   w[i] > QL_EPSILON
55
0
                      || w[i] < -1.0*QL_EPSILON) levii[i] = 1;
56
0
            }
57
0
            Integer jj = -1;
58
0
            while (jj < ii) {
59
0
                for (Integer k=jj+1; k<n; ++k) {
60
0
                    if (levii[k] != 0) {
61
0
                        jj = k;
62
0
                        break;
63
0
                    }
64
0
                }
65
0
                if (jj >= ii) {
66
0
                    break;
67
0
                }
68
0
                Integer jlev = levii[jj];
69
0
                if (jlev <= lfilp) {
70
0
                    std::vector<Integer> nonZeros;
71
0
                    std::vector<Real> nonZeroEntries;
72
0
                    nonZeros.reserve(uBandSet.size()+1);
73
0
                    nonZeroEntries.reserve(uBandSet.size()+1);
74
0
                    const Real entry = U_(jj,jj);
75
0
                    if(entry > QL_EPSILON || entry < -1.0*QL_EPSILON) {
76
0
                        nonZeros.push_back(jj);
77
0
                        nonZeroEntries.push_back(entry);
78
0
                    }
79
0
                    auto iter = uBandSet.begin();
80
0
                    auto end = uBandSet.end();
81
0
                    for (; iter != end; ++iter) {
82
0
                        const Real entry = U_(jj,jj+*iter);
83
0
                        if(entry > QL_EPSILON || entry < -1.0*QL_EPSILON) {
84
0
                            nonZeros.push_back(jj+*iter);
85
0
                            nonZeroEntries.push_back(entry);
86
0
                        }
87
0
                    }
88
0
                    Real fact = w[jj];
89
0
                    if(!nonZeroEntries.empty()) {
90
0
                        fact /= nonZeroEntries[0];
91
0
                    }
92
0
                    for (Size k=0; k<nonZeros.size(); ++k) {
93
0
                        const Integer j = nonZeros[k] ;
94
0
                        const Integer temp = levs(jj,j) + jlev ;
95
0
                        if (levii[j] == 0) {
96
0
                            if (temp <= lfilp) {
97
0
                                w[j] =  - fact*nonZeroEntries[k];
98
0
                                levii[j] = temp;
99
0
                            }
100
0
                        }
101
0
                        else {
102
0
                            w[j] -= fact*nonZeroEntries[k];
103
0
                            levii[j] = std::min(levii[j],temp);
104
0
                        }
105
0
                    }
106
0
                    w[jj] = fact;
107
0
                }
108
0
            }
109
0
            std::vector<Integer> wNonZeros;
110
0
            std::vector<Real> wNonZeroEntries;
111
0
            wNonZeros.reserve(w.size());
112
0
            wNonZeroEntries.reserve(w.size());
113
0
            for (Size i=0; i<w.size(); ++i) {
114
0
                const Real entry = w[i];
115
0
                if(entry > QL_EPSILON || entry < -1.0*QL_EPSILON) {
116
0
                    wNonZeros.push_back(i);
117
0
                    wNonZeroEntries.push_back(entry);
118
0
                }
119
0
            }
120
0
            std::vector<Integer> leviiNonZeroEntries;
121
0
            leviiNonZeroEntries.reserve(levii.size());
122
0
            for (int entry : levii) {
123
0
                if (entry > QL_EPSILON || entry < -1.0 * QL_EPSILON) {
124
0
                    leviiNonZeroEntries.push_back(entry);
125
0
                }
126
0
            }
127
0
            for (Size k=0; k<wNonZeros.size(); ++k) {
128
0
                Integer j = wNonZeros[k];
129
0
                if (j < ii) {
130
0
                    L_(ii,j) = wNonZeroEntries[k];
131
0
                    lBandSet.insert(ii-j);
132
0
                }
133
0
                else {
134
0
                    U_(ii,j) = wNonZeroEntries[k];
135
0
                    levs(ii,j) = leviiNonZeroEntries[k];
136
0
                    if(j-ii > 0) {
137
0
                        uBandSet.insert(j-ii);
138
0
                    }
139
0
                }
140
0
            }
141
0
        }
142
0
        lBands_.resize(lBandSet.size());
143
0
        uBands_.resize(uBandSet.size());
144
0
        std::copy(lBandSet.begin(), lBandSet.end(), lBands_.begin());
145
0
        std::copy(uBandSet.begin(), uBandSet.end(), uBands_.begin());
146
0
    }
147
148
0
    const SparseMatrix& SparseILUPreconditioner::L() const {
149
0
        return L_;
150
0
    }
151
152
0
    const SparseMatrix& SparseILUPreconditioner::U() const {
153
0
        return U_;
154
0
    }
155
156
0
    Array SparseILUPreconditioner::apply(const Array& b) const {
157
0
        return backwardSolve(forwardSolve(b));
158
0
    }
159
160
0
    Array SparseILUPreconditioner::forwardSolve(const Array& b) const {
161
0
        Integer n = b.size();
162
0
        Array y(n, 0.0);
163
0
        y[0]=b[0]/L_(0,0);
164
0
        for (Integer i=1; i<=n-1; ++i) {
165
0
            y[i] = b[i]/L_(i,i);
166
0
            for (Integer j=lBands_.size()-1;
167
0
                 j>=0 && i-Integer(lBands_[j]) <= i-1; --j) {
168
0
                const Integer k = i-Integer(lBands_[j]);
169
0
                if (k >= 0)
170
0
                    y[i]-=L_(i,k)*y[k]/L_(i,i);
171
0
            }
172
0
        }
173
0
        return y;
174
0
    }
175
176
0
    Array SparseILUPreconditioner::backwardSolve(const Array& y) const {
177
0
        Size n = y.size();
178
0
        Array x(n, 0.0);
179
0
        x[n-1] = y[n-1]/U_(n-1,n-1);
180
0
        for (Integer i=n-2; i>=0; --i) {
181
0
            x[i] = y[i]/U_(i,i);
182
0
            for (Size j=0; j<uBands_.size() && i+uBands_[j] <= n-1; ++j) {
183
0
                x[i] -= U_(i,i+uBands_[j])*x[i+uBands_[j]]/U_(i,i);
184
0
            }
185
0
        }
186
0
        return x;
187
0
    }
188
189
}
190