|
27 | 27 | #include <utility>
|
28 | 28 | #include <algorithm> // std::lower_bound, std::distance
|
29 | 29 |
|
| 30 | +#ifdef BOOST_MATH_INSTRUMENT_SKEW_NORMAL_ITERATIONS |
| 31 | +extern std::uintmax_t global_iter_count; |
| 32 | +#endif |
| 33 | + |
30 | 34 | namespace boost{ namespace math{
|
31 | 35 |
|
32 | 36 | namespace detail
|
@@ -614,32 +618,6 @@ namespace boost{ namespace math{
|
614 | 618 | return factor * y*y / (x*x);
|
615 | 619 | }
|
616 | 620 |
|
617 |
| - namespace detail |
618 |
| - { |
619 |
| - |
620 |
| - template <class RealType, class Policy> |
621 |
| - struct skew_normal_quantile_functor |
622 |
| - { |
623 |
| - skew_normal_quantile_functor(const boost::math::skew_normal_distribution<RealType, Policy> dist, RealType const& p) |
624 |
| - : distribution(dist), prob(p) |
625 |
| - { |
626 |
| - } |
627 |
| - |
628 |
| - boost::math::tuple<RealType, RealType> operator()(RealType const& x) |
629 |
| - { |
630 |
| - RealType c = cdf(distribution, x); |
631 |
| - RealType fx = c - prob; // Difference cdf - value - to minimize. |
632 |
| - RealType dx = pdf(distribution, x); // pdf is 1st derivative. |
633 |
| - // return both function evaluation difference f(x) and 1st derivative f'(x). |
634 |
| - return boost::math::make_tuple(fx, dx); |
635 |
| - } |
636 |
| - private: |
637 |
| - const boost::math::skew_normal_distribution<RealType, Policy> distribution; |
638 |
| - RealType prob; |
639 |
| - }; |
640 |
| - |
641 |
| - } // namespace detail |
642 |
| - |
643 | 621 | template <class RealType, class Policy>
|
644 | 622 | inline RealType quantile(const skew_normal_distribution<RealType, Policy>& dist, const RealType& p)
|
645 | 623 | {
|
@@ -681,14 +659,58 @@ namespace boost{ namespace math{
|
681 | 659 |
|
682 | 660 | // refine the result by numerically searching the root of (p-cdf)
|
683 | 661 |
|
684 |
| - const RealType search_min = support(dist).first; |
685 |
| - const RealType search_max = support(dist).second; |
686 |
| - |
687 | 662 | const int get_digits = policies::digits<RealType, Policy>();// get digits from policy,
|
688 | 663 | std::uintmax_t max_iter = policies::get_max_root_iterations<Policy>(); // and max iterations.
|
689 | 664 |
|
690 |
| - result = tools::newton_raphson_iterate(detail::skew_normal_quantile_functor<RealType, Policy>(dist, p), result, |
691 |
| - search_min, search_max, get_digits, max_iter); |
| 665 | + if (result == 0) |
| 666 | + result = tools::min_value<RealType>(); // we need to be one side of zero or the other for the root finder to work. |
| 667 | + |
| 668 | + auto fun = [&, dist, p](const RealType& x)->RealType { return cdf(dist, x) - p; }; |
| 669 | + |
| 670 | + RealType f_result = fun(result); |
| 671 | + |
| 672 | + if (f_result == 0) |
| 673 | + return result; |
| 674 | + |
| 675 | + if (f_result * result > 0) |
| 676 | + { |
| 677 | + // If the root is in the direction of zero, we need to check that we're the correct side of it: |
| 678 | + RealType f_zero = fun(static_cast<RealType>(0)); |
| 679 | + if (f_zero * f_result > 0) |
| 680 | + { |
| 681 | + // we're the wrong side of zero: |
| 682 | + result = -result; |
| 683 | + f_result = fun(result); |
| 684 | + } |
| 685 | + } |
| 686 | + |
| 687 | + RealType scaling_factor = 1.25; |
| 688 | + if (f_result * result > 0) |
| 689 | + { |
| 690 | + // We're heading towards zero... it's a long way down so use a larger scaling factor: |
| 691 | + scaling_factor = 16; |
| 692 | + } |
| 693 | + |
| 694 | + auto p_result = tools::bracket_and_solve_root(fun, result, scaling_factor, true, tools::eps_tolerance<RealType>(get_digits), max_iter, Policy()); |
| 695 | + |
| 696 | +#ifdef BOOST_MATH_INSTRUMENT_SKEW_NORMAL_ITERATIONS |
| 697 | + global_iter_count += max_iter; |
| 698 | +#endif |
| 699 | + |
| 700 | + result = (p_result.first + p_result.second) / 2; |
| 701 | + |
| 702 | + // |
| 703 | + // Try one last Newton step, just to close up the interval: |
| 704 | + // |
| 705 | + RealType step = fun(result) / pdf(dist, result); |
| 706 | + |
| 707 | + if (result - step <= p_result.first) |
| 708 | + result = p_result.first; |
| 709 | + else if (result - step >= p_result.second) |
| 710 | + result = p_result.second; |
| 711 | + else |
| 712 | + result -= step; |
| 713 | + |
692 | 714 | if (max_iter >= policies::get_max_root_iterations<Policy>())
|
693 | 715 | {
|
694 | 716 | 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
|
|
0 commit comments