ibeta_inverse.hpp

来自「Boost provides free peer-reviewed portab」· HPP 代码 · 共 940 行 · 第 1/3 页

HPP
940
字号
T temme_method_3_ibeta_inverse(T a, T b, T p, T q, const Policy& pol){   BOOST_MATH_STD_USING // ADL of std names   //   // Begin by getting an initial approximation for the quantity   // eta from the dominant part of the incomplete beta:   //   T eta0;   if(p < q)      eta0 = boost::math::gamma_q_inv(b, p, pol);   else      eta0 = boost::math::gamma_p_inv(b, q, pol);   eta0 /= a;   //   // Define the variables and powers we'll need later on:   //   T mu = b / a;   T w = sqrt(1 + mu);   T w_2 = w * w;   T w_3 = w_2 * w;   T w_4 = w_2 * w_2;   T w_5 = w_3 * w_2;   T w_6 = w_3 * w_3;   T w_7 = w_4 * w_3;   T w_8 = w_4 * w_4;   T w_9 = w_5 * w_4;   T w_10 = w_5 * w_5;   T d = eta0 - mu;   T d_2 = d * d;   T d_3 = d_2 * d;   T d_4 = d_2 * d_2;   T w1 = w + 1;   T w1_2 = w1 * w1;   T w1_3 = w1 * w1_2;   T w1_4 = w1_2 * w1_2;   //   // Now we need to compute the purturbation error terms that   // convert eta0 to eta, these are all polynomials of polynomials.   // Probably these should be re-written to use tabulated data   // (see examples above), but it's less of a win in this case as we   // need to calculate the individual powers for the denominator terms   // anyway, so we might as well use them for the numerator-polynomials   // as well....   //   // Refer to p154-p155 for the details of these expansions:   //   T e1 = (w + 2) * (w - 1) / (3 * w);   e1 += (w_3 + 9 * w_2 + 21 * w + 5) * d / (36 * w_2 * w1);   e1 -= (w_4 - 13 * w_3 + 69 * w_2 + 167 * w + 46) * d_2 / (1620 * w1_2 * w_3);   e1 -= (7 * w_5 + 21 * w_4 + 70 * w_3 + 26 * w_2 - 93 * w - 31) * d_3 / (6480 * w1_3 * w_4);   e1 -= (75 * w_6 + 202 * w_5 + 188 * w_4 - 888 * w_3 - 1345 * w_2 + 118 * w + 138) * d_4 / (272160 * w1_4 * w_5);   T e2 = (28 * w_4 + 131 * w_3 + 402 * w_2 + 581 * w + 208) * (w - 1) / (1620 * w1 * w_3);   e2 -= (35 * w_6 - 154 * w_5 - 623 * w_4 - 1636 * w_3 - 3983 * w_2 - 3514 * w - 925) * d / (12960 * w1_2 * w_4);   e2 -= (2132 * w_7 + 7915 * w_6 + 16821 * w_5 + 35066 * w_4 + 87490 * w_3 + 141183 * w_2 + 95993 * w + 21640) * d_2  / (816480 * w_5 * w1_3);   e2 -= (11053 * w_8 + 53308 * w_7 + 117010 * w_6 + 163924 * w_5 + 116188 * w_4 - 258428 * w_3 - 677042 * w_2 - 481940 * w - 105497) * d_3 / (14696640 * w1_4 * w_6);   T e3 = -((3592 * w_7 + 8375 * w_6 - 1323 * w_5 - 29198 * w_4 - 89578 * w_3 - 154413 * w_2 - 116063 * w - 29632) * (w - 1)) / (816480 * w_5 * w1_2);   e3 -= (442043 * w_9 + 2054169 * w_8 + 3803094 * w_7 + 3470754 * w_6 + 2141568 * w_5 - 2393568 * w_4 - 19904934 * w_3 - 34714674 * w_2 - 23128299 * w - 5253353) * d / (146966400 * w_6 * w1_3);   e3 -= (116932 * w_10 + 819281 * w_9 + 2378172 * w_8 + 4341330 * w_7 + 6806004 * w_6 + 10622748 * w_5 + 18739500 * w_4 + 30651894 * w_3 + 30869976 * w_2 + 15431867 * w + 2919016) * d_2 / (146966400 * w1_4 * w_7);   //   // Combine eta0 and the error terms to compute eta (Second eqaution p155):   //   T eta = eta0 + e1 / a + e2 / (a * a) + e3 / (a * a * a);   //   // Now we need to solve Eq 4.2 to obtain x.  For any given value of   // eta there are two solutions to this equation, and since the distribtion   // may be very skewed, these are not related by x ~ 1-x we used when   // implementing section 3 above.  However we know that:   //   //  cross < x <= 1       ; iff eta < mu   //          x == cross   ; iff eta == mu   //     0 <= x < cross    ; iff eta > mu   //   // Where cross == 1 / (1 + mu)   // Many thanks to Prof Temme for clarifying this point.   //   // Therefore we'll just jump straight into Newton iterations   // to solve Eq 4.2 using these bounds, and simple bisection   // as the first guess, in practice this converges pretty quickly   // and we only need a few digits correct anyway:   //   if(eta <= 0)      eta = tools::min_value<T>();   T u = eta - mu * log(eta) + (1 + mu) * log(1 + mu) - mu;   T cross = 1 / (1 + mu);   T lower = eta < mu ? cross : 0;   T upper = eta < mu ? 1 : cross;   T x = (lower + upper) / 2;   x = tools::newton_raphson_iterate(      temme_root_finder<T>(u, mu), x, lower, upper, policies::digits<T, Policy>() / 2);#ifdef BOOST_INSTRUMENT   std::cout << "Estimating x with Temme method 3: " << x << std::endl;#endif   return x;}template <class T, class Policy>struct ibeta_roots{   ibeta_roots(T _a, T _b, T t, bool inv = false)      : a(_a), b(_b), target(t), invert(inv) {}   std::tr1::tuple<T, T, T> operator()(T x)   {      BOOST_MATH_STD_USING // ADL of std names      BOOST_FPU_EXCEPTION_GUARD            T f1;      T y = 1 - x;      T f = ibeta_imp(a, b, x, Policy(), invert, true, &f1) - target;      if(invert)         f1 = -f1;      if(y == 0)         y = tools::min_value<T>() * 64;      if(x == 0)         x = tools::min_value<T>() * 64;      T f2 = f1 * (-y * a + (b - 2) * x + 1);      if(fabs(f2) < y * x * tools::max_value<T>())         f2 /= (y * x);      if(invert)         f2 = -f2;      // make sure we don't have a zero derivative:      if(f1 == 0)         f1 = (invert ? -1 : 1) * tools::min_value<T>() * 64;      return std::tr1::make_tuple(f, f1, f2);   }private:   T a, b, target;   bool invert;};template <class T, class Policy>T ibeta_inv_imp(T a, T b, T p, T q, const Policy& pol, T* py){   BOOST_MATH_STD_USING  // For ADL of math functions.   //   // The flag invert is set to true if we swap a for b and p for q,   // in which case the result has to be subtracted from 1:   //   bool invert = false;   //   // Depending upon which approximation method we use, we may end up   // calculating either x or y initially (where y = 1-x):   //   T x = 0; // Set to a safe zero to avoid a   // MSVC 2005 warning C4701: potentially uninitialized local variable 'x' used   // But code inspection appears to ensure that x IS assigned whatever the code path.   T y;    // For some of the methods we can put tighter bounds   // on the result than simply [0,1]:   //   T lower = 0;   T upper = 1;   //   // Student's T with b = 0.5 gets handled as a special case, swap   // around if the arguments are in the "wrong" order:   //   if(a == 0.5f)   {      std::swap(a, b);      std::swap(p, q);      invert = !invert;   }   //   // Handle trivial cases first:   //   if(q == 0)   {      if(py) *py = 0;      return 1;   }   else if(p == 0)   {      if(py) *py = 1;      return 0;   }   else if((a == 1) && (b == 1))   {      if(py) *py = 1 - p;      return p;   }   else if((b == 0.5f) && (a >= 0.5f))   {      //      // We have a Student's T distribution:      x = find_ibeta_inv_from_t_dist(a, p, q, &y, pol);   }   else if(a + b > 5)   {      //      // When a+b is large then we can use one of Prof Temme's      // asymptotic expansions, begin by swapping things around      // so that p < 0.5, we do this to avoid cancellations errors      // when p is large.      //      if(p > 0.5)      {         std::swap(a, b);         std::swap(p, q);         invert = !invert;      }      T minv = (std::min)(a, b);      T maxv = (std::max)(a, b);      if((sqrt(minv) > (maxv - minv)) && (minv > 5))      {         //         // When a and b differ by a small amount         // the curve is quite symmetrical and we can use an error         // function to approximate the inverse. This is the cheapest         // of the three Temme expantions, and the calculated value         // for x will never be much larger than p, so we don't have         // to worry about cancellation as long as p is small.         //         x = temme_method_1_ibeta_inverse(a, b, p, pol);         y = 1 - x;      }      else      {         T r = a + b;         T theta = asin(sqrt(a / r));         T lambda = minv / r;         if((lambda >= 0.2) && (lambda <= 0.8) && (lambda >= 10))         {            //            // The second error function case is the next cheapest            // to use, it brakes down when the result is likely to be            // very small, if a+b is also small, but we can use a            // cheaper expansion there in any case.  As before x won't            // be much larger than p, so as long as p is small we should            // be free of cancellation error.            //            T ppa = pow(p, 1/a);            if((ppa < 0.0025) && (a + b < 200))            {               x = ppa * pow(a * boost::math::beta(a, b, pol), 1/a);            }            else               x = temme_method_2_ibeta_inverse(a, b, p, r, theta, pol);            y = 1 - x;         }         else         {            //            // If we get here then a and b are very different in magnitude            // and we need to use the third of Temme's methods which            // involves inverting the incomplete gamma.  This is much more            // expensive than the other methods.  We also can only use this            // method when a > b, which can lead to cancellation errors            // if we really want y (as we will when x is close to 1), so            // a different expansion is used in that case.            //            if(a < b)            {               std::swap(a, b);               std::swap(p, q);               invert = !invert;            }            //            // Try and compute the easy way first:            //            T bet = 0;            if(b < 2)               bet = boost::math::beta(a, b, pol);            if(bet != 0)            {               y = pow(b * q * bet, 1/b);               x = 1 - y;            }            else                y = 1;            if(y > 1e-5)            {               x = temme_method_3_ibeta_inverse(a, b, p, q, pol);               y = 1 - x;            }         }      }   }   else if((a < 1) && (b < 1))   {      //      // Both a and b less than 1,      // there is a point of inflection at xs:      //      T xs = (1 - a) / (2 - a - b);      //      // Now we need to ensure that we start our iteration from the      // right side of the inflection point:      //      T fs = boost::math::ibeta(a, b, xs, pol) - p;      if(fabs(fs) / p < tools::epsilon<T>() * 3)      {         // The result is at the point of inflection, best just return it:         *py = invert ? xs : 1 - xs;         return invert ? 1-xs : xs;      }      if(fs < 0)      {         std::swap(a, b);         std::swap(p, q);         invert = true;         xs = 1 - xs;      }      T xg = pow(a * p * boost::math::beta(a, b, pol), 1/a);      x = xg / (1 + xg);      y = 1 / (1 + xg);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?