/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 | | } |