File indexing completed on 2025-01-18 09:40:05
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_HYPERGEOMETRIC_1F1_LARGE_ABZ_HPP_
0009 #define BOOST_HYPERGEOMETRIC_1F1_LARGE_ABZ_HPP_
0010
0011 #include <boost/math/special_functions/detail/hypergeometric_1F1_bessel.hpp>
0012 #include <boost/math/special_functions/detail/hypergeometric_series.hpp>
0013 #include <boost/math/special_functions/gamma.hpp>
0014 #include <boost/math/special_functions/trunc.hpp>
0015
0016 namespace boost { namespace math { namespace detail {
0017
0018 template <class T>
0019 inline bool is_negative_integer(const T& x)
0020 {
0021 using std::floor;
0022 return (x <= 0) && (floor(x) == x);
0023 }
0024
0025
0026 template <class T, class Policy>
0027 struct hypergeometric_1F1_igamma_series
0028 {
0029 enum{ cache_size = 64 };
0030
0031 typedef T result_type;
0032 hypergeometric_1F1_igamma_series(const T& alpha, const T& delta, const T& x, const Policy& pol)
0033 : delta_poch(-delta), alpha_poch(alpha), x(x), k(0), cache_offset(0), pol(pol)
0034 {
0035 BOOST_MATH_STD_USING
0036 T log_term = log(x) * -alpha;
0037 log_scaling = lltrunc(log_term - 3 - boost::math::tools::log_min_value<T>() / 50);
0038 term = exp(log_term - log_scaling);
0039 refill_cache();
0040 }
0041 T operator()()
0042 {
0043 if (k - cache_offset >= cache_size)
0044 {
0045 cache_offset += cache_size;
0046 refill_cache();
0047 }
0048 T result = term * gamma_cache[k - cache_offset];
0049 term *= delta_poch * alpha_poch / (++k * x);
0050 delta_poch += 1;
0051 alpha_poch += 1;
0052 return result;
0053 }
0054 void refill_cache()
0055 {
0056 typedef typename lanczos::lanczos<T, Policy>::type lanczos_type;
0057
0058 gamma_cache[cache_size - 1] = boost::math::gamma_p(alpha_poch + ((int)cache_size - 1), x, pol);
0059 for (int i = cache_size - 1; i > 0; --i)
0060 {
0061 gamma_cache[i - 1] = gamma_cache[i] >= 1 ? T(1) : T(gamma_cache[i] + regularised_gamma_prefix(T(alpha_poch + (i - 1)), x, pol, lanczos_type()) / (alpha_poch + (i - 1)));
0062 }
0063 }
0064 T delta_poch, alpha_poch, x, term;
0065 T gamma_cache[cache_size];
0066 int k;
0067 long long log_scaling;
0068 int cache_offset;
0069 Policy pol;
0070 };
0071
0072 template <class T, class Policy>
0073 T hypergeometric_1F1_igamma(const T& a, const T& b, const T& x, const T& b_minus_a, const Policy& pol, long long& log_scaling)
0074 {
0075 BOOST_MATH_STD_USING
0076 if (b_minus_a == 0)
0077 {
0078
0079 long long scale = lltrunc(x, pol);
0080 log_scaling += scale;
0081 return exp(x - scale);
0082 }
0083 hypergeometric_1F1_igamma_series<T, Policy> s(b_minus_a, a - 1, x, pol);
0084 log_scaling += s.log_scaling;
0085 std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
0086 T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
0087 boost::math::policies::check_series_iterations<T>("boost::math::tgamma<%1%>(%1%,%1%)", max_iter, pol);
0088 T log_prefix = x + boost::math::lgamma(b, pol) - boost::math::lgamma(a, pol);
0089 long long scale = lltrunc(log_prefix);
0090 log_scaling += scale;
0091 return result * exp(log_prefix - scale);
0092 }
0093
0094 template <class T, class Policy>
0095 T hypergeometric_1F1_shift_on_a(T h, const T& a_local, const T& b_local, const T& x, int a_shift, const Policy& pol, long long& log_scaling)
0096 {
0097 BOOST_MATH_STD_USING
0098 T a = a_local + a_shift;
0099 if (a_shift == 0)
0100 return h;
0101 else if (a_shift > 0)
0102 {
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114 T crossover_a = (b_local - x) / 2;
0115 int crossover_shift = itrunc(crossover_a - a_local);
0116
0117 if (crossover_shift > 1)
0118 {
0119
0120
0121
0122
0123 if (crossover_shift > a_shift)
0124 crossover_shift = a_shift;
0125 crossover_a = a_local + crossover_shift;
0126 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef(crossover_a, b_local, x);
0127 std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
0128 T b_ratio = boost::math::tools::function_ratio_from_backwards_recurrence(b_coef, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
0129 boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_large_abz<%1%>(%1%,%1%,%1%)", max_iter, pol);
0130
0131
0132
0133
0134
0135
0136 T first = 1;
0137 T second = ((1 + crossover_a - b_local) / crossover_a) + ((b_local - 1) / crossover_a) / b_ratio;
0138
0139
0140
0141 boost::math::detail::hypergeometric_1F1_recurrence_a_coefficients<T> a_coef(crossover_a, b_local, x);
0142 long long backwards_scale = 0;
0143 T comparitor = boost::math::tools::apply_recurrence_relation_backward(a_coef, crossover_shift, second, first, &backwards_scale);
0144 log_scaling -= backwards_scale;
0145 if ((h < 1) && (tools::max_value<T>() * h > comparitor))
0146 {
0147
0148 long long scale = lltrunc(log(h), pol) + 1;
0149 h *= exp(T(-scale));
0150 log_scaling += scale;
0151 }
0152 comparitor /= h;
0153 first /= comparitor;
0154 second /= comparitor;
0155
0156
0157
0158 if (crossover_shift < a_shift)
0159 {
0160 boost::math::detail::hypergeometric_1F1_recurrence_a_coefficients<T> a_coef_2(crossover_a + 1, b_local, x);
0161 h = boost::math::tools::apply_recurrence_relation_forward(a_coef_2, a_shift - crossover_shift - 1, first, second, &log_scaling);
0162 }
0163 else
0164 h = first;
0165 }
0166 else
0167 {
0168
0169
0170
0171 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef(a_local, b_local, x);
0172 std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
0173 T b_ratio = boost::math::tools::function_ratio_from_backwards_recurrence(b_coef, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
0174 boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_large_abz<%1%>(%1%,%1%,%1%)", max_iter, pol);
0175
0176
0177
0178
0179
0180
0181 T second = ((1 + a_local - b_local) / a_local) * h + ((b_local - 1) / a_local) * h / b_ratio;
0182 boost::math::detail::hypergeometric_1F1_recurrence_a_coefficients<T> a_coef(a_local + 1, b_local, x);
0183 h = boost::math::tools::apply_recurrence_relation_forward(a_coef, --a_shift, h, second, &log_scaling);
0184 }
0185 }
0186 else
0187 {
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198 BOOST_MATH_ASSERT(2 * a - b_local + x > 0);
0199 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef(a, b_local, x);
0200 std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
0201 T b_ratio = boost::math::tools::function_ratio_from_backwards_recurrence(b_coef, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
0202 boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_large_abz<%1%>(%1%,%1%,%1%)", max_iter, pol);
0203
0204
0205
0206
0207
0208
0209 T first = 1;
0210 T second = ((1 + a - b_local) / a) + ((b_local - 1) / a) * (1 / b_ratio);
0211
0212 if (a_shift == -1)
0213 h = h / second;
0214 else
0215 {
0216 boost::math::detail::hypergeometric_1F1_recurrence_a_coefficients<T> a_coef(a + 1, b_local, x);
0217 T comparitor = boost::math::tools::apply_recurrence_relation_forward(a_coef, -(a_shift + 1), first, second);
0218 if (boost::math::tools::min_value<T>() * comparitor > h)
0219 {
0220
0221 long long rescale = lltrunc(log(fabs(h)));
0222 T scale = exp(T(-rescale));
0223 h *= scale;
0224 log_scaling += rescale;
0225 }
0226 h = h / comparitor;
0227 }
0228 }
0229 return h;
0230 }
0231
0232 template <class T, class Policy>
0233 T hypergeometric_1F1_shift_on_b(T h, const T& a, const T& b_local, const T& x, int b_shift, const Policy& pol, long long& log_scaling)
0234 {
0235 BOOST_MATH_STD_USING
0236
0237 T b = b_local + b_shift;
0238 if (b_shift == 0)
0239 return h;
0240 else if (b_shift > 0)
0241 {
0242
0243
0244
0245
0246 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef(a, b, x);
0247 std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
0248
0249 T first = 1;
0250 T second = 1 / boost::math::tools::function_ratio_from_backwards_recurrence(b_coef, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
0251 boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_large_abz<%1%>(%1%,%1%,%1%)", max_iter, pol);
0252 if (b_shift == 1)
0253 h = h / second;
0254 else
0255 {
0256
0257
0258
0259 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef_2(a, b - 1, x);
0260 long long local_scale = 0;
0261 T comparitor = boost::math::tools::apply_recurrence_relation_backward(b_coef_2, --b_shift, first, second, &local_scale);
0262 log_scaling -= local_scale;
0263 if (boost::math::tools::min_value<T>() * comparitor > h)
0264 {
0265
0266 long long rescale = lltrunc(log(fabs(h)));
0267 T scale = exp(T(-rescale));
0268 h *= scale;
0269 log_scaling += rescale;
0270 }
0271 h = h / comparitor;
0272 }
0273 }
0274 else
0275 {
0276 T second;
0277 if (a == b_local)
0278 {
0279
0280 second = -b_local * (1 - b_local - x) * h / (b_local * (b_local - 1));
0281 }
0282 else
0283 {
0284 BOOST_MATH_ASSERT(!is_negative_integer(b - a));
0285 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef(a, b_local, x);
0286 std::uintmax_t max_iter = boost::math::policies::get_max_series_iterations<Policy>();
0287 second = h / boost::math::tools::function_ratio_from_backwards_recurrence(b_coef, boost::math::policies::get_epsilon<T, Policy>(), max_iter);
0288 boost::math::policies::check_series_iterations<T>("boost::math::hypergeometric_1F1_large_abz<%1%>(%1%,%1%,%1%)", max_iter, pol);
0289 }
0290 if (b_shift == -1)
0291 h = second;
0292 else
0293 {
0294 boost::math::detail::hypergeometric_1F1_recurrence_b_coefficients<T> b_coef_2(a, b_local - 1, x);
0295 h = boost::math::tools::apply_recurrence_relation_backward(b_coef_2, -(++b_shift), h, second, &log_scaling);
0296 }
0297 }
0298 return h;
0299 }
0300
0301
0302 template <class T, class Policy>
0303 T hypergeometric_1F1_large_igamma(const T& a, const T& b, const T& x, const T& b_minus_a, const Policy& pol, long long& log_scaling)
0304 {
0305 BOOST_MATH_STD_USING
0306
0307
0308
0309
0310
0311 int b_shift = b * 2 < x ? 0 : itrunc(b - x / 2);
0312 int a_shift = a > b - b_shift ? -itrunc(b - b_shift - a - 1) : -itrunc(b - b_shift - a);
0313
0314 if (a_shift < 0)
0315 {
0316
0317 b_shift -= a_shift;
0318 a_shift = 0;
0319 }
0320
0321 T a_local = a - a_shift;
0322 T b_local = b - b_shift;
0323 T b_minus_a_local = (a_shift == 0) && (b_shift == 0) ? b_minus_a : b_local - a_local;
0324 long long local_scaling = 0;
0325 T h = hypergeometric_1F1_igamma(a_local, b_local, x, b_minus_a_local, pol, local_scaling);
0326 log_scaling += local_scaling;
0327
0328
0329
0330
0331 h = hypergeometric_1F1_shift_on_a(h, a_local, b_local, x, a_shift, pol, log_scaling);
0332 h = hypergeometric_1F1_shift_on_b(h, a, b_local, x, b_shift, pol, log_scaling);
0333
0334 return h;
0335 }
0336
0337 template <class T, class Policy>
0338 T hypergeometric_1F1_large_series(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scaling)
0339 {
0340 BOOST_MATH_STD_USING
0341
0342
0343
0344 int a_shift(0), b_shift(0);
0345 if (a * z > b)
0346 {
0347 a_shift = itrunc(a) - 5;
0348 b_shift = b < z ? itrunc(b - z - 1) : 0;
0349 }
0350
0351
0352
0353
0354 if (a_shift < 5)
0355 a_shift = 0;
0356 T a_local = a - a_shift;
0357 T b_local = b - b_shift;
0358 T h = boost::math::detail::hypergeometric_1F1_generic_series(a_local, b_local, z, pol, log_scaling, "hypergeometric_1F1_large_series<%1%>(a,b,z)");
0359
0360
0361
0362 if (a_shift && (a_local == 0))
0363 {
0364
0365
0366
0367
0368
0369 long long scale = 0;
0370 T h2 = boost::math::detail::hypergeometric_1F1_generic_series(T(a_local + 1), b_local, z, pol, scale, "hypergeometric_1F1_large_series<%1%>(a,b,z)");
0371 if (scale != log_scaling)
0372 {
0373 h2 *= exp(T(scale - log_scaling));
0374 }
0375 boost::math::detail::hypergeometric_1F1_recurrence_a_coefficients<T> coef(a_local + 1, b_local, z);
0376 h = boost::math::tools::apply_recurrence_relation_forward(coef, a_shift - 1, h, h2, &log_scaling);
0377 h = hypergeometric_1F1_shift_on_b(h, a, b_local, z, b_shift, pol, log_scaling);
0378 }
0379 else
0380 {
0381 h = hypergeometric_1F1_shift_on_a(h, a_local, b_local, z, a_shift, pol, log_scaling);
0382 h = hypergeometric_1F1_shift_on_b(h, a, b_local, z, b_shift, pol, log_scaling);
0383 }
0384 return h;
0385 }
0386
0387 template <class T, class Policy>
0388 T hypergeometric_1F1_large_13_3_6_series(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scaling)
0389 {
0390 BOOST_MATH_STD_USING
0391
0392
0393
0394
0395 int b_shift = itrunc(b - a);
0396 if ((b_shift < 0) && (b - b_shift != a))
0397 b_shift -= 1;
0398 T b_local = b - b_shift;
0399 if ((b_local - a - 0.5 <= 0) && (b_local != a))
0400 {
0401
0402 b_shift -= 1;
0403 b_local += 1;
0404 }
0405 T h = boost::math::detail::hypergeometric_1F1_AS_13_3_6(a, b_local, z, T(b_local - a), pol, log_scaling);
0406 return hypergeometric_1F1_shift_on_b(h, a, b_local, z, b_shift, pol, log_scaling);
0407 }
0408
0409 template <class T, class Policy>
0410 T hypergeometric_1F1_large_abz(const T& a, const T& b, const T& z, const Policy& pol, long long& log_scaling)
0411 {
0412 BOOST_MATH_STD_USING
0413
0414
0415
0416
0417
0418 enum method
0419 {
0420 method_series = 0,
0421 method_shifted_series,
0422 method_gamma,
0423 method_bessel
0424 };
0425
0426
0427
0428 T current_cost = (sqrt(16 * z * (3 * a + z) + 9 * b * b - 24 * b * z) - 3 * b + 4 * z) / 6;
0429 method current_method = method_series;
0430
0431
0432
0433
0434
0435
0436
0437
0438 T cost = a + ((b < z) ? T(z - b) : T(0));
0439 if((b > 1) && (cost < current_cost) && ((b > z) || !is_negative_integer(b-a)))
0440 {
0441 current_method = method_shifted_series;
0442 current_cost = cost;
0443 }
0444
0445
0446
0447
0448
0449
0450
0451 T b_shift = fabs(b * 2 < z ? T(0) : T(b - z / 2));
0452 T a_shift = fabs(a > b - b_shift ? T(-(b - b_shift - a - 1)) : T(-(b - b_shift - a)));
0453 cost = 1000 + b_shift + a_shift;
0454 if((b > 1) && (cost <= current_cost))
0455 {
0456 current_method = method_gamma;
0457 current_cost = cost;
0458 }
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469 cost = 50 + fabs(b - a);
0470 if((b > 1) && (cost <= current_cost) && (z < tools::log_max_value<T>()) && (z < 11356) && (b - a != 0.5f))
0471 {
0472 current_method = method_bessel;
0473 current_cost = cost;
0474 }
0475
0476 switch (current_method)
0477 {
0478 case method_series:
0479 return detail::hypergeometric_1F1_generic_series(a, b, z, pol, log_scaling, "hypergeometric_1f1_large_abz<%1%>(a,b,z)");
0480 case method_shifted_series:
0481 return detail::hypergeometric_1F1_large_series(a, b, z, pol, log_scaling);
0482 case method_gamma:
0483 return detail::hypergeometric_1F1_large_igamma(a, b, z, T(b - a), pol, log_scaling);
0484 case method_bessel:
0485 return detail::hypergeometric_1F1_large_13_3_6_series(a, b, z, pol, log_scaling);
0486 }
0487 return 0;
0488 }
0489
0490 } } }
0491
0492 #endif