21#include <boost/numeric/ublas/lu.hpp>
23using namespace boost::numeric::ublas;
29bool isNull(
const Matrix& A) {
30 for (
auto const& a : A) {
31 if (std::abs(a) > QL_EPSILON)
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)
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());
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());
57 boost::numeric::ublas::lu_substitute(m, pert,
inverse);
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");
69 if (blockIndices.size() == 1) {
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) {
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");
88 Matrix a(m, m), b(m, n - m), c(n - m, m), d(n - m, n - m);
90 for (Size i = 0; i < n; ++i) {
91 for (Size j = 0; j < n; ++j) {
96 b[i][j - m] = A[i][j];
99 c[i - m][j] = A[i][j];
101 d[i - m][j - m] = A[i][j];
107 Matrix tmp = c * aInv;
109 if (isNull(c) || isNull(b))
113 Matrix b2 = (-1) * (aInv * b * schurCompInv);
114 Matrix a2 = aInv - b2 * tmp;
115 Matrix c2 = (-1) * schurCompInv * tmp;
118 for (Size i = 0; i < n; ++i) {
119 for (Size j = 0; j < n; ++j) {
122 res[i][j] = a2[i][j];
124 res[i][j] = b2[i][j - m];
127 res[i][j] = c2[i - m][j];
129 res[i][j] = schurCompInv[i - m][j - m];
137QuantLib::SparseMatrix
blockMatrixInverse(
const QuantLib::SparseMatrix& A,
const std::vector<Size>& blockIndices) {
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");
145 if (blockIndices.size() == 1) {
146 QuantLib::SparseMatrix res =
inverse(A);
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) {
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");
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));
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))
173 axpy_prod(tmp, b, p1,
true);
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);
182 QuantLib::SparseMatrix res(n, n);
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;
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;
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;
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;
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));
215 return std::sqrt(
static_cast<Real
>(A.size1()) *
static_cast<Real
>(A.size2())) * r;
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)