Coverage Report

Created: 2025-11-04 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}