Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
transitionmatrix.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/*! \file models/transitionmatrix.hpp
20 \brief utility functions for transition matrices and generators
21 \ingroup models
22*/
23
26
27#include <ql/math/comparison.hpp>
28#include <ql/math/distributions/normaldistribution.hpp>
29
30namespace QuantExt {
31
32void sanitiseTransitionMatrix(Matrix& m) {
33 for (Size i = 0; i < m.rows(); ++i) {
34 Real sum = 0.0;
35 for (Size j = 0; j < m.columns(); ++j) {
36 m[i][j] = std::max(std::min(m[i][j], 1.0), 0.0);
37 if (i != j)
38 sum += m[i][j];
39 }
40 if (sum <= 1.0) {
41 m(i, i) = 1.0 - sum;
42 } else {
43 sum += m[i][i];
44 for (Size j = 0; j < m.columns(); ++j) {
45 m[i][j] = m[i][j] / sum;
46 }
47 }
48 }
49}
50
51void checkTransitionMatrix(const Matrix& t) {
52 QL_REQUIRE(t.rows() == t.columns(), "transition matrix must be quadratic");
53 for (Size i = 0; i < t.rows(); ++i) {
54 Real sum = 0.0;
55 for (Size j = 0; j < t.columns(); ++j) {
56 sum += t[i][j];
57 QL_REQUIRE(t[i][j] > 0.0 || close_enough(t[i][j], 0.0),
58 "transition matrix entry (" << i << "," << j << ") is negative: " << t[i][j]);
59 }
60 QL_REQUIRE(close_enough(sum, 1.0), "row " << i << " sum (" << sum << ") not equal to 1");
61 }
62 return;
63}
64
65void checkGeneratorMatrix(const Matrix& g) {
66 QL_REQUIRE(g.rows() == g.columns(), "generator matrix must be quadratic");
67 for (Size i = 0; i < g.rows(); ++i) {
68 Real sum = 0.0;
69 for (Size j = 0; j < g.columns(); ++j) {
70 sum += g[i][j];
71 if (i != j) {
72 QL_REQUIRE(g[i][j] > 0.0 || close_enough(g[i][j], 0.0),
73 "generator matrix entry (" << i << "," << j << ") is negative: " << g[i][j]);
74 }
75 }
76 QL_REQUIRE(std::fabs(sum) < QL_EPSILON, "row " << i << " sum (" << sum << ") not equal to 0");
77 }
78 return;
79}
80
81namespace {
82struct PiCompare {
83 PiCompare(const std::vector<Real> a) : a_(a) {}
84 bool operator()(const Size i, const Size j) { return a_[i] < a_[j]; }
85 std::vector<Real> a_;
86};
87} // anonymous namespace
88
89Matrix generator(const Matrix& t, const Real horizon) {
90 // naked log
91 Matrix A = QuantExt::Logm(t) / horizon;
92 // regularisation
93 Size n = A.columns();
94 for (Size row = 0; row < A.rows(); ++row) {
95 // Step 1
96 Real lambda = 0.0;
97 for (Size i = 0; i < n; ++i) {
98 lambda += A(row, i);
99 }
100 lambda /= static_cast<Real>(n);
101 std::vector<Real> b(n);
102 for (Size i = 0; i < n; ++i) {
103 b[i] = A(row, i) - lambda;
104 }
105 // Step 2
106 std::vector<Size> pi(n);
107 for (Size i = 0; i < n; ++i)
108 pi[i] = i;
109 // ascending order, in the paper it says descending order...
110 std::sort(pi.begin(), pi.end(), PiCompare(b));
111 std::vector<Real> ahat(n);
112 for (Size i = 0; i < n; ++i)
113 ahat[i] = b[pi[i]];
114 // Step 3
115 // start with l=1, the paper says l=2....
116 Size l = 1;
117 for (; l <= n - 1; ++l) {
118 Real sum = 0.0;
119 for (Size i = 0; i <= n - (l + 1); ++i)
120 sum += ahat[n - i - 1];
121 if (static_cast<Real>(n - l + 1) * ahat[l + 1 - 1] >= ahat[0] + sum)
122 break;
123 }
124 QL_REQUIRE(l <= n - 1, "regularisedGenerator: expected 2 <= l <= n-1");
125 // Step 4
126 std::vector<Real> ghat(n, 0.0);
127 Real sum = 0.0;
128 for (Size j = 0; j < n; ++j) {
129 if (!(j + 1 >= 2 && j + 1 <= l))
130 sum += ahat[j];
131 }
132 sum /= static_cast<Real>(n - l + 1);
133 for (Size i = 0; i < n; ++i) {
134 if (!(i + 1 >= 2 && i + 1 <= l))
135 ghat[i] = ahat[i] - sum;
136 }
137 // Step 5
138 for (Size i = 0; i < n; ++i) {
139 A(row, pi[i]) = ghat[i];
140 }
141 } // for row
142 return A;
143}
144
145template <class I> std::vector<Real> creditStateBoundaries(const I& begin, const I& end) {
146 InverseCumulativeNormal icn;
147 std::vector<Real> bounds(end - begin);
148 Real sum = 0.0;
149 for (Size i = 0; i < bounds.size(); ++i) {
150 Real p = *(begin + i);
151 QL_REQUIRE(p >= 0.0, "transistion probability " << i << " is negative: " << p);
152 sum += p;
153 QL_REQUIRE(sum < 1.0 || close_enough(sum, 1.0), "sum of transition probabilities is greater than 1: " << sum);
154 bounds[i] = icn(sum);
155 }
156 QL_REQUIRE(close_enough(sum, 1.0), "sum of transition probabilities is not 1: " << sum);
157 return bounds;
158}
159
160} // namespace QuantExt
matrix functions
QuantLib::Matrix Logm(const QuantLib::Matrix &m)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
std::vector< Real > creditStateBoundaries(const I &begin, const I &end)
compute N(0,1) credit state boundaries
void checkTransitionMatrix(const Matrix &t)
check if the matrix is a transition matrix, i.e. row sums are 1 and entries are non-negative
void checkGeneratorMatrix(const Matrix &g)
check if the matrix is a generator matirx, i.e. row sums are 0 and non-diagonal elements are non-nega...
Real sum(const Cash &c, const Cash &d)
Definition: bondbasket.cpp:107
Matrix generator(const Matrix &t, const Real horizon)
void sanitiseTransitionMatrix(Matrix &m)
std::vector< Real > a_
utility functions for transition matrices and generators