Chris@16: // Copyright Benjamin Sobotta 2012 Chris@16: Chris@16: // Use, modification and distribution are subject to the Chris@16: // Boost Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_STATS_SKEW_NORMAL_HPP Chris@16: #define BOOST_STATS_SKEW_NORMAL_HPP Chris@16: Chris@16: // http://en.wikipedia.org/wiki/Skew_normal_distribution Chris@16: // http://azzalini.stat.unipd.it/SN/ Chris@16: // Also: Chris@16: // Azzalini, A. (1985). "A class of distributions which includes the normal ones". Chris@16: // Scand. J. Statist. 12: 171-178. Chris@16: Chris@16: #include // TODO add skew_normal distribution to fwd.hpp! Chris@16: #include // Owen's T function Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // Newton-Raphson Chris@16: #include Chris@16: #include // pdf max finder. Chris@16: Chris@16: #include Chris@16: #include // std::lower_bound, std::distance Chris@16: Chris@16: namespace boost{ namespace math{ Chris@16: Chris@16: namespace detail Chris@16: { Chris@16: template Chris@16: inline bool check_skew_normal_shape( Chris@16: const char* function, Chris@16: RealType shape, Chris@16: RealType* result, Chris@16: const Policy& pol) Chris@16: { Chris@16: if(!(boost::math::isfinite)(shape)) Chris@16: { Chris@16: *result = Chris@16: policies::raise_domain_error(function, Chris@16: "Shape parameter is %1%, but must be finite!", Chris@16: shape, pol); Chris@16: return false; Chris@16: } Chris@16: return true; Chris@16: } Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: template > Chris@16: class skew_normal_distribution Chris@16: { Chris@16: public: Chris@16: typedef RealType value_type; Chris@16: typedef Policy policy_type; Chris@16: Chris@16: skew_normal_distribution(RealType l_location = 0, RealType l_scale = 1, RealType l_shape = 0) Chris@16: : location_(l_location), scale_(l_scale), shape_(l_shape) Chris@16: { // Default is a 'standard' normal distribution N01. (shape=0 results in the normal distribution with no skew) Chris@16: static const char* function = "boost::math::skew_normal_distribution<%1%>::skew_normal_distribution"; Chris@16: Chris@16: RealType result; Chris@16: detail::check_scale(function, l_scale, &result, Policy()); Chris@16: detail::check_location(function, l_location, &result, Policy()); Chris@16: detail::check_skew_normal_shape(function, l_shape, &result, Policy()); Chris@16: } Chris@16: Chris@16: RealType location()const Chris@16: { Chris@16: return location_; Chris@16: } Chris@16: Chris@16: RealType scale()const Chris@16: { Chris@16: return scale_; Chris@16: } Chris@16: Chris@16: RealType shape()const Chris@16: { Chris@16: return shape_; Chris@16: } Chris@16: Chris@16: Chris@16: private: Chris@16: // Chris@16: // Data members: Chris@16: // Chris@16: RealType location_; // distribution location. Chris@16: RealType scale_; // distribution scale. Chris@16: RealType shape_; // distribution shape. Chris@16: }; // class skew_normal_distribution Chris@16: Chris@16: typedef skew_normal_distribution skew_normal; Chris@16: Chris@16: template Chris@16: inline const std::pair range(const skew_normal_distribution& /*dist*/) Chris@16: { // Range of permissible values for random variable x. Chris@16: using boost::math::tools::max_value; Chris@16: return std::pair( Chris@16: std::numeric_limits::has_infinity ? -std::numeric_limits::infinity() : -max_value(), Chris@16: std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : max_value()); // - to + max value. Chris@16: } Chris@16: Chris@16: template Chris@16: inline const std::pair support(const skew_normal_distribution& /*dist*/) Chris@16: { // Range of supported values for random variable x. Chris@16: // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. Chris@16: Chris@16: using boost::math::tools::max_value; Chris@16: return std::pair(-max_value(), max_value()); // - to + max value. Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType pdf(const skew_normal_distribution& dist, const RealType& x) Chris@16: { Chris@16: const RealType scale = dist.scale(); Chris@16: const RealType location = dist.location(); Chris@16: const RealType shape = dist.shape(); Chris@16: Chris@16: static const char* function = "boost::math::pdf(const skew_normal_distribution<%1%>&, %1%)"; Chris@16: if((boost::math::isinf)(x)) Chris@16: { Chris@16: return 0; // pdf + and - infinity is zero. Chris@16: } Chris@16: // Below produces MSVC 4127 warnings, so the above used instead. Chris@16: //if(std::numeric_limits::has_infinity && abs(x) == std::numeric_limits::infinity()) Chris@16: //{ // pdf + and - infinity is zero. Chris@16: // return 0; Chris@16: //} Chris@16: Chris@16: RealType result = 0; Chris@16: if(false == detail::check_scale(function, scale, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: if(false == detail::check_location(function, location, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: if(false == detail::check_skew_normal_shape(function, shape, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: if(false == detail::check_x(function, x, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: Chris@16: const RealType transformed_x = (x-location)/scale; Chris@16: Chris@16: normal_distribution std_normal; Chris@16: Chris@16: result = pdf(std_normal, transformed_x) * cdf(std_normal, shape*transformed_x) * 2 / scale; Chris@16: Chris@16: return result; Chris@16: } // pdf Chris@16: Chris@16: template Chris@16: inline RealType cdf(const skew_normal_distribution& dist, const RealType& x) Chris@16: { Chris@16: const RealType scale = dist.scale(); Chris@16: const RealType location = dist.location(); Chris@16: const RealType shape = dist.shape(); Chris@16: Chris@16: static const char* function = "boost::math::cdf(const skew_normal_distribution<%1%>&, %1%)"; Chris@16: RealType result = 0; Chris@16: if(false == detail::check_scale(function, scale, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: if(false == detail::check_location(function, location, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: if(false == detail::check_skew_normal_shape(function, shape, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: if((boost::math::isinf)(x)) Chris@16: { Chris@16: if(x < 0) return 0; // -infinity Chris@16: return 1; // + infinity Chris@16: } Chris@16: // These produce MSVC 4127 warnings, so the above used instead. Chris@16: //if(std::numeric_limits::has_infinity && x == std::numeric_limits::infinity()) Chris@16: //{ // cdf +infinity is unity. Chris@16: // return 1; Chris@16: //} Chris@16: //if(std::numeric_limits::has_infinity && x == -std::numeric_limits::infinity()) Chris@16: //{ // cdf -infinity is zero. Chris@16: // return 0; Chris@16: //} Chris@16: if(false == detail::check_x(function, x, &result, Policy())) Chris@16: { Chris@16: return result; Chris@16: } Chris@16: Chris@16: const RealType transformed_x = (x-location)/scale; Chris@16: Chris@16: normal_distribution std_normal; Chris@16: Chris@16: result = cdf(std_normal, transformed_x) - owens_t(transformed_x, shape)*static_cast(2); Chris@16: Chris@16: return result; Chris@16: } // cdf Chris@16: Chris@16: template Chris@16: inline RealType cdf(const complemented2_type, RealType>& c) Chris@16: { Chris@16: const RealType scale = c.dist.scale(); Chris@16: const RealType location = c.dist.location(); Chris@16: const RealType shape = c.dist.shape(); Chris@16: const RealType x = c.param; Chris@16: Chris@16: static const char* function = "boost::math::cdf(const complement(skew_normal_distribution<%1%>&), %1%)"; Chris@16: Chris@16: if((boost::math::isinf)(x)) Chris@16: { Chris@16: if(x < 0) return 1; // cdf complement -infinity is unity. Chris@16: return 0; // cdf complement +infinity is zero Chris@16: } Chris@16: // These produce MSVC 4127 warnings, so the above used instead. Chris@16: //if(std::numeric_limits::has_infinity && x == std::numeric_limits::infinity()) Chris@16: //{ // cdf complement +infinity is zero. Chris@16: // return 0; Chris@16: //} Chris@16: //if(std::numeric_limits::has_infinity && x == -std::numeric_limits::infinity()) Chris@16: //{ // cdf complement -infinity is unity. Chris@16: // return 1; Chris@16: //} Chris@16: RealType result = 0; Chris@16: if(false == detail::check_scale(function, scale, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_location(function, location, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_skew_normal_shape(function, shape, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_x(function, x, &result, Policy())) Chris@16: return result; Chris@16: Chris@16: const RealType transformed_x = (x-location)/scale; Chris@16: Chris@16: normal_distribution std_normal; Chris@16: Chris@16: result = cdf(complement(std_normal, transformed_x)) + owens_t(transformed_x, shape)*static_cast(2); Chris@16: return result; Chris@16: } // cdf complement Chris@16: Chris@16: template Chris@16: inline RealType location(const skew_normal_distribution& dist) Chris@16: { Chris@16: return dist.location(); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType scale(const skew_normal_distribution& dist) Chris@16: { Chris@16: return dist.scale(); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType shape(const skew_normal_distribution& dist) Chris@16: { Chris@16: return dist.shape(); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType mean(const skew_normal_distribution& dist) Chris@16: { Chris@16: BOOST_MATH_STD_USING // for ADL of std functions Chris@16: Chris@16: using namespace boost::math::constants; Chris@16: Chris@16: //const RealType delta = dist.shape() / sqrt(static_cast(1)+dist.shape()*dist.shape()); Chris@16: Chris@16: //return dist.location() + dist.scale() * delta * root_two_div_pi(); Chris@16: Chris@16: return dist.location() + dist.scale() * dist.shape() / sqrt(pi()+pi()*dist.shape()*dist.shape()) * root_two(); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType variance(const skew_normal_distribution& dist) Chris@16: { Chris@16: using namespace boost::math::constants; Chris@16: Chris@16: const RealType delta2 = static_cast(1) / (static_cast(1)+static_cast(1)/(dist.shape()*dist.shape())); Chris@16: //const RealType inv_delta2 = static_cast(1)+static_cast(1)/(dist.shape()*dist.shape()); Chris@16: Chris@16: RealType variance = dist.scale()*dist.scale()*(static_cast(1)-two_div_pi()*delta2); Chris@16: //RealType variance = dist.scale()*dist.scale()*(static_cast(1)-two_div_pi()/inv_delta2); Chris@16: Chris@16: return variance; Chris@16: } Chris@16: Chris@16: namespace detail Chris@16: { Chris@16: /* Chris@16: TODO No closed expression for mode, so use max of pdf. Chris@16: */ Chris@16: Chris@16: template Chris@16: inline RealType mode_fallback(const skew_normal_distribution& dist) Chris@16: { // mode. Chris@16: static const char* function = "mode(skew_normal_distribution<%1%> const&)"; Chris@16: const RealType scale = dist.scale(); Chris@16: const RealType location = dist.location(); Chris@16: const RealType shape = dist.shape(); Chris@16: Chris@16: RealType result; Chris@16: if(!detail::check_scale( Chris@16: function, Chris@16: scale, &result, Policy()) Chris@16: || Chris@16: !detail::check_skew_normal_shape( Chris@16: function, Chris@16: shape, Chris@16: &result, Chris@16: Policy())) Chris@16: return result; Chris@16: Chris@16: if( shape == 0 ) Chris@16: { Chris@16: return location; Chris@16: } Chris@16: Chris@16: if( shape < 0 ) Chris@16: { Chris@16: skew_normal_distribution D(0, 1, -shape); Chris@16: result = mode_fallback(D); Chris@16: result = location-scale*result; Chris@16: return result; Chris@16: } Chris@16: Chris@16: BOOST_MATH_STD_USING Chris@16: Chris@16: // 21 elements Chris@16: static const RealType shapes[] = { Chris@16: 0.0, Chris@16: 1.000000000000000e-004, Chris@16: 2.069138081114790e-004, Chris@16: 4.281332398719396e-004, Chris@16: 8.858667904100824e-004, Chris@16: 1.832980710832436e-003, Chris@16: 3.792690190732250e-003, Chris@16: 7.847599703514606e-003, Chris@16: 1.623776739188722e-002, Chris@16: 3.359818286283781e-002, Chris@16: 6.951927961775606e-002, Chris@16: 1.438449888287663e-001, Chris@16: 2.976351441631319e-001, Chris@16: 6.158482110660261e-001, Chris@16: 1.274274985703135e+000, Chris@16: 2.636650898730361e+000, Chris@16: 5.455594781168514e+000, Chris@16: 1.128837891684688e+001, Chris@16: 2.335721469090121e+001, Chris@16: 4.832930238571753e+001, Chris@16: 1.000000000000000e+002}; Chris@16: Chris@16: // 21 elements Chris@16: static const RealType guess[] = { Chris@16: 0.0, Chris@16: 5.000050000525391e-005, Chris@16: 1.500015000148736e-004, Chris@16: 3.500035000350010e-004, Chris@16: 7.500075000752560e-004, Chris@16: 1.450014500145258e-003, Chris@16: 3.050030500305390e-003, Chris@16: 6.250062500624765e-003, Chris@16: 1.295012950129504e-002, Chris@16: 2.675026750267495e-002, Chris@16: 5.525055250552491e-002, Chris@16: 1.132511325113255e-001, Chris@16: 2.249522495224952e-001, Chris@16: 3.992539925399257e-001, Chris@16: 5.353553535535358e-001, Chris@16: 4.954549545495457e-001, Chris@16: 3.524535245352451e-001, Chris@16: 2.182521825218249e-001, Chris@16: 1.256512565125654e-001, Chris@16: 6.945069450694508e-002, Chris@16: 3.735037350373460e-002 Chris@16: }; Chris@16: Chris@16: const RealType* result_ptr = std::lower_bound(shapes, shapes+21, shape); Chris@16: Chris@16: typedef typename std::iterator_traits::difference_type diff_type; Chris@16: Chris@16: const diff_type d = std::distance(shapes, result_ptr); Chris@16: Chris@16: BOOST_ASSERT(d > static_cast(0)); Chris@16: Chris@16: // refine Chris@16: if(d < static_cast(21)) // shape smaller 100 Chris@16: { Chris@16: result = guess[d-static_cast(1)] Chris@16: + (guess[d]-guess[d-static_cast(1)])/(shapes[d]-shapes[d-static_cast(1)]) Chris@16: * (shape-shapes[d-static_cast(1)]); Chris@16: } Chris@16: else // shape greater 100 Chris@16: { Chris@16: result = 1e-4; Chris@16: } Chris@16: Chris@16: skew_normal_distribution helper(0, 1, shape); Chris@16: Chris@16: result = detail::generic_find_mode_01(helper, result, function); Chris@16: Chris@16: result = result*scale + location; Chris@16: Chris@16: return result; Chris@16: } // mode_fallback Chris@16: Chris@16: Chris@16: /* Chris@16: * TODO No closed expression for mode, so use f'(x) = 0 Chris@16: */ Chris@16: template Chris@16: struct skew_normal_mode_functor Chris@16: { Chris@16: skew_normal_mode_functor(const boost::math::skew_normal_distribution dist) Chris@16: : distribution(dist) Chris@16: { Chris@16: } Chris@16: Chris@16: boost::math::tuple operator()(RealType const& x) Chris@16: { Chris@16: normal_distribution std_normal; Chris@16: const RealType shape = distribution.shape(); Chris@16: const RealType pdf_x = pdf(distribution, x); Chris@16: const RealType normpdf_x = pdf(std_normal, x); Chris@16: const RealType normpdf_ax = pdf(std_normal, x*shape); Chris@16: RealType fx = static_cast(2)*shape*normpdf_ax*normpdf_x - x*pdf_x; Chris@16: RealType dx = static_cast(2)*shape*x*normpdf_x*normpdf_ax*(static_cast(1) + shape*shape) + pdf_x + x*fx; Chris@16: // return both function evaluation difference f(x) and 1st derivative f'(x). Chris@16: return boost::math::make_tuple(fx, -dx); Chris@16: } Chris@16: private: Chris@16: const boost::math::skew_normal_distribution distribution; Chris@16: }; Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: template Chris@16: inline RealType mode(const skew_normal_distribution& dist) Chris@16: { Chris@16: const RealType scale = dist.scale(); Chris@16: const RealType location = dist.location(); Chris@16: const RealType shape = dist.shape(); Chris@16: Chris@16: static const char* function = "boost::math::mode(const skew_normal_distribution<%1%>&, %1%)"; Chris@16: Chris@16: RealType result = 0; Chris@16: if(false == detail::check_scale(function, scale, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_location(function, location, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_skew_normal_shape(function, shape, &result, Policy())) Chris@16: return result; Chris@16: Chris@16: if( shape == 0 ) Chris@16: { Chris@16: return location; Chris@16: } Chris@16: Chris@16: if( shape < 0 ) Chris@16: { Chris@16: skew_normal_distribution D(0, 1, -shape); Chris@16: result = mode(D); Chris@16: result = location-scale*result; Chris@16: return result; Chris@16: } Chris@16: Chris@16: // 21 elements Chris@16: static const RealType shapes[] = { Chris@16: 0.0, Chris@16: static_cast(1.000000000000000e-004), Chris@16: static_cast(2.069138081114790e-004), Chris@16: static_cast(4.281332398719396e-004), Chris@16: static_cast(8.858667904100824e-004), Chris@16: static_cast(1.832980710832436e-003), Chris@16: static_cast(3.792690190732250e-003), Chris@16: static_cast(7.847599703514606e-003), Chris@16: static_cast(1.623776739188722e-002), Chris@16: static_cast(3.359818286283781e-002), Chris@16: static_cast(6.951927961775606e-002), Chris@16: static_cast(1.438449888287663e-001), Chris@16: static_cast(2.976351441631319e-001), Chris@16: static_cast(6.158482110660261e-001), Chris@16: static_cast(1.274274985703135e+000), Chris@16: static_cast(2.636650898730361e+000), Chris@16: static_cast(5.455594781168514e+000), Chris@16: static_cast(1.128837891684688e+001), Chris@16: static_cast(2.335721469090121e+001), Chris@16: static_cast(4.832930238571753e+001), Chris@16: static_cast(1.000000000000000e+002) Chris@16: }; Chris@16: Chris@16: // 21 elements Chris@16: static const RealType guess[] = { Chris@16: 0.0, Chris@16: static_cast(5.000050000525391e-005), Chris@16: static_cast(1.500015000148736e-004), Chris@16: static_cast(3.500035000350010e-004), Chris@16: static_cast(7.500075000752560e-004), Chris@16: static_cast(1.450014500145258e-003), Chris@16: static_cast(3.050030500305390e-003), Chris@16: static_cast(6.250062500624765e-003), Chris@16: static_cast(1.295012950129504e-002), Chris@16: static_cast(2.675026750267495e-002), Chris@16: static_cast(5.525055250552491e-002), Chris@16: static_cast(1.132511325113255e-001), Chris@16: static_cast(2.249522495224952e-001), Chris@16: static_cast(3.992539925399257e-001), Chris@16: static_cast(5.353553535535358e-001), Chris@16: static_cast(4.954549545495457e-001), Chris@16: static_cast(3.524535245352451e-001), Chris@16: static_cast(2.182521825218249e-001), Chris@16: static_cast(1.256512565125654e-001), Chris@16: static_cast(6.945069450694508e-002), Chris@16: static_cast(3.735037350373460e-002) Chris@16: }; Chris@16: Chris@16: const RealType* result_ptr = std::lower_bound(shapes, shapes+21, shape); Chris@16: Chris@16: typedef typename std::iterator_traits::difference_type diff_type; Chris@16: Chris@16: const diff_type d = std::distance(shapes, result_ptr); Chris@16: Chris@16: BOOST_ASSERT(d > static_cast(0)); Chris@16: Chris@16: // TODO: make the search bounds smarter, depending on the shape parameter Chris@16: RealType search_min = 0; // below zero was caught above Chris@16: RealType search_max = 0.55f; // will never go above 0.55 Chris@16: Chris@16: // refine Chris@16: if(d < static_cast(21)) // shape smaller 100 Chris@16: { Chris@16: // it is safe to assume that d > 0, because shape==0.0 is caught earlier Chris@16: result = guess[d-static_cast(1)] Chris@16: + (guess[d]-guess[d-static_cast(1)])/(shapes[d]-shapes[d-static_cast(1)]) Chris@16: * (shape-shapes[d-static_cast(1)]); Chris@16: } Chris@16: else // shape greater 100 Chris@16: { Chris@16: result = 1e-4f; Chris@16: search_max = guess[19]; // set 19 instead of 20 to have a safety margin because the table may not be exact @ shape=100 Chris@16: } Chris@16: Chris@16: const int get_digits = policies::digits();// get digits from policy, Chris@16: boost::uintmax_t m = policies::get_max_root_iterations(); // and max iterations. Chris@16: Chris@16: skew_normal_distribution helper(0, 1, shape); Chris@16: Chris@16: result = tools::newton_raphson_iterate(detail::skew_normal_mode_functor(helper), result, Chris@16: search_min, search_max, get_digits, m); Chris@16: Chris@16: result = result*scale + location; Chris@16: Chris@16: return result; Chris@16: } Chris@16: Chris@16: Chris@16: Chris@16: template Chris@16: inline RealType skewness(const skew_normal_distribution& dist) Chris@16: { Chris@16: BOOST_MATH_STD_USING // for ADL of std functions Chris@16: using namespace boost::math::constants; Chris@16: Chris@16: static const RealType factor = four_minus_pi()/static_cast(2); Chris@16: const RealType delta = dist.shape() / sqrt(static_cast(1)+dist.shape()*dist.shape()); Chris@16: Chris@16: return factor * pow(root_two_div_pi() * delta, 3) / Chris@16: pow(static_cast(1)-two_div_pi()*delta*delta, static_cast(1.5)); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType kurtosis(const skew_normal_distribution& dist) Chris@16: { Chris@16: return kurtosis_excess(dist)+static_cast(3); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType kurtosis_excess(const skew_normal_distribution& dist) Chris@16: { Chris@16: using namespace boost::math::constants; Chris@16: Chris@16: static const RealType factor = pi_minus_three()*static_cast(2); Chris@16: Chris@16: const RealType delta2 = static_cast(1) / (static_cast(1)+static_cast(1)/(dist.shape()*dist.shape())); Chris@16: Chris@16: const RealType x = static_cast(1)-two_div_pi()*delta2; Chris@16: const RealType y = two_div_pi() * delta2; Chris@16: Chris@16: return factor * y*y / (x*x); Chris@16: } Chris@16: Chris@16: namespace detail Chris@16: { Chris@16: Chris@16: template Chris@16: struct skew_normal_quantile_functor Chris@16: { Chris@16: skew_normal_quantile_functor(const boost::math::skew_normal_distribution dist, RealType const& p) Chris@16: : distribution(dist), prob(p) Chris@16: { Chris@16: } Chris@16: Chris@16: boost::math::tuple operator()(RealType const& x) Chris@16: { Chris@16: RealType c = cdf(distribution, x); Chris@16: RealType fx = c - prob; // Difference cdf - value - to minimize. Chris@16: RealType dx = pdf(distribution, x); // pdf is 1st derivative. Chris@16: // return both function evaluation difference f(x) and 1st derivative f'(x). Chris@16: return boost::math::make_tuple(fx, dx); Chris@16: } Chris@16: private: Chris@16: const boost::math::skew_normal_distribution distribution; Chris@16: RealType prob; Chris@16: }; Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: template Chris@16: inline RealType quantile(const skew_normal_distribution& dist, const RealType& p) Chris@16: { Chris@16: const RealType scale = dist.scale(); Chris@16: const RealType location = dist.location(); Chris@16: const RealType shape = dist.shape(); Chris@16: Chris@16: static const char* function = "boost::math::quantile(const skew_normal_distribution<%1%>&, %1%)"; Chris@16: Chris@16: RealType result = 0; Chris@16: if(false == detail::check_scale(function, scale, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_location(function, location, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_skew_normal_shape(function, shape, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_probability(function, p, &result, Policy())) Chris@16: return result; Chris@16: Chris@16: // Compute initial guess via Cornish-Fisher expansion. Chris@16: RealType x = -boost::math::erfc_inv(2 * p, Policy()) * constants::root_two(); Chris@16: Chris@16: // Avoid unnecessary computations if there is no skew. Chris@16: if(shape != 0) Chris@16: { Chris@16: const RealType skew = skewness(dist); Chris@16: const RealType exk = kurtosis_excess(dist); Chris@16: Chris@16: x = x + (x*x-static_cast(1))*skew/static_cast(6) Chris@16: + x*(x*x-static_cast(3))*exk/static_cast(24) Chris@16: - x*(static_cast(2)*x*x-static_cast(5))*skew*skew/static_cast(36); Chris@16: } // if(shape != 0) Chris@16: Chris@16: result = standard_deviation(dist)*x+mean(dist); Chris@16: Chris@16: // handle special case of non-skew normal distribution. Chris@16: if(shape == 0) Chris@16: return result; Chris@16: Chris@16: // refine the result by numerically searching the root of (p-cdf) Chris@16: Chris@16: const RealType search_min = range(dist).first; Chris@16: const RealType search_max = range(dist).second; Chris@16: Chris@16: const int get_digits = policies::digits();// get digits from policy, Chris@16: boost::uintmax_t m = policies::get_max_root_iterations(); // and max iterations. Chris@16: Chris@16: result = tools::newton_raphson_iterate(detail::skew_normal_quantile_functor(dist, p), result, Chris@16: search_min, search_max, get_digits, m); Chris@16: Chris@16: return result; Chris@16: } // quantile Chris@16: Chris@16: template Chris@16: inline RealType quantile(const complemented2_type, RealType>& c) Chris@16: { Chris@16: const RealType scale = c.dist.scale(); Chris@16: const RealType location = c.dist.location(); Chris@16: const RealType shape = c.dist.shape(); Chris@16: Chris@16: static const char* function = "boost::math::quantile(const complement(skew_normal_distribution<%1%>&), %1%)"; Chris@16: RealType result = 0; Chris@16: if(false == detail::check_scale(function, scale, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_location(function, location, &result, Policy())) Chris@16: return result; Chris@16: if(false == detail::check_skew_normal_shape(function, shape, &result, Policy())) Chris@16: return result; Chris@16: RealType q = c.param; Chris@16: if(false == detail::check_probability(function, q, &result, Policy())) Chris@16: return result; Chris@16: Chris@16: skew_normal_distribution D(-location, scale, -shape); Chris@16: Chris@16: result = -quantile(D, q); Chris@16: Chris@16: return result; Chris@16: } // quantile Chris@16: Chris@16: Chris@16: } // namespace math Chris@16: } // namespace boost Chris@16: Chris@16: // This include must be at the end, *after* the accessors Chris@16: // for this distribution have been defined, in order to Chris@16: // keep compilers that support two-phase lookup happy. Chris@16: #include Chris@16: Chris@16: #endif // BOOST_STATS_SKEW_NORMAL_HPP Chris@16: Chris@16: