/src/quantlib/ql/math/matrixutilities/qrdecomposition.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2008 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 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 | | /*! \file qrdecomposition.cpp |
21 | | \brief QR decomposition |
22 | | */ |
23 | | |
24 | | #include <ql/math/optimization/lmdif.hpp> |
25 | | #include <ql/math/matrixutilities/qrdecomposition.hpp> |
26 | | #include <memory> |
27 | | |
28 | | namespace QuantLib { |
29 | | |
30 | | std::vector<Size> qrDecomposition(const Matrix& M, |
31 | | Matrix& q, |
32 | | Matrix& r, |
33 | 0 | bool pivot) { |
34 | 0 | Matrix mT = transpose(M); |
35 | 0 | const Size m = M.rows(); |
36 | 0 | const Size n = M.columns(); |
37 | |
|
38 | 0 | std::unique_ptr<int[]> lipvt(new int[n]); |
39 | 0 | std::unique_ptr<Real[]> rdiag(new Real[n]); |
40 | 0 | std::unique_ptr<Real[]> wa(new Real[n]); |
41 | |
|
42 | 0 | MINPACK::qrfac(m, n, mT.begin(), 0, (pivot)?1:0, |
43 | 0 | lipvt.get(), n, rdiag.get(), rdiag.get(), wa.get()); |
44 | 0 | if (r.columns() != n || r.rows() !=n) |
45 | 0 | r = Matrix(n, n); |
46 | |
|
47 | 0 | for (Size i=0; i < n; ++i) { |
48 | 0 | std::fill(r.row_begin(i), r.row_begin(i)+i, 0.0); |
49 | 0 | r[i][i] = rdiag[i]; |
50 | 0 | if (i < m) { |
51 | 0 | std::copy(mT.column_begin(i)+i+1, mT.column_end(i), |
52 | 0 | r.row_begin(i)+i+1); |
53 | 0 | } |
54 | 0 | else { |
55 | 0 | std::fill(r.row_begin(i)+i+1, r.row_end(i), 0.0); |
56 | 0 | } |
57 | 0 | } |
58 | |
|
59 | 0 | if (q.rows() != m || q.columns() != n) |
60 | 0 | q = Matrix(m, n); |
61 | |
|
62 | 0 | if (m > n) { |
63 | 0 | std::fill(q.begin(), q.end(), 0.0); |
64 | |
|
65 | 0 | Integer u = std::min(n,m); |
66 | 0 | for (Integer i=0; i < u; ++i) |
67 | 0 | q[i][i] = 1.0; |
68 | |
|
69 | 0 | Array v(m); |
70 | 0 | for (Integer i=u-1; i >=0; --i) { |
71 | 0 | if (std::fabs(mT[i][i]) > QL_EPSILON) { |
72 | 0 | const Real tau = 1.0/mT[i][i]; |
73 | |
|
74 | 0 | std::fill(v.begin(), v.begin()+i, 0.0); |
75 | 0 | std::copy(mT.row_begin(i)+i, mT.row_end(i), v.begin()+i); |
76 | |
|
77 | 0 | Array w(n, 0.0); |
78 | 0 | for (Size l=0; l < n; ++l) |
79 | 0 | w[l] += std::inner_product( |
80 | 0 | v.begin()+i, v.end(), q.column_begin(l)+i, Real(0.0)); |
81 | |
|
82 | 0 | for (Size k=i; k < m; ++k) { |
83 | 0 | const Real a = tau*v[k]; |
84 | 0 | for (Size l=0; l < n; ++l) |
85 | 0 | q[k][l] -= a*w[l]; |
86 | 0 | } |
87 | 0 | } |
88 | 0 | } |
89 | 0 | } |
90 | 0 | else { |
91 | 0 | Array w(m); |
92 | 0 | for (Size k=0; k < m; ++k) { |
93 | 0 | std::fill(w.begin(), w.end(), 0.0); |
94 | 0 | w[k] = 1.0; |
95 | |
|
96 | 0 | for (Size j=0; j < std::min(n, m); ++j) { |
97 | 0 | const Real t3 = mT[j][j]; |
98 | 0 | if (t3 != 0.0) { |
99 | 0 | const Real t |
100 | 0 | = std::inner_product(mT.row_begin(j)+j, mT.row_end(j), |
101 | 0 | w.begin()+j, Real(0.0))/t3; |
102 | 0 | for (Size i=j; i<m; ++i) { |
103 | 0 | w[i]-=mT[j][i]*t; |
104 | 0 | } |
105 | 0 | } |
106 | 0 | q[k][j] = w[j]; |
107 | 0 | } |
108 | 0 | std::fill(q.row_begin(k) + std::min(n, m), q.row_end(k), 0.0); |
109 | 0 | } |
110 | 0 | } |
111 | |
|
112 | 0 | std::vector<Size> ipvt(n); |
113 | |
|
114 | 0 | if (pivot) { |
115 | 0 | std::copy(lipvt.get(), lipvt.get()+n, ipvt.begin()); |
116 | 0 | } |
117 | 0 | else { |
118 | 0 | for (Size i=0; i < n; ++i) |
119 | 0 | ipvt[i] = i; |
120 | 0 | } |
121 | |
|
122 | 0 | return ipvt; |
123 | 0 | } |
124 | | |
125 | | Array qrSolve(const Matrix& a, const Array& b, |
126 | 0 | bool pivot, const Array& d) { |
127 | 0 | const Size m = a.rows(); |
128 | 0 | const Size n = a.columns(); |
129 | |
|
130 | 0 | QL_REQUIRE(b.size() == m, "dimensions of A and b don't match"); |
131 | 0 | QL_REQUIRE(d.size() == n || d.empty(), |
132 | 0 | "dimensions of A and d don't match"); |
133 | | |
134 | 0 | Matrix q(m, n), r(n, n); |
135 | |
|
136 | 0 | std::vector<Size> lipvt = qrDecomposition(a, q, r, pivot); |
137 | |
|
138 | 0 | std::unique_ptr<int[]> ipvt(new int[n]); |
139 | 0 | std::copy(lipvt.begin(), lipvt.end(), ipvt.get()); |
140 | |
|
141 | 0 | Matrix rT = transpose(r); |
142 | |
|
143 | 0 | std::unique_ptr<Real[]> sdiag(new Real[n]); |
144 | 0 | std::unique_ptr<Real[]> wa(new Real[n]); |
145 | |
|
146 | 0 | Array ld(n, 0.0); |
147 | 0 | if (!d.empty()) { |
148 | 0 | std::copy(d.begin(), d.end(), ld.begin()); |
149 | 0 | } |
150 | |
|
151 | 0 | Array x(n); |
152 | 0 | Array qtb = transpose(q)*b; |
153 | |
|
154 | 0 | MINPACK::qrsolv(n, rT.begin(), n, ipvt.get(), |
155 | 0 | ld.begin(), qtb.begin(), |
156 | 0 | x.begin(), sdiag.get(), wa.get()); |
157 | |
|
158 | 0 | return x; |
159 | 0 | } |
160 | | } |