Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
blockmatrixinverse.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <boost/numeric/ublas/lu.hpp>
22
23using namespace boost::numeric::ublas;
24
25namespace QuantExt {
26using namespace QuantLib;
27
28namespace {
29bool isNull(const Matrix& A) {
30 for (auto const& a : A) {
31 if (std::abs(a) > QL_EPSILON)
32 return false;
33 }
34 return true;
35} // isNull
36
37bool isNull(const QuantLib::SparseMatrix& A) {
38 for (auto i1 = A.begin1(); i1 != A.end1(); ++i1) {
39 for (auto i2 = i1.begin(); i2 != i1.end(); ++i2) {
40 if (std::abs(*i2) > QL_EPSILON)
41 return false;
42 }
43 }
44 return true;
45} // isNull
46
47} // namespace
48
49QuantLib::SparseMatrix inverse(QuantLib::SparseMatrix m) {
50 QL_REQUIRE(m.size1() == m.size2(), "matrix is not square");
51 boost::numeric::ublas::permutation_matrix<Size> pert(m.size1());
52 // lu decomposition
53 const Size singular = lu_factorize(m, pert);
54 QL_REQUIRE(singular == 0, "singular matrix given");
55 QuantLib::SparseMatrix inverse = boost::numeric::ublas::identity_matrix<Real>(m.size1());
56 // backsubstitution
57 boost::numeric::ublas::lu_substitute(m, pert, inverse);
58 return inverse;
59}
60
61Matrix blockMatrixInverse(const Matrix& A, const std::vector<Size>& blockIndices) {
62
63 QL_REQUIRE(blockIndices.size() > 0, "blockMatrixInverse: at least one entry in blockIndices required");
64 Size n = blockIndices.back();
65 QL_REQUIRE(n > 0 && A.rows() == A.columns() && A.rows() == n,
66 "blockMatrixInverse: matrix (" << A.rows() << "x" << A.columns() << ") must be square of size " << n
67 << "x" << n << ", n>0");
68
69 if (blockIndices.size() == 1) {
70 Matrix res = inverse(A);
71 return res;
72 }
73
74 Size mid = (blockIndices.size() - 1) / 2;
75 Size m = blockIndices[mid];
76 QL_REQUIRE(m > 0 && m < n,
77 "blockMatrixInverse: expected m (" << m << ") to be positive and less than n (" << n << ")");
78 std::vector<Size> leftIndices(blockIndices.begin(), blockIndices.begin() + (mid + 1));
79 std::vector<Size> rightIndices(blockIndices.begin() + (mid + 1), blockIndices.end());
80 for (auto& i : rightIndices) {
81 i -= m;
82 }
83 QL_REQUIRE(leftIndices.size() > 0, "blockMatrixInverse: expected left indices to be non-empty");
84 QL_REQUIRE(rightIndices.size() > 0, "blockMatrixInverse: expected right indices to be non-empty");
85
86 // FIXME it would be nice if we could avoid building these submatrices, instead use a view
87 // on parts of the original matrix
88 Matrix a(m, m), b(m, n - m), c(n - m, m), d(n - m, n - m);
89
90 for (Size i = 0; i < n; ++i) {
91 for (Size j = 0; j < n; ++j) {
92 if (i < m) {
93 if (j < m)
94 a[i][j] = A[i][j];
95 else
96 b[i][j - m] = A[i][j];
97 } else {
98 if (j < m)
99 c[i - m][j] = A[i][j];
100 else
101 d[i - m][j - m] = A[i][j];
102 }
103 }
104 }
105
106 Matrix aInv = blockMatrixInverse(a, leftIndices);
107 Matrix tmp = c * aInv;
108 Matrix schurCompInv;
109 if (isNull(c) || isNull(b))
110 schurCompInv = blockMatrixInverse(d, rightIndices);
111 else
112 schurCompInv = blockMatrixInverse(d - tmp * b, rightIndices);
113 Matrix b2 = (-1) * (aInv * b * schurCompInv);
114 Matrix a2 = aInv - b2 * tmp;
115 Matrix c2 = (-1) * schurCompInv * tmp;
116
117 Matrix res(n, n);
118 for (Size i = 0; i < n; ++i) {
119 for (Size j = 0; j < n; ++j) {
120 if (i < m) {
121 if (j < m)
122 res[i][j] = a2[i][j];
123 else
124 res[i][j] = b2[i][j - m];
125 } else {
126 if (j < m)
127 res[i][j] = c2[i - m][j];
128 else
129 res[i][j] = schurCompInv[i - m][j - m];
130 }
131 }
132 }
133
134 return res;
135} // blockMatrixInverse(Matrix)
136
137QuantLib::SparseMatrix blockMatrixInverse(const QuantLib::SparseMatrix& A, const std::vector<Size>& blockIndices) {
138
139 QL_REQUIRE(blockIndices.size() > 0, "blockMatrixInverse: at least one entry in blockIndices required");
140 Size n = blockIndices.back();
141 QL_REQUIRE(n > 0 && A.size1() == A.size2() && A.size1() == n,
142 "blockMatrixInverse: matrix (" << A.size1() << "x" << A.size2() << ") must be square of size " << n
143 << "x" << n << ", n>0");
144
145 if (blockIndices.size() == 1) {
146 QuantLib::SparseMatrix res = inverse(A);
147 return res;
148 }
149
150 Size mid = (blockIndices.size() - 1) / 2;
151 Size m = blockIndices[mid];
152 QL_REQUIRE(m > 0 && m < n,
153 "blockMatrixInverse: expected m (" << m << ") to be positive and less than n (" << n << ")");
154 std::vector<Size> leftIndices(blockIndices.begin(), blockIndices.begin() + (mid + 1));
155 std::vector<Size> rightIndices(blockIndices.begin() + (mid + 1), blockIndices.end());
156 for (auto& i : rightIndices) {
157 i -= m;
158 }
159 QL_REQUIRE(leftIndices.size() > 0, "blockMatrixInverse: expected left indices to be non-empty");
160 QL_REQUIRE(rightIndices.size() > 0, "blockMatrixInverse: expected right indices to be non-empty");
161
162 QuantLib::SparseMatrix a = project(A, range(0, m), range(0, m));
163 QuantLib::SparseMatrix b = project(A, range(0, m), range(m, n));
164 QuantLib::SparseMatrix c = project(A, range(m, n), range(0, m));
165 QuantLib::SparseMatrix d = project(A, range(m, n), range(m, n));
166
167 QuantLib::SparseMatrix aInv = blockMatrixInverse(a, leftIndices);
168 QuantLib::SparseMatrix tmp(n - m, m), schurCompInv, p(m, n - m), p1(n - m, n - m), p2(m, m), b2(m, n - m), c2(n - m, m);
169 axpy_prod(c, aInv, tmp, true);
170 if (isNull(c) || isNull(b))
171 schurCompInv = blockMatrixInverse(d, rightIndices);
172 else {
173 axpy_prod(tmp, b, p1, true);
174 schurCompInv = blockMatrixInverse(d - p1, rightIndices);
175 }
176 axpy_prod(aInv, b, p, true);
177 axpy_prod(-p, schurCompInv, b2);
178 axpy_prod(b2, tmp, p2);
179 QuantLib::SparseMatrix a2 = aInv - p2;
180 axpy_prod(-schurCompInv, tmp, c2);
181
182 QuantLib::SparseMatrix res(n, n);
183
184 for (auto i1 = a2.begin1(); i1 != a2.end1(); ++i1) {
185 for (auto i2 = i1.begin(); i2 != i1.end(); ++i2) {
186 res(i2.index1(), i2.index2()) = *i2;
187 }
188 }
189 for (auto i1 = b2.begin1(); i1 != b2.end1(); ++i1) {
190 for (auto i2 = i1.begin(); i2 != i1.end(); ++i2) {
191 res(i2.index1(), i2.index2() + m) = *i2;
192 }
193 }
194 for (auto i1 = c2.begin1(); i1 != c2.end1(); ++i1) {
195 for (auto i2 = i1.begin(); i2 != i1.end(); ++i2) {
196 res(i2.index1() + m, i2.index2()) = *i2;
197 }
198 }
199 for (auto i1 = schurCompInv.begin1(); i1 != schurCompInv.end1(); ++i1) {
200 for (auto i2 = i1.begin(); i2 != i1.end(); ++i2) {
201 res(i2.index1() + m, i2.index2() + m) = *i2;
202 }
203 }
204
205 return res;
206} // blockMatrixInverse(SparseMatrix)
207
208Real modifiedMaxNorm(const QuantLib::SparseMatrix& A) {
209 Real r = 0.0;
210 for (auto i1 = A.begin1(); i1 != A.end1(); ++i1) {
211 for (auto i2 = i1.begin(); i2 != i1.end(); ++i2) {
212 r = std::max(r, std::abs(*i2));
213 }
214 }
215 return std::sqrt(static_cast<Real>(A.size1()) * static_cast<Real>(A.size2())) * r;
216}
217
218} // namespace QuantExt
inverse of a matrix using a block formula
QuantLib::SparseMatrix inverse(QuantLib::SparseMatrix m)
Real modifiedMaxNorm(const QuantLib::SparseMatrix &A)
Matrix blockMatrixInverse(const Matrix &A, const std::vector< Size > &blockIndices)