cannam@160
|
1 // Copyright Benjamin Sobotta 2012
|
cannam@160
|
2
|
cannam@160
|
3 // Use, modification and distribution are subject to the
|
cannam@160
|
4 // Boost Software License, Version 1.0. (See accompanying file
|
cannam@160
|
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
cannam@160
|
6
|
cannam@160
|
7 #ifndef BOOST_STATS_SKEW_NORMAL_HPP
|
cannam@160
|
8 #define BOOST_STATS_SKEW_NORMAL_HPP
|
cannam@160
|
9
|
cannam@160
|
10 // http://en.wikipedia.org/wiki/Skew_normal_distribution
|
cannam@160
|
11 // http://azzalini.stat.unipd.it/SN/
|
cannam@160
|
12 // Also:
|
cannam@160
|
13 // Azzalini, A. (1985). "A class of distributions which includes the normal ones".
|
cannam@160
|
14 // Scand. J. Statist. 12: 171-178.
|
cannam@160
|
15
|
cannam@160
|
16 #include <boost/math/distributions/fwd.hpp> // TODO add skew_normal distribution to fwd.hpp!
|
cannam@160
|
17 #include <boost/math/special_functions/owens_t.hpp> // Owen's T function
|
cannam@160
|
18 #include <boost/math/distributions/complement.hpp>
|
cannam@160
|
19 #include <boost/math/distributions/normal.hpp>
|
cannam@160
|
20 #include <boost/math/distributions/detail/common_error_handling.hpp>
|
cannam@160
|
21 #include <boost/math/constants/constants.hpp>
|
cannam@160
|
22 #include <boost/math/tools/tuple.hpp>
|
cannam@160
|
23 #include <boost/math/tools/roots.hpp> // Newton-Raphson
|
cannam@160
|
24 #include <boost/assert.hpp>
|
cannam@160
|
25 #include <boost/math/distributions/detail/generic_mode.hpp> // pdf max finder.
|
cannam@160
|
26
|
cannam@160
|
27 #include <utility>
|
cannam@160
|
28 #include <algorithm> // std::lower_bound, std::distance
|
cannam@160
|
29
|
cannam@160
|
30 namespace boost{ namespace math{
|
cannam@160
|
31
|
cannam@160
|
32 namespace detail
|
cannam@160
|
33 {
|
cannam@160
|
34 template <class RealType, class Policy>
|
cannam@160
|
35 inline bool check_skew_normal_shape(
|
cannam@160
|
36 const char* function,
|
cannam@160
|
37 RealType shape,
|
cannam@160
|
38 RealType* result,
|
cannam@160
|
39 const Policy& pol)
|
cannam@160
|
40 {
|
cannam@160
|
41 if(!(boost::math::isfinite)(shape))
|
cannam@160
|
42 {
|
cannam@160
|
43 *result =
|
cannam@160
|
44 policies::raise_domain_error<RealType>(function,
|
cannam@160
|
45 "Shape parameter is %1%, but must be finite!",
|
cannam@160
|
46 shape, pol);
|
cannam@160
|
47 return false;
|
cannam@160
|
48 }
|
cannam@160
|
49 return true;
|
cannam@160
|
50 }
|
cannam@160
|
51
|
cannam@160
|
52 } // namespace detail
|
cannam@160
|
53
|
cannam@160
|
54 template <class RealType = double, class Policy = policies::policy<> >
|
cannam@160
|
55 class skew_normal_distribution
|
cannam@160
|
56 {
|
cannam@160
|
57 public:
|
cannam@160
|
58 typedef RealType value_type;
|
cannam@160
|
59 typedef Policy policy_type;
|
cannam@160
|
60
|
cannam@160
|
61 skew_normal_distribution(RealType l_location = 0, RealType l_scale = 1, RealType l_shape = 0)
|
cannam@160
|
62 : location_(l_location), scale_(l_scale), shape_(l_shape)
|
cannam@160
|
63 { // Default is a 'standard' normal distribution N01. (shape=0 results in the normal distribution with no skew)
|
cannam@160
|
64 static const char* function = "boost::math::skew_normal_distribution<%1%>::skew_normal_distribution";
|
cannam@160
|
65
|
cannam@160
|
66 RealType result;
|
cannam@160
|
67 detail::check_scale(function, l_scale, &result, Policy());
|
cannam@160
|
68 detail::check_location(function, l_location, &result, Policy());
|
cannam@160
|
69 detail::check_skew_normal_shape(function, l_shape, &result, Policy());
|
cannam@160
|
70 }
|
cannam@160
|
71
|
cannam@160
|
72 RealType location()const
|
cannam@160
|
73 {
|
cannam@160
|
74 return location_;
|
cannam@160
|
75 }
|
cannam@160
|
76
|
cannam@160
|
77 RealType scale()const
|
cannam@160
|
78 {
|
cannam@160
|
79 return scale_;
|
cannam@160
|
80 }
|
cannam@160
|
81
|
cannam@160
|
82 RealType shape()const
|
cannam@160
|
83 {
|
cannam@160
|
84 return shape_;
|
cannam@160
|
85 }
|
cannam@160
|
86
|
cannam@160
|
87
|
cannam@160
|
88 private:
|
cannam@160
|
89 //
|
cannam@160
|
90 // Data members:
|
cannam@160
|
91 //
|
cannam@160
|
92 RealType location_; // distribution location.
|
cannam@160
|
93 RealType scale_; // distribution scale.
|
cannam@160
|
94 RealType shape_; // distribution shape.
|
cannam@160
|
95 }; // class skew_normal_distribution
|
cannam@160
|
96
|
cannam@160
|
97 typedef skew_normal_distribution<double> skew_normal;
|
cannam@160
|
98
|
cannam@160
|
99 template <class RealType, class Policy>
|
cannam@160
|
100 inline const std::pair<RealType, RealType> range(const skew_normal_distribution<RealType, Policy>& /*dist*/)
|
cannam@160
|
101 { // Range of permissible values for random variable x.
|
cannam@160
|
102 using boost::math::tools::max_value;
|
cannam@160
|
103 return std::pair<RealType, RealType>(
|
cannam@160
|
104 std::numeric_limits<RealType>::has_infinity ? -std::numeric_limits<RealType>::infinity() : -max_value<RealType>(),
|
cannam@160
|
105 std::numeric_limits<RealType>::has_infinity ? std::numeric_limits<RealType>::infinity() : max_value<RealType>()); // - to + max value.
|
cannam@160
|
106 }
|
cannam@160
|
107
|
cannam@160
|
108 template <class RealType, class Policy>
|
cannam@160
|
109 inline const std::pair<RealType, RealType> support(const skew_normal_distribution<RealType, Policy>& /*dist*/)
|
cannam@160
|
110 { // Range of supported values for random variable x.
|
cannam@160
|
111 // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
|
cannam@160
|
112
|
cannam@160
|
113 using boost::math::tools::max_value;
|
cannam@160
|
114 return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>()); // - to + max value.
|
cannam@160
|
115 }
|
cannam@160
|
116
|
cannam@160
|
117 template <class RealType, class Policy>
|
cannam@160
|
118 inline RealType pdf(const skew_normal_distribution<RealType, Policy>& dist, const RealType& x)
|
cannam@160
|
119 {
|
cannam@160
|
120 const RealType scale = dist.scale();
|
cannam@160
|
121 const RealType location = dist.location();
|
cannam@160
|
122 const RealType shape = dist.shape();
|
cannam@160
|
123
|
cannam@160
|
124 static const char* function = "boost::math::pdf(const skew_normal_distribution<%1%>&, %1%)";
|
cannam@160
|
125
|
cannam@160
|
126 RealType result = 0;
|
cannam@160
|
127 if(false == detail::check_scale(function, scale, &result, Policy()))
|
cannam@160
|
128 {
|
cannam@160
|
129 return result;
|
cannam@160
|
130 }
|
cannam@160
|
131 if(false == detail::check_location(function, location, &result, Policy()))
|
cannam@160
|
132 {
|
cannam@160
|
133 return result;
|
cannam@160
|
134 }
|
cannam@160
|
135 if(false == detail::check_skew_normal_shape(function, shape, &result, Policy()))
|
cannam@160
|
136 {
|
cannam@160
|
137 return result;
|
cannam@160
|
138 }
|
cannam@160
|
139 if((boost::math::isinf)(x))
|
cannam@160
|
140 {
|
cannam@160
|
141 return 0; // pdf + and - infinity is zero.
|
cannam@160
|
142 }
|
cannam@160
|
143 // Below produces MSVC 4127 warnings, so the above used instead.
|
cannam@160
|
144 //if(std::numeric_limits<RealType>::has_infinity && abs(x) == std::numeric_limits<RealType>::infinity())
|
cannam@160
|
145 //{ // pdf + and - infinity is zero.
|
cannam@160
|
146 // return 0;
|
cannam@160
|
147 //}
|
cannam@160
|
148 if(false == detail::check_x(function, x, &result, Policy()))
|
cannam@160
|
149 {
|
cannam@160
|
150 return result;
|
cannam@160
|
151 }
|
cannam@160
|
152
|
cannam@160
|
153 const RealType transformed_x = (x-location)/scale;
|
cannam@160
|
154
|
cannam@160
|
155 normal_distribution<RealType, Policy> std_normal;
|
cannam@160
|
156
|
cannam@160
|
157 result = pdf(std_normal, transformed_x) * cdf(std_normal, shape*transformed_x) * 2 / scale;
|
cannam@160
|
158
|
cannam@160
|
159 return result;
|
cannam@160
|
160 } // pdf
|
cannam@160
|
161
|
cannam@160
|
162 template <class RealType, class Policy>
|
cannam@160
|
163 inline RealType cdf(const skew_normal_distribution<RealType, Policy>& dist, const RealType& x)
|
cannam@160
|
164 {
|
cannam@160
|
165 const RealType scale = dist.scale();
|
cannam@160
|
166 const RealType location = dist.location();
|
cannam@160
|
167 const RealType shape = dist.shape();
|
cannam@160
|
168
|
cannam@160
|
169 static const char* function = "boost::math::cdf(const skew_normal_distribution<%1%>&, %1%)";
|
cannam@160
|
170 RealType result = 0;
|
cannam@160
|
171 if(false == detail::check_scale(function, scale, &result, Policy()))
|
cannam@160
|
172 {
|
cannam@160
|
173 return result;
|
cannam@160
|
174 }
|
cannam@160
|
175 if(false == detail::check_location(function, location, &result, Policy()))
|
cannam@160
|
176 {
|
cannam@160
|
177 return result;
|
cannam@160
|
178 }
|
cannam@160
|
179 if(false == detail::check_skew_normal_shape(function, shape, &result, Policy()))
|
cannam@160
|
180 {
|
cannam@160
|
181 return result;
|
cannam@160
|
182 }
|
cannam@160
|
183 if((boost::math::isinf)(x))
|
cannam@160
|
184 {
|
cannam@160
|
185 if(x < 0) return 0; // -infinity
|
cannam@160
|
186 return 1; // + infinity
|
cannam@160
|
187 }
|
cannam@160
|
188 // These produce MSVC 4127 warnings, so the above used instead.
|
cannam@160
|
189 //if(std::numeric_limits<RealType>::has_infinity && x == std::numeric_limits<RealType>::infinity())
|
cannam@160
|
190 //{ // cdf +infinity is unity.
|
cannam@160
|
191 // return 1;
|
cannam@160
|
192 //}
|
cannam@160
|
193 //if(std::numeric_limits<RealType>::has_infinity && x == -std::numeric_limits<RealType>::infinity())
|
cannam@160
|
194 //{ // cdf -infinity is zero.
|
cannam@160
|
195 // return 0;
|
cannam@160
|
196 //}
|
cannam@160
|
197 if(false == detail::check_x(function, x, &result, Policy()))
|
cannam@160
|
198 {
|
cannam@160
|
199 return result;
|
cannam@160
|
200 }
|
cannam@160
|
201
|
cannam@160
|
202 const RealType transformed_x = (x-location)/scale;
|
cannam@160
|
203
|
cannam@160
|
204 normal_distribution<RealType, Policy> std_normal;
|
cannam@160
|
205
|
cannam@160
|
206 result = cdf(std_normal, transformed_x) - owens_t(transformed_x, shape)*static_cast<RealType>(2);
|
cannam@160
|
207
|
cannam@160
|
208 return result;
|
cannam@160
|
209 } // cdf
|
cannam@160
|
210
|
cannam@160
|
211 template <class RealType, class Policy>
|
cannam@160
|
212 inline RealType cdf(const complemented2_type<skew_normal_distribution<RealType, Policy>, RealType>& c)
|
cannam@160
|
213 {
|
cannam@160
|
214 const RealType scale = c.dist.scale();
|
cannam@160
|
215 const RealType location = c.dist.location();
|
cannam@160
|
216 const RealType shape = c.dist.shape();
|
cannam@160
|
217 const RealType x = c.param;
|
cannam@160
|
218
|
cannam@160
|
219 static const char* function = "boost::math::cdf(const complement(skew_normal_distribution<%1%>&), %1%)";
|
cannam@160
|
220
|
cannam@160
|
221 if((boost::math::isinf)(x))
|
cannam@160
|
222 {
|
cannam@160
|
223 if(x < 0) return 1; // cdf complement -infinity is unity.
|
cannam@160
|
224 return 0; // cdf complement +infinity is zero
|
cannam@160
|
225 }
|
cannam@160
|
226 // These produce MSVC 4127 warnings, so the above used instead.
|
cannam@160
|
227 //if(std::numeric_limits<RealType>::has_infinity && x == std::numeric_limits<RealType>::infinity())
|
cannam@160
|
228 //{ // cdf complement +infinity is zero.
|
cannam@160
|
229 // return 0;
|
cannam@160
|
230 //}
|
cannam@160
|
231 //if(std::numeric_limits<RealType>::has_infinity && x == -std::numeric_limits<RealType>::infinity())
|
cannam@160
|
232 //{ // cdf complement -infinity is unity.
|
cannam@160
|
233 // return 1;
|
cannam@160
|
234 //}
|
cannam@160
|
235 RealType result = 0;
|
cannam@160
|
236 if(false == detail::check_scale(function, scale, &result, Policy()))
|
cannam@160
|
237 return result;
|
cannam@160
|
238 if(false == detail::check_location(function, location, &result, Policy()))
|
cannam@160
|
239 return result;
|
cannam@160
|
240 if(false == detail::check_skew_normal_shape(function, shape, &result, Policy()))
|
cannam@160
|
241 return result;
|
cannam@160
|
242 if(false == detail::check_x(function, x, &result, Policy()))
|
cannam@160
|
243 return result;
|
cannam@160
|
244
|
cannam@160
|
245 const RealType transformed_x = (x-location)/scale;
|
cannam@160
|
246
|
cannam@160
|
247 normal_distribution<RealType, Policy> std_normal;
|
cannam@160
|
248
|
cannam@160
|
249 result = cdf(complement(std_normal, transformed_x)) + owens_t(transformed_x, shape)*static_cast<RealType>(2);
|
cannam@160
|
250 return result;
|
cannam@160
|
251 } // cdf complement
|
cannam@160
|
252
|
cannam@160
|
253 template <class RealType, class Policy>
|
cannam@160
|
254 inline RealType location(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
255 {
|
cannam@160
|
256 return dist.location();
|
cannam@160
|
257 }
|
cannam@160
|
258
|
cannam@160
|
259 template <class RealType, class Policy>
|
cannam@160
|
260 inline RealType scale(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
261 {
|
cannam@160
|
262 return dist.scale();
|
cannam@160
|
263 }
|
cannam@160
|
264
|
cannam@160
|
265 template <class RealType, class Policy>
|
cannam@160
|
266 inline RealType shape(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
267 {
|
cannam@160
|
268 return dist.shape();
|
cannam@160
|
269 }
|
cannam@160
|
270
|
cannam@160
|
271 template <class RealType, class Policy>
|
cannam@160
|
272 inline RealType mean(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
273 {
|
cannam@160
|
274 BOOST_MATH_STD_USING // for ADL of std functions
|
cannam@160
|
275
|
cannam@160
|
276 using namespace boost::math::constants;
|
cannam@160
|
277
|
cannam@160
|
278 //const RealType delta = dist.shape() / sqrt(static_cast<RealType>(1)+dist.shape()*dist.shape());
|
cannam@160
|
279
|
cannam@160
|
280 //return dist.location() + dist.scale() * delta * root_two_div_pi<RealType>();
|
cannam@160
|
281
|
cannam@160
|
282 return dist.location() + dist.scale() * dist.shape() / sqrt(pi<RealType>()+pi<RealType>()*dist.shape()*dist.shape()) * root_two<RealType>();
|
cannam@160
|
283 }
|
cannam@160
|
284
|
cannam@160
|
285 template <class RealType, class Policy>
|
cannam@160
|
286 inline RealType variance(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
287 {
|
cannam@160
|
288 using namespace boost::math::constants;
|
cannam@160
|
289
|
cannam@160
|
290 const RealType delta2 = static_cast<RealType>(1) / (static_cast<RealType>(1)+static_cast<RealType>(1)/(dist.shape()*dist.shape()));
|
cannam@160
|
291 //const RealType inv_delta2 = static_cast<RealType>(1)+static_cast<RealType>(1)/(dist.shape()*dist.shape());
|
cannam@160
|
292
|
cannam@160
|
293 RealType variance = dist.scale()*dist.scale()*(static_cast<RealType>(1)-two_div_pi<RealType>()*delta2);
|
cannam@160
|
294 //RealType variance = dist.scale()*dist.scale()*(static_cast<RealType>(1)-two_div_pi<RealType>()/inv_delta2);
|
cannam@160
|
295
|
cannam@160
|
296 return variance;
|
cannam@160
|
297 }
|
cannam@160
|
298
|
cannam@160
|
299 namespace detail
|
cannam@160
|
300 {
|
cannam@160
|
301 /*
|
cannam@160
|
302 TODO No closed expression for mode, so use max of pdf.
|
cannam@160
|
303 */
|
cannam@160
|
304
|
cannam@160
|
305 template <class RealType, class Policy>
|
cannam@160
|
306 inline RealType mode_fallback(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
307 { // mode.
|
cannam@160
|
308 static const char* function = "mode(skew_normal_distribution<%1%> const&)";
|
cannam@160
|
309 const RealType scale = dist.scale();
|
cannam@160
|
310 const RealType location = dist.location();
|
cannam@160
|
311 const RealType shape = dist.shape();
|
cannam@160
|
312
|
cannam@160
|
313 RealType result;
|
cannam@160
|
314 if(!detail::check_scale(
|
cannam@160
|
315 function,
|
cannam@160
|
316 scale, &result, Policy())
|
cannam@160
|
317 ||
|
cannam@160
|
318 !detail::check_skew_normal_shape(
|
cannam@160
|
319 function,
|
cannam@160
|
320 shape,
|
cannam@160
|
321 &result,
|
cannam@160
|
322 Policy()))
|
cannam@160
|
323 return result;
|
cannam@160
|
324
|
cannam@160
|
325 if( shape == 0 )
|
cannam@160
|
326 {
|
cannam@160
|
327 return location;
|
cannam@160
|
328 }
|
cannam@160
|
329
|
cannam@160
|
330 if( shape < 0 )
|
cannam@160
|
331 {
|
cannam@160
|
332 skew_normal_distribution<RealType, Policy> D(0, 1, -shape);
|
cannam@160
|
333 result = mode_fallback(D);
|
cannam@160
|
334 result = location-scale*result;
|
cannam@160
|
335 return result;
|
cannam@160
|
336 }
|
cannam@160
|
337
|
cannam@160
|
338 BOOST_MATH_STD_USING
|
cannam@160
|
339
|
cannam@160
|
340 // 21 elements
|
cannam@160
|
341 static const RealType shapes[] = {
|
cannam@160
|
342 0.0,
|
cannam@160
|
343 1.000000000000000e-004,
|
cannam@160
|
344 2.069138081114790e-004,
|
cannam@160
|
345 4.281332398719396e-004,
|
cannam@160
|
346 8.858667904100824e-004,
|
cannam@160
|
347 1.832980710832436e-003,
|
cannam@160
|
348 3.792690190732250e-003,
|
cannam@160
|
349 7.847599703514606e-003,
|
cannam@160
|
350 1.623776739188722e-002,
|
cannam@160
|
351 3.359818286283781e-002,
|
cannam@160
|
352 6.951927961775606e-002,
|
cannam@160
|
353 1.438449888287663e-001,
|
cannam@160
|
354 2.976351441631319e-001,
|
cannam@160
|
355 6.158482110660261e-001,
|
cannam@160
|
356 1.274274985703135e+000,
|
cannam@160
|
357 2.636650898730361e+000,
|
cannam@160
|
358 5.455594781168514e+000,
|
cannam@160
|
359 1.128837891684688e+001,
|
cannam@160
|
360 2.335721469090121e+001,
|
cannam@160
|
361 4.832930238571753e+001,
|
cannam@160
|
362 1.000000000000000e+002};
|
cannam@160
|
363
|
cannam@160
|
364 // 21 elements
|
cannam@160
|
365 static const RealType guess[] = {
|
cannam@160
|
366 0.0,
|
cannam@160
|
367 5.000050000525391e-005,
|
cannam@160
|
368 1.500015000148736e-004,
|
cannam@160
|
369 3.500035000350010e-004,
|
cannam@160
|
370 7.500075000752560e-004,
|
cannam@160
|
371 1.450014500145258e-003,
|
cannam@160
|
372 3.050030500305390e-003,
|
cannam@160
|
373 6.250062500624765e-003,
|
cannam@160
|
374 1.295012950129504e-002,
|
cannam@160
|
375 2.675026750267495e-002,
|
cannam@160
|
376 5.525055250552491e-002,
|
cannam@160
|
377 1.132511325113255e-001,
|
cannam@160
|
378 2.249522495224952e-001,
|
cannam@160
|
379 3.992539925399257e-001,
|
cannam@160
|
380 5.353553535535358e-001,
|
cannam@160
|
381 4.954549545495457e-001,
|
cannam@160
|
382 3.524535245352451e-001,
|
cannam@160
|
383 2.182521825218249e-001,
|
cannam@160
|
384 1.256512565125654e-001,
|
cannam@160
|
385 6.945069450694508e-002,
|
cannam@160
|
386 3.735037350373460e-002
|
cannam@160
|
387 };
|
cannam@160
|
388
|
cannam@160
|
389 const RealType* result_ptr = std::lower_bound(shapes, shapes+21, shape);
|
cannam@160
|
390
|
cannam@160
|
391 typedef typename std::iterator_traits<RealType*>::difference_type diff_type;
|
cannam@160
|
392
|
cannam@160
|
393 const diff_type d = std::distance(shapes, result_ptr);
|
cannam@160
|
394
|
cannam@160
|
395 BOOST_ASSERT(d > static_cast<diff_type>(0));
|
cannam@160
|
396
|
cannam@160
|
397 // refine
|
cannam@160
|
398 if(d < static_cast<diff_type>(21)) // shape smaller 100
|
cannam@160
|
399 {
|
cannam@160
|
400 result = guess[d-static_cast<diff_type>(1)]
|
cannam@160
|
401 + (guess[d]-guess[d-static_cast<diff_type>(1)])/(shapes[d]-shapes[d-static_cast<diff_type>(1)])
|
cannam@160
|
402 * (shape-shapes[d-static_cast<diff_type>(1)]);
|
cannam@160
|
403 }
|
cannam@160
|
404 else // shape greater 100
|
cannam@160
|
405 {
|
cannam@160
|
406 result = 1e-4;
|
cannam@160
|
407 }
|
cannam@160
|
408
|
cannam@160
|
409 skew_normal_distribution<RealType, Policy> helper(0, 1, shape);
|
cannam@160
|
410
|
cannam@160
|
411 result = detail::generic_find_mode_01(helper, result, function);
|
cannam@160
|
412
|
cannam@160
|
413 result = result*scale + location;
|
cannam@160
|
414
|
cannam@160
|
415 return result;
|
cannam@160
|
416 } // mode_fallback
|
cannam@160
|
417
|
cannam@160
|
418
|
cannam@160
|
419 /*
|
cannam@160
|
420 * TODO No closed expression for mode, so use f'(x) = 0
|
cannam@160
|
421 */
|
cannam@160
|
422 template <class RealType, class Policy>
|
cannam@160
|
423 struct skew_normal_mode_functor
|
cannam@160
|
424 {
|
cannam@160
|
425 skew_normal_mode_functor(const boost::math::skew_normal_distribution<RealType, Policy> dist)
|
cannam@160
|
426 : distribution(dist)
|
cannam@160
|
427 {
|
cannam@160
|
428 }
|
cannam@160
|
429
|
cannam@160
|
430 boost::math::tuple<RealType, RealType> operator()(RealType const& x)
|
cannam@160
|
431 {
|
cannam@160
|
432 normal_distribution<RealType, Policy> std_normal;
|
cannam@160
|
433 const RealType shape = distribution.shape();
|
cannam@160
|
434 const RealType pdf_x = pdf(distribution, x);
|
cannam@160
|
435 const RealType normpdf_x = pdf(std_normal, x);
|
cannam@160
|
436 const RealType normpdf_ax = pdf(std_normal, x*shape);
|
cannam@160
|
437 RealType fx = static_cast<RealType>(2)*shape*normpdf_ax*normpdf_x - x*pdf_x;
|
cannam@160
|
438 RealType dx = static_cast<RealType>(2)*shape*x*normpdf_x*normpdf_ax*(static_cast<RealType>(1) + shape*shape) + pdf_x + x*fx;
|
cannam@160
|
439 // return both function evaluation difference f(x) and 1st derivative f'(x).
|
cannam@160
|
440 return boost::math::make_tuple(fx, -dx);
|
cannam@160
|
441 }
|
cannam@160
|
442 private:
|
cannam@160
|
443 const boost::math::skew_normal_distribution<RealType, Policy> distribution;
|
cannam@160
|
444 };
|
cannam@160
|
445
|
cannam@160
|
446 } // namespace detail
|
cannam@160
|
447
|
cannam@160
|
448 template <class RealType, class Policy>
|
cannam@160
|
449 inline RealType mode(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
450 {
|
cannam@160
|
451 const RealType scale = dist.scale();
|
cannam@160
|
452 const RealType location = dist.location();
|
cannam@160
|
453 const RealType shape = dist.shape();
|
cannam@160
|
454
|
cannam@160
|
455 static const char* function = "boost::math::mode(const skew_normal_distribution<%1%>&, %1%)";
|
cannam@160
|
456
|
cannam@160
|
457 RealType result = 0;
|
cannam@160
|
458 if(false == detail::check_scale(function, scale, &result, Policy()))
|
cannam@160
|
459 return result;
|
cannam@160
|
460 if(false == detail::check_location(function, location, &result, Policy()))
|
cannam@160
|
461 return result;
|
cannam@160
|
462 if(false == detail::check_skew_normal_shape(function, shape, &result, Policy()))
|
cannam@160
|
463 return result;
|
cannam@160
|
464
|
cannam@160
|
465 if( shape == 0 )
|
cannam@160
|
466 {
|
cannam@160
|
467 return location;
|
cannam@160
|
468 }
|
cannam@160
|
469
|
cannam@160
|
470 if( shape < 0 )
|
cannam@160
|
471 {
|
cannam@160
|
472 skew_normal_distribution<RealType, Policy> D(0, 1, -shape);
|
cannam@160
|
473 result = mode(D);
|
cannam@160
|
474 result = location-scale*result;
|
cannam@160
|
475 return result;
|
cannam@160
|
476 }
|
cannam@160
|
477
|
cannam@160
|
478 // 21 elements
|
cannam@160
|
479 static const RealType shapes[] = {
|
cannam@160
|
480 0.0,
|
cannam@160
|
481 static_cast<RealType>(1.000000000000000e-004),
|
cannam@160
|
482 static_cast<RealType>(2.069138081114790e-004),
|
cannam@160
|
483 static_cast<RealType>(4.281332398719396e-004),
|
cannam@160
|
484 static_cast<RealType>(8.858667904100824e-004),
|
cannam@160
|
485 static_cast<RealType>(1.832980710832436e-003),
|
cannam@160
|
486 static_cast<RealType>(3.792690190732250e-003),
|
cannam@160
|
487 static_cast<RealType>(7.847599703514606e-003),
|
cannam@160
|
488 static_cast<RealType>(1.623776739188722e-002),
|
cannam@160
|
489 static_cast<RealType>(3.359818286283781e-002),
|
cannam@160
|
490 static_cast<RealType>(6.951927961775606e-002),
|
cannam@160
|
491 static_cast<RealType>(1.438449888287663e-001),
|
cannam@160
|
492 static_cast<RealType>(2.976351441631319e-001),
|
cannam@160
|
493 static_cast<RealType>(6.158482110660261e-001),
|
cannam@160
|
494 static_cast<RealType>(1.274274985703135e+000),
|
cannam@160
|
495 static_cast<RealType>(2.636650898730361e+000),
|
cannam@160
|
496 static_cast<RealType>(5.455594781168514e+000),
|
cannam@160
|
497 static_cast<RealType>(1.128837891684688e+001),
|
cannam@160
|
498 static_cast<RealType>(2.335721469090121e+001),
|
cannam@160
|
499 static_cast<RealType>(4.832930238571753e+001),
|
cannam@160
|
500 static_cast<RealType>(1.000000000000000e+002)
|
cannam@160
|
501 };
|
cannam@160
|
502
|
cannam@160
|
503 // 21 elements
|
cannam@160
|
504 static const RealType guess[] = {
|
cannam@160
|
505 0.0,
|
cannam@160
|
506 static_cast<RealType>(5.000050000525391e-005),
|
cannam@160
|
507 static_cast<RealType>(1.500015000148736e-004),
|
cannam@160
|
508 static_cast<RealType>(3.500035000350010e-004),
|
cannam@160
|
509 static_cast<RealType>(7.500075000752560e-004),
|
cannam@160
|
510 static_cast<RealType>(1.450014500145258e-003),
|
cannam@160
|
511 static_cast<RealType>(3.050030500305390e-003),
|
cannam@160
|
512 static_cast<RealType>(6.250062500624765e-003),
|
cannam@160
|
513 static_cast<RealType>(1.295012950129504e-002),
|
cannam@160
|
514 static_cast<RealType>(2.675026750267495e-002),
|
cannam@160
|
515 static_cast<RealType>(5.525055250552491e-002),
|
cannam@160
|
516 static_cast<RealType>(1.132511325113255e-001),
|
cannam@160
|
517 static_cast<RealType>(2.249522495224952e-001),
|
cannam@160
|
518 static_cast<RealType>(3.992539925399257e-001),
|
cannam@160
|
519 static_cast<RealType>(5.353553535535358e-001),
|
cannam@160
|
520 static_cast<RealType>(4.954549545495457e-001),
|
cannam@160
|
521 static_cast<RealType>(3.524535245352451e-001),
|
cannam@160
|
522 static_cast<RealType>(2.182521825218249e-001),
|
cannam@160
|
523 static_cast<RealType>(1.256512565125654e-001),
|
cannam@160
|
524 static_cast<RealType>(6.945069450694508e-002),
|
cannam@160
|
525 static_cast<RealType>(3.735037350373460e-002)
|
cannam@160
|
526 };
|
cannam@160
|
527
|
cannam@160
|
528 const RealType* result_ptr = std::lower_bound(shapes, shapes+21, shape);
|
cannam@160
|
529
|
cannam@160
|
530 typedef typename std::iterator_traits<RealType*>::difference_type diff_type;
|
cannam@160
|
531
|
cannam@160
|
532 const diff_type d = std::distance(shapes, result_ptr);
|
cannam@160
|
533
|
cannam@160
|
534 BOOST_ASSERT(d > static_cast<diff_type>(0));
|
cannam@160
|
535
|
cannam@160
|
536 // TODO: make the search bounds smarter, depending on the shape parameter
|
cannam@160
|
537 RealType search_min = 0; // below zero was caught above
|
cannam@160
|
538 RealType search_max = 0.55f; // will never go above 0.55
|
cannam@160
|
539
|
cannam@160
|
540 // refine
|
cannam@160
|
541 if(d < static_cast<diff_type>(21)) // shape smaller 100
|
cannam@160
|
542 {
|
cannam@160
|
543 // it is safe to assume that d > 0, because shape==0.0 is caught earlier
|
cannam@160
|
544 result = guess[d-static_cast<diff_type>(1)]
|
cannam@160
|
545 + (guess[d]-guess[d-static_cast<diff_type>(1)])/(shapes[d]-shapes[d-static_cast<diff_type>(1)])
|
cannam@160
|
546 * (shape-shapes[d-static_cast<diff_type>(1)]);
|
cannam@160
|
547 }
|
cannam@160
|
548 else // shape greater 100
|
cannam@160
|
549 {
|
cannam@160
|
550 result = 1e-4f;
|
cannam@160
|
551 search_max = guess[19]; // set 19 instead of 20 to have a safety margin because the table may not be exact @ shape=100
|
cannam@160
|
552 }
|
cannam@160
|
553
|
cannam@160
|
554 const int get_digits = policies::digits<RealType, Policy>();// get digits from policy,
|
cannam@160
|
555 boost::uintmax_t m = policies::get_max_root_iterations<Policy>(); // and max iterations.
|
cannam@160
|
556
|
cannam@160
|
557 skew_normal_distribution<RealType, Policy> helper(0, 1, shape);
|
cannam@160
|
558
|
cannam@160
|
559 result = tools::newton_raphson_iterate(detail::skew_normal_mode_functor<RealType, Policy>(helper), result,
|
cannam@160
|
560 search_min, search_max, get_digits, m);
|
cannam@160
|
561
|
cannam@160
|
562 result = result*scale + location;
|
cannam@160
|
563
|
cannam@160
|
564 return result;
|
cannam@160
|
565 }
|
cannam@160
|
566
|
cannam@160
|
567
|
cannam@160
|
568
|
cannam@160
|
569 template <class RealType, class Policy>
|
cannam@160
|
570 inline RealType skewness(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
571 {
|
cannam@160
|
572 BOOST_MATH_STD_USING // for ADL of std functions
|
cannam@160
|
573 using namespace boost::math::constants;
|
cannam@160
|
574
|
cannam@160
|
575 static const RealType factor = four_minus_pi<RealType>()/static_cast<RealType>(2);
|
cannam@160
|
576 const RealType delta = dist.shape() / sqrt(static_cast<RealType>(1)+dist.shape()*dist.shape());
|
cannam@160
|
577
|
cannam@160
|
578 return factor * pow(root_two_div_pi<RealType>() * delta, 3) /
|
cannam@160
|
579 pow(static_cast<RealType>(1)-two_div_pi<RealType>()*delta*delta, static_cast<RealType>(1.5));
|
cannam@160
|
580 }
|
cannam@160
|
581
|
cannam@160
|
582 template <class RealType, class Policy>
|
cannam@160
|
583 inline RealType kurtosis(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
584 {
|
cannam@160
|
585 return kurtosis_excess(dist)+static_cast<RealType>(3);
|
cannam@160
|
586 }
|
cannam@160
|
587
|
cannam@160
|
588 template <class RealType, class Policy>
|
cannam@160
|
589 inline RealType kurtosis_excess(const skew_normal_distribution<RealType, Policy>& dist)
|
cannam@160
|
590 {
|
cannam@160
|
591 using namespace boost::math::constants;
|
cannam@160
|
592
|
cannam@160
|
593 static const RealType factor = pi_minus_three<RealType>()*static_cast<RealType>(2);
|
cannam@160
|
594
|
cannam@160
|
595 const RealType delta2 = static_cast<RealType>(1) / (static_cast<RealType>(1)+static_cast<RealType>(1)/(dist.shape()*dist.shape()));
|
cannam@160
|
596
|
cannam@160
|
597 const RealType x = static_cast<RealType>(1)-two_div_pi<RealType>()*delta2;
|
cannam@160
|
598 const RealType y = two_div_pi<RealType>() * delta2;
|
cannam@160
|
599
|
cannam@160
|
600 return factor * y*y / (x*x);
|
cannam@160
|
601 }
|
cannam@160
|
602
|
cannam@160
|
603 namespace detail
|
cannam@160
|
604 {
|
cannam@160
|
605
|
cannam@160
|
606 template <class RealType, class Policy>
|
cannam@160
|
607 struct skew_normal_quantile_functor
|
cannam@160
|
608 {
|
cannam@160
|
609 skew_normal_quantile_functor(const boost::math::skew_normal_distribution<RealType, Policy> dist, RealType const& p)
|
cannam@160
|
610 : distribution(dist), prob(p)
|
cannam@160
|
611 {
|
cannam@160
|
612 }
|
cannam@160
|
613
|
cannam@160
|
614 boost::math::tuple<RealType, RealType> operator()(RealType const& x)
|
cannam@160
|
615 {
|
cannam@160
|
616 RealType c = cdf(distribution, x);
|
cannam@160
|
617 RealType fx = c - prob; // Difference cdf - value - to minimize.
|
cannam@160
|
618 RealType dx = pdf(distribution, x); // pdf is 1st derivative.
|
cannam@160
|
619 // return both function evaluation difference f(x) and 1st derivative f'(x).
|
cannam@160
|
620 return boost::math::make_tuple(fx, dx);
|
cannam@160
|
621 }
|
cannam@160
|
622 private:
|
cannam@160
|
623 const boost::math::skew_normal_distribution<RealType, Policy> distribution;
|
cannam@160
|
624 RealType prob;
|
cannam@160
|
625 };
|
cannam@160
|
626
|
cannam@160
|
627 } // namespace detail
|
cannam@160
|
628
|
cannam@160
|
629 template <class RealType, class Policy>
|
cannam@160
|
630 inline RealType quantile(const skew_normal_distribution<RealType, Policy>& dist, const RealType& p)
|
cannam@160
|
631 {
|
cannam@160
|
632 const RealType scale = dist.scale();
|
cannam@160
|
633 const RealType location = dist.location();
|
cannam@160
|
634 const RealType shape = dist.shape();
|
cannam@160
|
635
|
cannam@160
|
636 static const char* function = "boost::math::quantile(const skew_normal_distribution<%1%>&, %1%)";
|
cannam@160
|
637
|
cannam@160
|
638 RealType result = 0;
|
cannam@160
|
639 if(false == detail::check_scale(function, scale, &result, Policy()))
|
cannam@160
|
640 return result;
|
cannam@160
|
641 if(false == detail::check_location(function, location, &result, Policy()))
|
cannam@160
|
642 return result;
|
cannam@160
|
643 if(false == detail::check_skew_normal_shape(function, shape, &result, Policy()))
|
cannam@160
|
644 return result;
|
cannam@160
|
645 if(false == detail::check_probability(function, p, &result, Policy()))
|
cannam@160
|
646 return result;
|
cannam@160
|
647
|
cannam@160
|
648 // Compute initial guess via Cornish-Fisher expansion.
|
cannam@160
|
649 RealType x = -boost::math::erfc_inv(2 * p, Policy()) * constants::root_two<RealType>();
|
cannam@160
|
650
|
cannam@160
|
651 // Avoid unnecessary computations if there is no skew.
|
cannam@160
|
652 if(shape != 0)
|
cannam@160
|
653 {
|
cannam@160
|
654 const RealType skew = skewness(dist);
|
cannam@160
|
655 const RealType exk = kurtosis_excess(dist);
|
cannam@160
|
656
|
cannam@160
|
657 x = x + (x*x-static_cast<RealType>(1))*skew/static_cast<RealType>(6)
|
cannam@160
|
658 + x*(x*x-static_cast<RealType>(3))*exk/static_cast<RealType>(24)
|
cannam@160
|
659 - x*(static_cast<RealType>(2)*x*x-static_cast<RealType>(5))*skew*skew/static_cast<RealType>(36);
|
cannam@160
|
660 } // if(shape != 0)
|
cannam@160
|
661
|
cannam@160
|
662 result = standard_deviation(dist)*x+mean(dist);
|
cannam@160
|
663
|
cannam@160
|
664 // handle special case of non-skew normal distribution.
|
cannam@160
|
665 if(shape == 0)
|
cannam@160
|
666 return result;
|
cannam@160
|
667
|
cannam@160
|
668 // refine the result by numerically searching the root of (p-cdf)
|
cannam@160
|
669
|
cannam@160
|
670 const RealType search_min = range(dist).first;
|
cannam@160
|
671 const RealType search_max = range(dist).second;
|
cannam@160
|
672
|
cannam@160
|
673 const int get_digits = policies::digits<RealType, Policy>();// get digits from policy,
|
cannam@160
|
674 boost::uintmax_t m = policies::get_max_root_iterations<Policy>(); // and max iterations.
|
cannam@160
|
675
|
cannam@160
|
676 result = tools::newton_raphson_iterate(detail::skew_normal_quantile_functor<RealType, Policy>(dist, p), result,
|
cannam@160
|
677 search_min, search_max, get_digits, m);
|
cannam@160
|
678
|
cannam@160
|
679 return result;
|
cannam@160
|
680 } // quantile
|
cannam@160
|
681
|
cannam@160
|
682 template <class RealType, class Policy>
|
cannam@160
|
683 inline RealType quantile(const complemented2_type<skew_normal_distribution<RealType, Policy>, RealType>& c)
|
cannam@160
|
684 {
|
cannam@160
|
685 const RealType scale = c.dist.scale();
|
cannam@160
|
686 const RealType location = c.dist.location();
|
cannam@160
|
687 const RealType shape = c.dist.shape();
|
cannam@160
|
688
|
cannam@160
|
689 static const char* function = "boost::math::quantile(const complement(skew_normal_distribution<%1%>&), %1%)";
|
cannam@160
|
690 RealType result = 0;
|
cannam@160
|
691 if(false == detail::check_scale(function, scale, &result, Policy()))
|
cannam@160
|
692 return result;
|
cannam@160
|
693 if(false == detail::check_location(function, location, &result, Policy()))
|
cannam@160
|
694 return result;
|
cannam@160
|
695 if(false == detail::check_skew_normal_shape(function, shape, &result, Policy()))
|
cannam@160
|
696 return result;
|
cannam@160
|
697 RealType q = c.param;
|
cannam@160
|
698 if(false == detail::check_probability(function, q, &result, Policy()))
|
cannam@160
|
699 return result;
|
cannam@160
|
700
|
cannam@160
|
701 skew_normal_distribution<RealType, Policy> D(-location, scale, -shape);
|
cannam@160
|
702
|
cannam@160
|
703 result = -quantile(D, q);
|
cannam@160
|
704
|
cannam@160
|
705 return result;
|
cannam@160
|
706 } // quantile
|
cannam@160
|
707
|
cannam@160
|
708
|
cannam@160
|
709 } // namespace math
|
cannam@160
|
710 } // namespace boost
|
cannam@160
|
711
|
cannam@160
|
712 // This include must be at the end, *after* the accessors
|
cannam@160
|
713 // for this distribution have been defined, in order to
|
cannam@160
|
714 // keep compilers that support two-phase lookup happy.
|
cannam@160
|
715 #include <boost/math/distributions/detail/derived_accessors.hpp>
|
cannam@160
|
716
|
cannam@160
|
717 #endif // BOOST_STATS_SKEW_NORMAL_HPP
|
cannam@160
|
718
|
cannam@160
|
719
|