| 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 | <http://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 | bool pivot) { |
| 34 | Matrix mT = transpose(m: M); |
| 35 | const Size m = M.rows(); |
| 36 | const Size n = M.columns(); |
| 37 | |
| 38 | std::unique_ptr<int[]> lipvt(new int[n]); |
| 39 | std::unique_ptr<Real[]> rdiag(new Real[n]); |
| 40 | std::unique_ptr<Real[]> wa(new Real[n]); |
| 41 | |
| 42 | MINPACK::qrfac(m, n, a: mT.begin(), 0, pivot: (pivot)?1:0, |
| 43 | ipvt: lipvt.get(), n, rdiag: rdiag.get(), acnorm: rdiag.get(), wa: wa.get()); |
| 44 | if (r.columns() != n || r.rows() !=n) |
| 45 | r = Matrix(n, n); |
| 46 | |
| 47 | for (Size i=0; i < n; ++i) { |
| 48 | std::fill(first: r.row_begin(i), last: r.row_begin(i)+i, value: 0.0); |
| 49 | r[i][i] = rdiag[i]; |
| 50 | if (i < m) { |
| 51 | std::copy(first: mT.column_begin(i)+i+1, last: mT.column_end(i), |
| 52 | result: r.row_begin(i)+i+1); |
| 53 | } |
| 54 | else { |
| 55 | std::fill(first: r.row_begin(i)+i+1, last: r.row_end(i), value: 0.0); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | if (q.rows() != m || q.columns() != n) |
| 60 | q = Matrix(m, n); |
| 61 | |
| 62 | if (m > n) { |
| 63 | std::fill(first: q.begin(), last: q.end(), value: 0.0); |
| 64 | |
| 65 | Integer u = std::min(a: n,b: m); |
| 66 | for (Integer i=0; i < u; ++i) |
| 67 | q[i][i] = 1.0; |
| 68 | |
| 69 | Array v(m); |
| 70 | for (Integer i=u-1; i >=0; --i) { |
| 71 | if (std::fabs(x: mT[i][i]) > QL_EPSILON) { |
| 72 | const Real tau = 1.0/mT[i][i]; |
| 73 | |
| 74 | std::fill(first: v.begin(), last: v.begin()+i, value: 0.0); |
| 75 | std::copy(first: mT.row_begin(i)+i, last: mT.row_end(i), result: v.begin()+i); |
| 76 | |
| 77 | Array w(n, 0.0); |
| 78 | for (Size l=0; l < n; ++l) |
| 79 | w[l] += std::inner_product( |
| 80 | first1: v.begin()+i, last1: v.end(), first2: q.column_begin(i: l)+i, init: Real(0.0)); |
| 81 | |
| 82 | for (Size k=i; k < m; ++k) { |
| 83 | const Real a = tau*v[k]; |
| 84 | for (Size l=0; l < n; ++l) |
| 85 | q[k][l] -= a*w[l]; |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | else { |
| 91 | Array w(m); |
| 92 | for (Size k=0; k < m; ++k) { |
| 93 | std::fill(first: w.begin(), last: w.end(), value: 0.0); |
| 94 | w[k] = 1.0; |
| 95 | |
| 96 | for (Size j=0; j < std::min(a: n, b: m); ++j) { |
| 97 | const Real t3 = mT[j][j]; |
| 98 | if (t3 != 0.0) { |
| 99 | const Real t |
| 100 | = std::inner_product(first1: mT.row_begin(i: j)+j, last1: mT.row_end(i: j), |
| 101 | first2: w.begin()+j, init: Real(0.0))/t3; |
| 102 | for (Size i=j; i<m; ++i) { |
| 103 | w[i]-=mT[j][i]*t; |
| 104 | } |
| 105 | } |
| 106 | q[k][j] = w[j]; |
| 107 | } |
| 108 | std::fill(first: q.row_begin(i: k) + std::min(a: n, b: m), last: q.row_end(i: k), value: 0.0); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | std::vector<Size> ipvt(n); |
| 113 | |
| 114 | if (pivot) { |
| 115 | std::copy(first: lipvt.get(), last: lipvt.get()+n, result: ipvt.begin()); |
| 116 | } |
| 117 | else { |
| 118 | for (Size i=0; i < n; ++i) |
| 119 | ipvt[i] = i; |
| 120 | } |
| 121 | |
| 122 | return ipvt; |
| 123 | } |
| 124 | |
| 125 | Array qrSolve(const Matrix& a, const Array& b, |
| 126 | bool pivot, const Array& d) { |
| 127 | const Size m = a.rows(); |
| 128 | const Size n = a.columns(); |
| 129 | |
| 130 | QL_REQUIRE(b.size() == m, "dimensions of A and b don't match" ); |
| 131 | QL_REQUIRE(d.size() == n || d.empty(), |
| 132 | "dimensions of A and d don't match" ); |
| 133 | |
| 134 | Matrix q(m, n), r(n, n); |
| 135 | |
| 136 | std::vector<Size> lipvt = qrDecomposition(M: a, q, r, pivot); |
| 137 | |
| 138 | std::unique_ptr<int[]> ipvt(new int[n]); |
| 139 | std::copy(first: lipvt.begin(), last: lipvt.end(), result: ipvt.get()); |
| 140 | |
| 141 | Matrix rT = transpose(m: r); |
| 142 | |
| 143 | std::unique_ptr<Real[]> sdiag(new Real[n]); |
| 144 | std::unique_ptr<Real[]> wa(new Real[n]); |
| 145 | |
| 146 | Array ld(n, 0.0); |
| 147 | if (!d.empty()) { |
| 148 | std::copy(first: d.begin(), last: d.end(), result: ld.begin()); |
| 149 | } |
| 150 | |
| 151 | Array x(n); |
| 152 | Array qtb = transpose(m: q)*b; |
| 153 | |
| 154 | MINPACK::qrsolv(n, r: rT.begin(), ldr: n, ipvt: ipvt.get(), |
| 155 | diag: ld.begin(), qtb: qtb.begin(), |
| 156 | x: x.begin(), sdiag: sdiag.get(), wa: wa.get()); |
| 157 | |
| 158 | return x; |
| 159 | } |
| 160 | } |
| 161 | |