skew_normal.hpp 27 KB

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