skew_normal.hpp 26 KB

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