annotate any/include/boost/math/distributions/skew_normal.hpp @ 160:cff480c41f97

Add some cross-platform Boost headers
author Chris Cannam <cannam@all-day-breakfast.com>
date Sat, 16 Feb 2019 16:31:25 +0000
parents
children
rev   line source
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