Coverage Report

Created: 2025-08-05 06:45

/src/quantlib/ql/math/matrixutilities/symmetricschurdecomposition.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2003 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
 <http://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/math/matrixutilities/symmetricschurdecomposition.hpp>
22
#include <vector>
23
24
namespace QuantLib {
25
26
    SymmetricSchurDecomposition::SymmetricSchurDecomposition(const Matrix & s)
27
0
    : diagonal_(s.rows()), eigenVectors_(s.rows(), s.columns(), 0.0) {
28
29
0
        QL_REQUIRE(s.rows() > 0 && s.columns() > 0, "null matrix given");
30
0
        QL_REQUIRE(s.rows()==s.columns(), "input matrix must be square");
31
32
0
        Size size = s.rows();
33
0
        for (Size q=0; q<size; q++) {
34
0
            diagonal_[q] = s[q][q];
35
0
            eigenVectors_[q][q] = 1.0;
36
0
        }
37
0
        Matrix ss = s;
38
39
0
        std::vector<Real> tmpDiag(diagonal_.begin(), diagonal_.end());
40
0
        std::vector<Real> tmpAccumulate(size, 0.0);
41
0
        Real threshold, epsPrec = 1e-15;
42
0
        bool keeplooping = true;
43
0
        Size maxIterations = 100, ite = 1;
44
0
        do {
45
            //main loop
46
0
            Real sum = 0;
47
0
            for (Size a=0; a<size-1; a++) {
48
0
                for (Size b=a+1; b<size; b++) {
49
0
                    sum += std::fabs(ss[a][b]);
50
0
                }
51
0
            }
52
53
0
            if (sum==0) {
54
0
                keeplooping = false;
55
0
            } else {
56
                /* To speed up computation a threshold is introduced to
57
                   make sure it is worthy to perform the Jacobi rotation
58
                */
59
0
                if (ite<5) threshold = 0.2*sum/(size*size);
60
0
                else       threshold = 0.0;
61
62
0
                Size j, k, l;
63
0
                for (j=0; j<size-1; j++) {
64
0
                    for (k=j+1; k<size; k++) {
65
0
                        Real sine, rho, cosin, heig, tang, beta;
66
0
                        Real smll = std::fabs(ss[j][k]);
67
0
                        if(ite> 5 &&
68
0
                           smll<epsPrec*std::fabs(diagonal_[j]) &&
69
0
                           smll<epsPrec*std::fabs(diagonal_[k])) {
70
0
                                ss[j][k] = 0;
71
0
                        } else if (std::fabs(ss[j][k])>threshold) {
72
0
                            heig = diagonal_[k]-diagonal_[j];
73
0
                            if (smll<epsPrec*std::fabs(heig)) {
74
0
                                tang = ss[j][k]/heig;
75
0
                            } else {
76
0
                                beta = 0.5*heig/ss[j][k];
77
0
                                tang = 1.0/(std::fabs(beta)+
78
0
                                    std::sqrt(1+beta*beta));
79
0
                                if (beta<0)
80
0
                                    tang = -tang;
81
0
                            }
82
0
                            cosin = 1/std::sqrt(1+tang*tang);
83
0
                            sine = tang*cosin;
84
0
                            rho = sine/(1+cosin);
85
0
                            heig = tang*ss[j][k];
86
0
                            tmpAccumulate[j] -= heig;
87
0
                            tmpAccumulate[k] += heig;
88
0
                            diagonal_[j] -= heig;
89
0
                            diagonal_[k] += heig;
90
0
                            ss[j][k] = 0.0;
91
0
                            for (l=0; l+1<=j; l++)
92
0
                                jacobiRotate_(ss, rho, sine, l, j, l, k);
93
0
                            for (l=j+1; l<=k-1; l++)
94
0
                                jacobiRotate_(ss, rho, sine, j, l, l, k);
95
0
                            for (l=k+1; l<size; l++)
96
0
                                jacobiRotate_(ss, rho, sine, j, l, k, l);
97
0
                            for (l=0;   l<size; l++)
98
0
                                jacobiRotate_(eigenVectors_,
99
0
                                                  rho, sine, l, j, l, k);
100
0
                        }
101
0
                    }
102
0
                }
103
0
                for (k=0; k<size; k++) {
104
0
                    tmpDiag[k] += tmpAccumulate[k];
105
0
                    diagonal_[k] = tmpDiag[k];
106
0
                    tmpAccumulate[k] = 0.0;
107
0
                }
108
0
            }
109
0
        } while (++ite<=maxIterations && keeplooping);
110
111
0
        QL_ENSURE(ite<=maxIterations,
112
0
                  "Too many iterations (" << maxIterations << ") reached");
113
114
115
        // sort (eigenvalues, eigenvectors)
116
0
        std::vector<std::pair<Real, std::vector<Real> > > temp(size);
117
0
        std::vector<Real> eigenVector(size);
118
0
        Size row, col;
119
0
        for (col=0; col<size; col++) {
120
0
            std::copy(eigenVectors_.column_begin(col),
121
0
                      eigenVectors_.column_end(col), eigenVector.begin());
122
0
            temp[col] = std::make_pair(diagonal_[col], eigenVector);
123
0
        }
124
0
        std::sort(temp.begin(), temp.end(), std::greater<>());
125
0
        Real maxEv = temp[0].first;
126
0
        for (col=0; col<size; col++) {
127
            // check for round-off errors
128
0
            diagonal_[col] =
129
0
                (std::fabs(temp[col].first/maxEv)<1e-16 ? 0.0 :
130
0
                                                          temp[col].first);
131
0
            Real sign = 1.0;
132
0
            if (temp[col].second[0]<0.0)
133
0
                sign = -1.0;
134
0
            for (row=0; row<size; row++) {
135
0
                eigenVectors_[row][col] = sign * temp[col].second[row];
136
0
            }
137
0
        }
138
0
    }
139
140
}