/src/quantlib/ql/experimental/math/convolvedstudentt.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2014 Jose Aparicio |
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 | | #include <ql/experimental/math/convolvedstudentt.hpp> |
21 | | #include <ql/errors.hpp> |
22 | | #include <ql/math/factorial.hpp> |
23 | | #include <ql/math/distributions/normaldistribution.hpp> |
24 | | #include <ql/math/solvers1d/brent.hpp> |
25 | | #include <ql/math/functional.hpp> |
26 | | #include <boost/math/distributions/students_t.hpp> |
27 | | |
28 | | namespace QuantLib { |
29 | | |
30 | | CumulativeBehrensFisher::CumulativeBehrensFisher(const std::vector<Integer>& degreesFreedom, |
31 | | const std::vector<Real>& factors) |
32 | 0 | : degreesFreedom_(degreesFreedom), factors_(factors), polyConvolved_(std::vector<Real>(1, 1.)) |
33 | | |
34 | 0 | { |
35 | 0 | QL_REQUIRE(degreesFreedom.size() == factors.size(), |
36 | 0 | "Incompatible sizes in convolution."); |
37 | 0 | for (int i : degreesFreedom) { |
38 | 0 | QL_REQUIRE(i % 2 != 0, "Even degree of freedom not allowed"); |
39 | 0 | QL_REQUIRE(i >= 0, "Negative degree of freedom not allowed"); |
40 | 0 | } |
41 | 0 | for(Size i=0; i<degreesFreedom_.size(); i++) |
42 | 0 | polynCharFnc_.push_back(polynCharactT((degreesFreedom[i]-1)/2)); |
43 | | // adjust the polynomial coefficients by the factors in the linear |
44 | | // combination: |
45 | 0 | for(Size i=0; i<degreesFreedom_.size(); i++) { |
46 | 0 | Real multiplier = 1.; |
47 | 0 | for(Size k=1; k<polynCharFnc_[i].size(); k++) { |
48 | 0 | multiplier *= std::abs(factors_[i]); |
49 | 0 | polynCharFnc_[i][k] *= multiplier; |
50 | 0 | } |
51 | 0 | } |
52 | | //convolution, here it is a product of polynomials and exponentials |
53 | 0 | for (auto& i : polynCharFnc_) |
54 | 0 | polyConvolved_ = convolveVectorPolynomials(polyConvolved_, i); |
55 | | // trim possible zeros that might have arised: |
56 | 0 | auto it = polyConvolved_.rbegin(); |
57 | 0 | while (it != polyConvolved_.rend()) { |
58 | 0 | if (*it == 0.) { |
59 | 0 | polyConvolved_.pop_back(); |
60 | 0 | it = polyConvolved_.rbegin(); |
61 | 0 | }else{ |
62 | 0 | break; |
63 | 0 | } |
64 | 0 | } |
65 | | // cache 'a' value (the exponent) |
66 | 0 | for(Size i=0; i<degreesFreedom_.size(); i++) |
67 | 0 | a_ += std::sqrt(static_cast<Real>(degreesFreedom_[i])) |
68 | 0 | * std::abs(factors_[i]); |
69 | 0 | a2_ = a_ * a_; |
70 | 0 | } |
71 | | |
72 | 0 | std::vector<Real> CumulativeBehrensFisher::polynCharactT(Natural n) const { |
73 | 0 | Natural nu = 2 * n +1; |
74 | 0 | std::vector<Real> low(1,1.), high(1,1.); |
75 | 0 | high.push_back(std::sqrt(static_cast<Real>(nu))); |
76 | 0 | if(n==0) return low; |
77 | 0 | if(n==1) return high; |
78 | | |
79 | 0 | for(Size k=1; k<n; k++) { |
80 | 0 | std::vector<Real> recursionFactor(1,0.); // 0 coef |
81 | 0 | recursionFactor.push_back(0.); // 1 coef |
82 | 0 | recursionFactor.push_back(nu/((2.*k+1.)*(2.*k-1.))); // 2 coef |
83 | 0 | std::vector<Real> lowUp = |
84 | 0 | convolveVectorPolynomials(recursionFactor, low); |
85 | | //add them up: |
86 | 0 | for(Size i=0; i<high.size(); i++) |
87 | 0 | lowUp[i] += high[i]; |
88 | 0 | low = high; |
89 | 0 | high = lowUp; |
90 | 0 | } |
91 | 0 | return high; |
92 | 0 | } |
93 | | |
94 | | std::vector<Real> CumulativeBehrensFisher::convolveVectorPolynomials( |
95 | | const std::vector<Real>& v1, |
96 | 0 | const std::vector<Real>& v2) const { |
97 | | #if defined(QL_EXTRA_SAFETY_CHECKS) |
98 | | QL_REQUIRE(!v1.empty() && !v2.empty(), |
99 | | "Incorrect vectors in polynomial."); |
100 | | #endif |
101 | |
|
102 | 0 | const std::vector<Real>& shorter = v1.size() < v2.size() ? v1 : v2; |
103 | 0 | const std::vector<Real>& longer = (v1 == shorter) ? v2 : v1; |
104 | |
|
105 | 0 | Size newDegree = v1.size()+v2.size()-2; |
106 | 0 | std::vector<Real> resultB(newDegree+1, 0.); |
107 | 0 | for(Size polyOrdr=0; polyOrdr<resultB.size(); polyOrdr++) { |
108 | 0 | for(Size i=std::max<Integer>(0, polyOrdr-longer.size()+1); |
109 | 0 | i<=std::min(polyOrdr, shorter.size()-1); i++) |
110 | 0 | resultB[polyOrdr] += shorter[i]*longer[polyOrdr-i]; |
111 | 0 | } |
112 | 0 | return resultB; |
113 | 0 | } |
114 | | |
115 | 0 | Probability CumulativeBehrensFisher::operator()(const Real x) const { |
116 | | // 1st & 0th terms with the table integration |
117 | 0 | Real integral = polyConvolved_[0] * std::atan(x/a_); |
118 | 0 | Real squared = a2_ + x*x; |
119 | 0 | Real rootsqr = std::sqrt(squared); |
120 | 0 | Real atan2xa = std::atan2(-x,a_); |
121 | 0 | if(polyConvolved_.size()>1) |
122 | 0 | integral += polyConvolved_[1] * x/squared; |
123 | |
|
124 | 0 | for(Size exponent = 2; exponent <polyConvolved_.size(); exponent++) { |
125 | 0 | integral -= polyConvolved_[exponent] * |
126 | 0 | Factorial::get(exponent-1) * std::sin((exponent)*atan2xa) |
127 | 0 | /std::pow(rootsqr, static_cast<Real>(exponent)); |
128 | 0 | } |
129 | 0 | return .5 + integral / M_PI; |
130 | 0 | } |
131 | | |
132 | | Probability |
133 | 0 | CumulativeBehrensFisher::density(const Real x) const { |
134 | 0 | Real squared = a2_ + x*x; |
135 | 0 | Real integral = polyConvolved_[0] * a_ / squared; |
136 | 0 | Real rootsqr = std::sqrt(squared); |
137 | 0 | Real atan2xa = std::atan2(-x,a_); |
138 | 0 | for(Size exponent=1; exponent <polyConvolved_.size(); exponent++) { |
139 | 0 | integral += polyConvolved_[exponent] * |
140 | 0 | Factorial::get(exponent) * std::cos((exponent+1)*atan2xa) |
141 | 0 | /std::pow(rootsqr, static_cast<Real>(exponent+1) ); |
142 | 0 | } |
143 | 0 | return integral / M_PI; |
144 | 0 | } |
145 | | |
146 | | |
147 | | |
148 | | InverseCumulativeBehrensFisher::InverseCumulativeBehrensFisher( |
149 | | const std::vector<Integer>& degreesFreedom, |
150 | | const std::vector<Real>& factors, |
151 | | Real accuracy) |
152 | 0 | : normSqr_(std::inner_product(factors.begin(), factors.end(), |
153 | 0 | factors.begin(), Real(0.))), |
154 | 0 | accuracy_(accuracy), distrib_(degreesFreedom, factors) { } |
155 | | |
156 | 0 | Real InverseCumulativeBehrensFisher::operator()(const Probability q) const { |
157 | 0 | Probability effectiveq; |
158 | 0 | Real sign; |
159 | | // since the distrib is symmetric solve only on the right side: |
160 | 0 | if(q==0.5) { |
161 | 0 | return 0.; |
162 | 0 | }else if(q < 0.5) { |
163 | 0 | sign = -1.; |
164 | 0 | effectiveq = 1.-q; |
165 | 0 | }else{ |
166 | 0 | sign = 1.; |
167 | 0 | effectiveq = q; |
168 | 0 | } |
169 | 0 | Real xMin = |
170 | 0 | InverseCumulativeNormal::standard_value(effectiveq) * normSqr_; |
171 | | // inversion will fail at the Brent's bounds-check if this is not enough |
172 | | // (q is very close to 1.), in a bad combination fails around 1.-1.e-7 |
173 | 0 | Real xMax = 1.e6; |
174 | 0 | return sign * |
175 | 0 | Brent().solve([&](Real x) -> Real { return distrib_(x) - effectiveq; }, |
176 | 0 | accuracy_, (xMin+xMax)/2., xMin, xMax); |
177 | 0 | } |
178 | | |
179 | | } |