/src/quantlib/ql/math/distributions/normaldistribution.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl |
5 | | Copyright (C) 2002, 2003 Ferdinando Ametrano |
6 | | Copyright (C) 2008 StatPro Italia srl |
7 | | Copyright (C) 2010 Kakhkhor Abdijalilov |
8 | | |
9 | | This file is part of QuantLib, a free-software/open-source library |
10 | | for financial quantitative analysts and developers - http://quantlib.org/ |
11 | | |
12 | | QuantLib is free software: you can redistribute it and/or modify it |
13 | | under the terms of the QuantLib license. You should have received a |
14 | | copy of the license along with this program; if not, please email |
15 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
16 | | <https://www.quantlib.org/license.shtml>. |
17 | | |
18 | | This program is distributed in the hope that it will be useful, but WITHOUT |
19 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
20 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
21 | | */ |
22 | | |
23 | | #include <ql/math/distributions/normaldistribution.hpp> |
24 | | #include <ql/math/comparison.hpp> |
25 | | |
26 | | #include <boost/math/distributions/normal.hpp> |
27 | | |
28 | | namespace QuantLib { |
29 | | |
30 | 0 | Real CumulativeNormalDistribution::operator()(Real z) const { |
31 | | //QL_REQUIRE(!(z >= average_ && 2.0*average_-z > average_), |
32 | | // "not a real number. "); |
33 | 0 | z = (z - average_) / sigma_; |
34 | |
|
35 | 0 | Real result = 0.5 * ( 1.0 + errorFunction_( z*M_SQRT_2 ) ); |
36 | 0 | if (result<=1e-8) { //todo: investigate the threshold level |
37 | | // Asymptotic expansion for very negative z following (26.2.12) |
38 | | // on page 408 in M. Abramowitz and A. Stegun, |
39 | | // Pocketbook of Mathematical Functions, ISBN 3-87144818-4. |
40 | 0 | Real sum=1.0, zsqr=z*z, i=1.0, g=1.0, x, y, |
41 | 0 | a=QL_MAX_REAL, lasta; |
42 | 0 | do { |
43 | 0 | lasta=a; |
44 | 0 | x = (4.0*i-3.0)/zsqr; |
45 | 0 | y = x*((4.0*i-1)/zsqr); |
46 | 0 | a = g*(x-y); |
47 | 0 | sum -= a; |
48 | 0 | g *= y; |
49 | 0 | ++i; |
50 | 0 | a = std::fabs(a); |
51 | 0 | } while (lasta>a && a>=std::fabs(sum*QL_EPSILON)); |
52 | 0 | result = -gaussian_(z)/z*sum; |
53 | 0 | } |
54 | 0 | return result; |
55 | 0 | } |
56 | | |
57 | | #if !defined(QL_PATCH_SOLARIS) |
58 | | const CumulativeNormalDistribution InverseCumulativeNormal::f_; |
59 | | #endif |
60 | | |
61 | | // Coefficients for the rational approximation. |
62 | | const Real InverseCumulativeNormal::a1_ = -3.969683028665376e+01; |
63 | | const Real InverseCumulativeNormal::a2_ = 2.209460984245205e+02; |
64 | | const Real InverseCumulativeNormal::a3_ = -2.759285104469687e+02; |
65 | | const Real InverseCumulativeNormal::a4_ = 1.383577518672690e+02; |
66 | | const Real InverseCumulativeNormal::a5_ = -3.066479806614716e+01; |
67 | | const Real InverseCumulativeNormal::a6_ = 2.506628277459239e+00; |
68 | | |
69 | | const Real InverseCumulativeNormal::b1_ = -5.447609879822406e+01; |
70 | | const Real InverseCumulativeNormal::b2_ = 1.615858368580409e+02; |
71 | | const Real InverseCumulativeNormal::b3_ = -1.556989798598866e+02; |
72 | | const Real InverseCumulativeNormal::b4_ = 6.680131188771972e+01; |
73 | | const Real InverseCumulativeNormal::b5_ = -1.328068155288572e+01; |
74 | | |
75 | | const Real InverseCumulativeNormal::c1_ = -7.784894002430293e-03; |
76 | | const Real InverseCumulativeNormal::c2_ = -3.223964580411365e-01; |
77 | | const Real InverseCumulativeNormal::c3_ = -2.400758277161838e+00; |
78 | | const Real InverseCumulativeNormal::c4_ = -2.549732539343734e+00; |
79 | | const Real InverseCumulativeNormal::c5_ = 4.374664141464968e+00; |
80 | | const Real InverseCumulativeNormal::c6_ = 2.938163982698783e+00; |
81 | | |
82 | | const Real InverseCumulativeNormal::d1_ = 7.784695709041462e-03; |
83 | | const Real InverseCumulativeNormal::d2_ = 3.224671290700398e-01; |
84 | | const Real InverseCumulativeNormal::d3_ = 2.445134137142996e+00; |
85 | | const Real InverseCumulativeNormal::d4_ = 3.754408661907416e+00; |
86 | | |
87 | | // Limits of the approximation regions |
88 | | const Real InverseCumulativeNormal::x_low_ = 0.02425; |
89 | | const Real InverseCumulativeNormal::x_high_= 1.0 - x_low_; |
90 | | |
91 | 0 | Real InverseCumulativeNormal::tail_value(Real x) { |
92 | 0 | if (x <= 0.0 || x >= 1.0) { |
93 | | // try to recover if due to numerical error |
94 | 0 | if (close_enough(x, 1.0)) { |
95 | 0 | return QL_MAX_REAL; // largest value available |
96 | 0 | } else if (std::fabs(x) < QL_EPSILON) { |
97 | 0 | return QL_MIN_REAL; // largest negative value available |
98 | 0 | } else { |
99 | 0 | QL_FAIL("InverseCumulativeNormal(" << x |
100 | 0 | << ") undefined: must be 0 < x < 1"); |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | 0 | Real z; |
105 | 0 | if (x < x_low_) { |
106 | | // Rational approximation for the lower region 0<x<u_low |
107 | 0 | z = std::sqrt(-2.0*std::log(x)); |
108 | 0 | z = (((((c1_*z+c2_)*z+c3_)*z+c4_)*z+c5_)*z+c6_) / |
109 | 0 | ((((d1_*z+d2_)*z+d3_)*z+d4_)*z+1.0); |
110 | 0 | } else { |
111 | | // Rational approximation for the upper region u_high<x<1 |
112 | 0 | z = std::sqrt(-2.0*std::log(1.0-x)); |
113 | 0 | z = -(((((c1_*z+c2_)*z+c3_)*z+c4_)*z+c5_)*z+c6_) / |
114 | 0 | ((((d1_*z+d2_)*z+d3_)*z+d4_)*z+1.0); |
115 | 0 | } |
116 | |
|
117 | 0 | return z; |
118 | 0 | } |
119 | | |
120 | | const Real MoroInverseCumulativeNormal::a0_ = 2.50662823884; |
121 | | const Real MoroInverseCumulativeNormal::a1_ =-18.61500062529; |
122 | | const Real MoroInverseCumulativeNormal::a2_ = 41.39119773534; |
123 | | const Real MoroInverseCumulativeNormal::a3_ =-25.44106049637; |
124 | | |
125 | | const Real MoroInverseCumulativeNormal::b0_ = -8.47351093090; |
126 | | const Real MoroInverseCumulativeNormal::b1_ = 23.08336743743; |
127 | | const Real MoroInverseCumulativeNormal::b2_ =-21.06224101826; |
128 | | const Real MoroInverseCumulativeNormal::b3_ = 3.13082909833; |
129 | | |
130 | | const Real MoroInverseCumulativeNormal::c0_ = 0.3374754822726147; |
131 | | const Real MoroInverseCumulativeNormal::c1_ = 0.9761690190917186; |
132 | | const Real MoroInverseCumulativeNormal::c2_ = 0.1607979714918209; |
133 | | const Real MoroInverseCumulativeNormal::c3_ = 0.0276438810333863; |
134 | | const Real MoroInverseCumulativeNormal::c4_ = 0.0038405729373609; |
135 | | const Real MoroInverseCumulativeNormal::c5_ = 0.0003951896511919; |
136 | | const Real MoroInverseCumulativeNormal::c6_ = 0.0000321767881768; |
137 | | const Real MoroInverseCumulativeNormal::c7_ = 0.0000002888167364; |
138 | | const Real MoroInverseCumulativeNormal::c8_ = 0.0000003960315187; |
139 | | |
140 | 0 | Real MoroInverseCumulativeNormal::operator()(Real x) const { |
141 | 0 | QL_REQUIRE(x > 0.0 && x < 1.0, |
142 | 0 | "MoroInverseCumulativeNormal(" << x |
143 | 0 | << ") undefined: must be 0<x<1"); |
144 | | |
145 | 0 | Real result; |
146 | 0 | Real temp=x-0.5; |
147 | |
|
148 | 0 | if (std::fabs(temp) < 0.42) { |
149 | | // Beasley and Springer, 1977 |
150 | 0 | result=temp*temp; |
151 | 0 | result=temp* |
152 | 0 | (((a3_*result+a2_)*result+a1_)*result+a0_) / |
153 | 0 | ((((b3_*result+b2_)*result+b1_)*result+b0_)*result+1.0); |
154 | 0 | } else { |
155 | | // improved approximation for the tail (Moro 1995) |
156 | 0 | if (x<0.5) |
157 | 0 | result = x; |
158 | 0 | else |
159 | 0 | result=1.0-x; |
160 | 0 | result = std::log(-std::log(result)); |
161 | 0 | result = c0_+result*(c1_+result*(c2_+result*(c3_+result* |
162 | 0 | (c4_+result*(c5_+result*(c6_+result* |
163 | 0 | (c7_+result*c8_))))))); |
164 | 0 | if (x<0.5) |
165 | 0 | result=-result; |
166 | 0 | } |
167 | |
|
168 | 0 | return average_ + result*sigma_; |
169 | 0 | } |
170 | | |
171 | | MaddockInverseCumulativeNormal::MaddockInverseCumulativeNormal( |
172 | | Real average, Real sigma) |
173 | 0 | : average_(average), sigma_(sigma) {} |
174 | | |
175 | 0 | Real MaddockInverseCumulativeNormal::operator()(Real x) const { |
176 | 0 | return boost::math::quantile( |
177 | 0 | boost::math::normal_distribution<Real>(average_, sigma_), x); |
178 | 0 | } |
179 | | |
180 | | MaddockCumulativeNormal::MaddockCumulativeNormal( |
181 | | Real average, Real sigma) |
182 | 0 | : average_(average), sigma_(sigma) {} |
183 | | |
184 | 0 | Real MaddockCumulativeNormal::operator()(Real x) const { |
185 | 0 | return boost::math::cdf( |
186 | 0 | boost::math::normal_distribution<Real>(average_, sigma_), x); |
187 | 0 | } |
188 | | } |