Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/methods/finitedifferences/tridiagonaloperator.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2002, 2003, 2011 Ferdinando Ametrano
5
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
6
7
 This file is part of QuantLib, a free-software/open-source library
8
 for financial quantitative analysts and developers - http://quantlib.org/
9
10
 QuantLib is free software: you can redistribute it and/or modify it
11
 under the terms of the QuantLib license.  You should have received a
12
 copy of the license along with this program; if not, please email
13
 <quantlib-dev@lists.sf.net>. The license is also available online at
14
 <https://www.quantlib.org/license.shtml>.
15
16
 This program is distributed in the hope that it will be useful, but WITHOUT
17
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 FOR A PARTICULAR PURPOSE.  See the license for more details.
19
*/
20
21
#include <ql/methods/finitedifferences/tridiagonaloperator.hpp>
22
23
namespace QuantLib {
24
25
0
    TridiagonalOperator::TridiagonalOperator(Size size) {
26
0
        if (size>=2) {
27
0
            n_ = size;
28
0
            diagonal_      = Array(size);
29
0
            lowerDiagonal_ = Array(size-1);
30
0
            upperDiagonal_ = Array(size-1);
31
0
            temp_          = Array(size);
32
0
        } else if (size==0) {
33
0
            n_ = 0;
34
0
            diagonal_      = Array(0);
35
0
            lowerDiagonal_ = Array(0);
36
0
            upperDiagonal_ = Array(0);
37
0
            temp_          = Array(0);
38
0
        } else {
39
0
            QL_FAIL("invalid size (" << size << ") for tridiagonal operator "
40
0
                    "(must be null or >= 2)");
41
0
        }
42
0
    }
43
44
    TridiagonalOperator::TridiagonalOperator(const Array& low,
45
                                             const Array& mid,
46
                                             const Array& high)
47
0
    : n_(mid.size()),
48
0
      diagonal_(mid), lowerDiagonal_(low), upperDiagonal_(high), temp_(n_) {
49
0
        QL_REQUIRE(low.size() == n_-1,
50
0
                   "low diagonal vector of size " << low.size() <<
51
0
                   " instead of " << n_-1);
52
0
        QL_REQUIRE(high.size() == n_-1,
53
0
                   "high diagonal vector of size " << high.size() <<
54
0
                   " instead of " << n_-1);
55
0
    }
56
57
0
    Array TridiagonalOperator::applyTo(const Array& v) const {
58
0
        QL_REQUIRE(n_!=0,
59
0
                   "uninitialized TridiagonalOperator");
60
0
        QL_REQUIRE(v.size()==n_,
61
0
                   "vector of the wrong size " << v.size() <<
62
0
                   " instead of " << n_);
63
0
        Array result(n_);
64
0
        std::transform(diagonal_.begin(), diagonal_.end(),
65
0
                       v.begin(),
66
0
                       result.begin(),
67
0
                       std::multiplies<>());
68
69
        // matricial product
70
0
        result[0] += upperDiagonal_[0]*v[1];
71
0
        for (Size j=1; j<=n_-2; j++)
72
0
            result[j] += lowerDiagonal_[j-1]*v[j-1]+
73
0
                upperDiagonal_[j]*v[j+1];
74
0
        result[n_-1] += lowerDiagonal_[n_-2]*v[n_-2];
75
76
0
        return result;
77
0
    }
78
79
0
    Array TridiagonalOperator::solveFor(const Array& rhs) const  {
80
0
        Array result(rhs.size());
81
0
        solveFor(rhs, result);
82
0
        return result;
83
0
    }
84
85
    void TridiagonalOperator::solveFor(const Array& rhs,
86
0
                                       Array& result) const  {
87
88
0
        QL_REQUIRE(n_!=0,
89
0
                   "uninitialized TridiagonalOperator");
90
0
        QL_REQUIRE(rhs.size()==n_,
91
0
                   "rhs vector of size " << rhs.size() <<
92
0
                   " instead of " << n_);
93
94
0
        Real bet = diagonal_[0];
95
0
        QL_REQUIRE(!close(bet, 0.0),
96
0
                   "diagonal's first element (" << bet <<
97
0
                   ") cannot be close to zero");
98
0
        result[0] = rhs[0]/bet;
99
0
        for (Size j=1; j<=n_-1; ++j) {
100
0
            temp_[j] = upperDiagonal_[j-1]/bet;
101
0
            bet = diagonal_[j]-lowerDiagonal_[j-1]*temp_[j];
102
0
            QL_ENSURE(!close(bet, 0.0), "division by zero");
103
0
            result[j] = (rhs[j] - lowerDiagonal_[j-1]*result[j-1])/bet;
104
0
        }
105
        // cannot be j>=0 with Size j
106
0
        for (Size j=n_-2; j>0; --j)
107
0
            result[j] -= temp_[j+1]*result[j+1];
108
0
        result[0] -= temp_[1]*result[1];
109
0
    }
110
111
    Array TridiagonalOperator::SOR(const Array& rhs,
112
0
                                   Real tol) const {
113
0
        QL_REQUIRE(n_!=0,
114
0
                   "uninitialized TridiagonalOperator");
115
0
        QL_REQUIRE(rhs.size()==n_,
116
0
                   "rhs vector of size " << rhs.size() <<
117
0
                   " instead of " << n_);
118
119
        // initial guess
120
0
        Array result = rhs;
121
122
        // solve tridiagonal system with SOR technique
123
0
        Real omega = 1.5;
124
0
        Real err = 2.0*tol;
125
0
        Real temp;
126
0
        for (Size sorIteration=0; err>tol ; ++sorIteration) {
127
0
            QL_REQUIRE(sorIteration<100000,
128
0
                       "tolerance (" << tol << ") not reached in " <<
129
0
                       sorIteration << " iterations. " <<
130
0
                       "The error still is " << err);
131
132
0
            temp = omega * (rhs[0]     -
133
0
                            upperDiagonal_[0]   * result[1]-
134
0
                            diagonal_[0]        * result[0])/diagonal_[0];
135
0
            err = temp*temp;
136
0
            result[0] += temp;
137
0
            Size i;
138
0
            for (i=1; i<n_-1 ; ++i) {
139
0
                temp = omega *(rhs[i]     -
140
0
                               upperDiagonal_[i]   * result[i+1]-
141
0
                               diagonal_[i]        * result[i] -
142
0
                               lowerDiagonal_[i-1] * result[i-1])/diagonal_[i];
143
0
                err += temp * temp;
144
0
                result[i] += temp;
145
0
            }
146
147
0
            temp = omega * (rhs[i]     -
148
0
                            diagonal_[i]        * result[i] -
149
0
                            lowerDiagonal_[i-1] * result[i-1])/diagonal_[i];
150
0
            err += temp*temp;
151
0
            result[i] += temp;
152
0
        }
153
0
        return result;
154
0
    }
155
156
0
    TridiagonalOperator TridiagonalOperator::identity(Size size) {
157
0
        return TridiagonalOperator(Array(size-1, 0.0),     // lower diagonal
158
0
                                   Array(size,   1.0),     // diagonal
159
0
                                   Array(size-1, 0.0));    // upper diagonal
160
0
    }
161
162
}