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) 2017 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
19// clang-format off
20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
22// clang-format on
23
25
26#include "toplevelfixture.hpp"
27
28#include <ql/math/comparison.hpp>
29#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
30
31#include <boost/make_shared.hpp>
32#include <boost/timer/timer.hpp>
33
34using namespace QuantLib;
35using namespace QuantExt;
36
37using namespace boost::unit_test_framework;
38using std::vector;
39
40BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
41
42BOOST_AUTO_TEST_SUITE(BlockMatrixInverseTest)
43
44namespace {
45void check(const Matrix res, const Matrix ex) {
46 QL_REQUIRE(res.rows() == ex.rows(), "different number of rows (" << res.rows() << ", expected " << ex.rows());
47 QL_REQUIRE(res.columns() == ex.columns(),
48 "different number of columns (" << res.columns() << ", expected " << ex.columns());
49 // BOOST_TEST_MESSAGE("check matrices: \n" << res << "\n" << ex);
50 for (Size i = 0; i < res.rows(); ++i) {
51 for (Size j = 0; j < res.columns(); ++j) {
52 if (std::abs(ex[i][j]) < 1E-5)
53 BOOST_CHECK_SMALL(std::abs(res[i][j] - ex[i][j]), 1E-10);
54 else
55 BOOST_CHECK_CLOSE(res[i][j], ex[i][j], 1E-10);
56 }
57 }
58} // check
59} // namespace
60
61BOOST_AUTO_TEST_CASE(testSingleBlock) {
62 BOOST_TEST_MESSAGE("Test block matrix inversion with single block matrix");
63
64 // clang-format off
65 std::vector<Real> data = { 1.0, 2.0, 2.0,
66 1.0, 1.0, 5.0,
67 -2.0, 0.5, 4.0 };
68 // clang-format on
69
70 Matrix m(3, 3);
71 std::copy(data.begin(), data.end(), m.begin());
72
73 std::vector<Size> indices = {3};
74
75 Matrix res = blockMatrixInverse(m, indices);
76 Matrix ex = inverse(m);
77 check(res, ex);
78} // testSingleBlock
79
80BOOST_AUTO_TEST_CASE(testTwoBlocks) {
81 BOOST_TEST_MESSAGE("Test block matrix inversion with two blocks");
82
83 // clang-format off
84 std::vector<Real> data = { 1.0, 2.0, 2.0, 3.0,
85 1.0, 1.0, 5.0, 1.0,
86 -2.0, 0.5, 4.0, -2.0,
87 3.0, 1.0, -1.0, -1.0};
88 // clang-format on
89
90 Matrix m(4, 4);
91 std::copy(data.begin(), data.end(), m.begin());
92
93 std::vector<Size> indices = {2, 4};
94
95 Matrix res = blockMatrixInverse(m, indices);
96 Matrix ex = inverse(m);
97 check(res, ex);
98} // testTwoBlocks
99
100BOOST_AUTO_TEST_CASE(testThreeBlocks) {
101 BOOST_TEST_MESSAGE("Test block matrix inversion with three blocks");
102
103 // clang-format off
104 std::vector<Real> data = { 1.0, 2.0, 2.0, 3.0,
105 1.0, 1.0, 5.0, 1.0,
106 -2.0, 0.5, 4.0, -2.0,
107 3.0, 1.0, -1.0, -1.0};
108 // clang-format on
109
110 Matrix m(4, 4);
111 std::copy(data.begin(), data.end(), m.begin());
112
113 std::vector<Size> indices = {1, 2, 4};
114
115 Matrix res = blockMatrixInverse(m, indices);
116 Matrix ex = inverse(m);
117 check(res, ex);
118} // testThreeBlocks
119
120BOOST_AUTO_TEST_CASE(testFourBlocksBigMatrix) {
121 BOOST_TEST_MESSAGE("Test block matrix inversion with four blocks big matrix");
122
123 Matrix m(300, 300, 0.0);
124
125 std::vector<Size> indices = {50, 100, 280, 300};
126
127 MersenneTwisterUniformRng mt(42);
128 for (Size i = 0; i < indices.size(); ++i) {
129 Size a0 = (i == 0 ? 0 : indices[i - 1]);
130 Size a1 = indices[i];
131 for (Size ii = a0; ii < a1; ++ii) {
132 for (Size jj = a0; jj < a1; ++jj) {
133 m[ii][jj] = mt.nextReal();
134 }
135 }
136 }
137
138 boost::timer::cpu_timer timer;
139 Matrix res = blockMatrixInverse(m, indices);
140 timer.stop();
141 BOOST_TEST_MESSAGE("block matrix inversion: " << timer.elapsed().wall * 1e-6 << " ms");
142 timer.start();
143 Matrix ex = inverse(m);
144 timer.stop();
145 BOOST_TEST_MESSAGE("plain matrix inversion: " << timer.elapsed().wall * 1e-6 << " ms");
146 check(res, ex);
147} // testFourBlocksBigMatrix
148
149BOOST_AUTO_TEST_CASE(testTenBlocksBigMatrix) {
150 BOOST_TEST_MESSAGE("Test block matrix inversion with ten blocks big matrix");
151
152 Matrix m(500, 500, 0.0);
153
154 std::vector<Size> indices = {30, 80, 130, 150, 200, 280, 370, 420, 430, 500};
155
156 MersenneTwisterUniformRng mt(42);
157 for (Size i = 0; i < indices.size(); ++i) {
158 Size a0 = (i == 0 ? 0 : indices[i - 1]);
159 Size a1 = indices[i];
160 for (Size ii = a0; ii < a1; ++ii) {
161 for (Size jj = a0; jj < a1; ++jj) {
162 m[ii][jj] = mt.nextReal();
163 }
164 }
165 }
166
167 boost::timer::cpu_timer timer;
168 Matrix res = blockMatrixInverse(m, indices);
169 timer.stop();
170 BOOST_TEST_MESSAGE("block matrix inversion: " << timer.elapsed().wall * 1e-6 << " ms");
171 timer.start();
172 Matrix ex = inverse(m);
173 timer.stop();
174 BOOST_TEST_MESSAGE("plain matrix inversion: " << timer.elapsed().wall * 1e-6 << " ms");
175 check(res, ex);
176} // testFourBlocksBigMatrix
177
178BOOST_AUTO_TEST_CASE(testSparseMatrix) {
179 BOOST_TEST_MESSAGE("Test sparse matrix with two blocks");
180
181 // clang-format off
182 std::vector<Real> data = { 1.0, 2.0, 0.0, 3.0,
183 0.0, 1.0, 5.0, 0.0,
184 -2.0, 0.0, 4.0, -2.0,
185 3.0, 1.0, -1.0, -1.0};
186 // clang-format on
187
188 Matrix m(4, 4);
189 std::copy(data.begin(), data.end(), m.begin());
190
191 SparseMatrix sm(4, 4);
192 for (Size i = 0; i < 4; ++i) {
193 for (Size j = 0; j < 4; ++j) {
194 Real tmp = data[i * 4 + j];
195 if (!close_enough(tmp, 0.0))
196 sm(i, j) = tmp;
197 }
198 }
199
200 std::vector<Size> indices = {2, 4};
201
202 SparseMatrix res = blockMatrixInverse(sm, indices);
203 Matrix ex = inverse(m);
204
205 Matrix res2(4, 4);
206 for (Size i = 0; i < 4; ++i) {
207 for (Size j = 0; j < 4; ++j) {
208 res2[i][j] = res(i, j);
209 }
210 }
211
212 check(res2, ex);
213} // testSingleBlock
214
215
216BOOST_AUTO_TEST_SUITE_END()
217
218BOOST_AUTO_TEST_SUITE_END()
inverse of a matrix using a block formula
QuantLib::SparseMatrix inverse(QuantLib::SparseMatrix m)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Matrix blockMatrixInverse(const Matrix &A, const std::vector< Size > &blockIndices)
BOOST_AUTO_TEST_CASE(testSingleBlock)
Fixture that can be used at top level.