1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
|
#include <cmath>
#include <cstdint>
#include <functional>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/cbrt.hpp>
#include <boost/math/special_functions/factorials.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/tools/roots.hpp>
#include <boost/noncopyable.hpp>
#define CPP_BIN_FLOAT 1
#define CPP_DEC_FLOAT 2
#define CPP_MPFR_FLOAT 3
//#define MP_TYPE CPP_BIN_FLOAT
#define MP_TYPE CPP_DEC_FLOAT
//#define MP_TYPE CPP_MPFR_FLOAT
namespace
{
struct digits_characteristics
{
static const int digits10 = 300;
static const int guard_digits = 6;
};
}
#if (MP_TYPE == CPP_BIN_FLOAT)
#include <boost/multiprecision/cpp_bin_float.hpp>
namespace mp = boost::multiprecision;
typedef mp::number<mp::cpp_bin_float<digits_characteristics::digits10 + digits_characteristics::guard_digits>, mp::et_off> mp_type;
#elif (MP_TYPE == CPP_DEC_FLOAT)
#include <boost/multiprecision/cpp_dec_float.hpp>
namespace mp = boost::multiprecision;
typedef mp::number<mp::cpp_dec_float<digits_characteristics::digits10 + digits_characteristics::guard_digits>, mp::et_off> mp_type;
#elif (MP_TYPE == CPP_MPFR_FLOAT)
#include <boost/multiprecision/mpfr.hpp>
namespace mp = boost::multiprecision;
typedef mp::number<mp::mpfr_float_backend<digits_characteristics::digits10 + digits_characteristics::guard_digits>, mp::et_off> mp_type;
#else
#error MP_TYPE is undefined
#endif
template<typename T>
class laguerre_function_object
{
public:
laguerre_function_object(const int n, const T a) : order(n),
alpha(a),
p1 (0),
d2 (0) { }
laguerre_function_object(const laguerre_function_object& other) : order(other.order),
alpha(other.alpha),
p1 (other.p1),
d2 (other.d2) { }
~laguerre_function_object() { }
T operator()(const T& x) const
{
// Calculate (via forward recursion):
// * the value of the Laguerre function L(n, alpha, x), called (p2),
// * the value of the derivative of the Laguerre function (d2),
// * and the value of the corresponding Laguerre function of
// previous order (p1).
// Return the value of the function (p2) in order to be used as a
// function object with Boost.Math root-finding. Store the values
// of the Laguerre function derivative (d2) and the Laguerre function
// of previous order (p1) in class members for later use.
p1 = T(0);
T p2 = T(1);
d2 = T(0);
T j_plus_alpha(alpha);
T two_j_plus_one_plus_alpha_minus_x(1 + alpha - x);
int j;
const T my_two(2);
for(j = 0; j < order; ++j)
{
const T p0(p1);
// Set the value of the previous Laguerre function.
p1 = p2;
// Use a recurrence relation to compute the value of the Laguerre function.
p2 = ((two_j_plus_one_plus_alpha_minus_x * p1) - (j_plus_alpha * p0)) / (j + 1);
++j_plus_alpha;
two_j_plus_one_plus_alpha_minus_x += my_two;
}
// Set the value of the derivative of the Laguerre function.
d2 = ((p2 * j) - (j_plus_alpha * p1)) / x;
// Return the value of the Laguerre function.
return p2;
}
const T& previous () const { return p1; }
const T& derivative() const { return d2; }
static bool root_tolerance(const T& a, const T& b)
{
using std::abs;
// The relative tolerance here is: ((a - b) * 2) / (a + b).
return (abs((a - b) * 2) < ((a + b) * boost::math::tools::epsilon<T>()));
}
private:
const int order;
const T alpha;
mutable T p1;
mutable T d2;
laguerre_function_object();
const laguerre_function_object& operator=(const laguerre_function_object&);
};
template<typename T>
class guass_laguerre_abscissas_and_weights : private boost::noncopyable
{
public:
guass_laguerre_abscissas_and_weights(const int n, const T a) : order(n),
alpha(a),
valid(true),
xi (),
wi ()
{
if(alpha < -20.0F)
{
// TBD: If we ever boostify this, throw a range error here.
// If so, then also document it in the docs.
std::cout << "Range error: the order of the Laguerre function must exceed -20.0." << std::endl;
}
else
{
calculate();
}
}
virtual ~guass_laguerre_abscissas_and_weights() { }
const std::vector<T>& abscissas() const { return xi; }
const std::vector<T>& weights () const { return wi; }
bool get_valid() const { return valid; }
private:
const int order;
const T alpha;
bool valid;
std::vector<T> xi;
std::vector<T> wi;
void calculate()
{
using std::abs;
std::cout << "finding approximate roots..." << std::endl;
std::vector<boost::math::tuple<T, T> > root_estimates;
root_estimates.reserve(static_cast<typename std::vector<boost::math::tuple<T, T> >::size_type>(order));
const laguerre_function_object<T> laguerre_object(order, alpha);
// Set the initial values of the step size and the running step
// to be used for finding the estimate of the first root.
T step_size = 0.01F;
T step = step_size;
T first_laguerre_root = 0.0F;
bool first_laguerre_root_has_been_found = true;
if(alpha < -1.0F)
{
// Iteratively step through the Laguerre function using a
// small step-size in order to find a rough estimate of
// the first zero.
bool this_laguerre_value_is_negative = (laguerre_object(mp_type(0)) < 0);
static const int j_max = 10000;
int j;
for(j = 0; (j < j_max) && (this_laguerre_value_is_negative != (laguerre_object(step) < 0)); ++j)
{
// Increment the step size until the sign of the Laguerre function
// switches. This indicates a zero-crossing, signalling the next root.
step += step_size;
}
if(j >= j_max)
{
first_laguerre_root_has_been_found = false;
}
else
{
// We have found the first zero-crossing. Put a loose bracket around
// the root using a window. Here, we know that the first root lies
// between (x - step_size) < root < x.
// Before storing the approximate root, perform a couple of
// bisection steps in order to tighten up the root bracket.
boost::uintmax_t a_couple_of_iterations = 3U;
const std::pair<T, T>
first_laguerre_root = boost::math::tools::bisect(laguerre_object,
step - step_size,
step,
laguerre_function_object<T>::root_tolerance,
a_couple_of_iterations);
static_cast<void>(a_couple_of_iterations);
}
}
else
{
// Calculate an estimate of the 1st root of a generalized Laguerre
// function using either a Taylor series or an expansion in Bessel
// function zeros. The Bessel function zeros expansion is from Tricomi.
// Here, we obtain an estimate of the first zero of J_alpha(x).
T j_alpha_m1;
if(alpha < 1.4F)
{
// For small alpha, use a short series obtained from Mathematica(R).
// Series[BesselJZero[v, 1], {v, 0, 3}]
// N[%, 12]
j_alpha_m1 = ((( 0.09748661784476F
* alpha - 0.17549359276115F)
* alpha + 1.54288974259931F)
* alpha + 2.40482555769577F);
}
else
{
// For larger alpha, use the first line of Eqs. 10.21.40 in the NIST Handbook.
const T alpha_pow_third(boost::math::cbrt(alpha));
const T alpha_pow_minus_two_thirds(T(1) / (alpha_pow_third * alpha_pow_third));
j_alpha_m1 = alpha * ((((( + 0.043F
* alpha_pow_minus_two_thirds - 0.0908F)
* alpha_pow_minus_two_thirds - 0.00397F)
* alpha_pow_minus_two_thirds + 1.033150F)
* alpha_pow_minus_two_thirds + 1.8557571F)
* alpha_pow_minus_two_thirds + 1.0F);
}
const T vf = ((order * 4.0F) + (alpha * 2.0F) + 2.0F);
const T vf2 = vf * vf;
const T j_alpha_m1_sqr = j_alpha_m1 * j_alpha_m1;
first_laguerre_root = (j_alpha_m1_sqr * (-0.6666666666667F + ((0.6666666666667F * alpha) * alpha) + (0.3333333333333F * j_alpha_m1_sqr) + vf2)) / (vf2 * vf);
}
if(first_laguerre_root_has_been_found)
{
bool this_laguerre_value_is_negative = (laguerre_object(mp_type(0)) < 0);
// Re-set the initial value of the step-size based on the
// estimate of the first root.
step_size = first_laguerre_root / 2;
step = step_size;
// Step through the Laguerre function using a step-size
// of dynamic width in order to find the zero crossings
// of the Laguerre function, providing rough estimates
// of the roots. Refine the brackets with a few bisection
// steps, and store the results as bracketed root estimates.
while(static_cast<int>(root_estimates.size()) < order)
{
// Increment the step size until the sign of the Laguerre function
// switches. This indicates a zero-crossing, signalling the next root.
step += step_size;
if(this_laguerre_value_is_negative != (laguerre_object(step) < 0))
{
// We have found the next zero-crossing.
// Change the running sign of the Laguerre function.
this_laguerre_value_is_negative = (!this_laguerre_value_is_negative);
// We have found the first zero-crossing. Put a loose bracket around
// the root using a window. Here, we know that the first root lies
// between (x - step_size) < root < x.
// Before storing the approximate root, perform a couple of
// bisection steps in order to tighten up the root bracket.
boost::uintmax_t a_couple_of_iterations = 3U;
const std::pair<T, T>
root_estimate_bracket = boost::math::tools::bisect(laguerre_object,
step - step_size,
step,
laguerre_function_object<T>::root_tolerance,
a_couple_of_iterations);
static_cast<void>(a_couple_of_iterations);
// Store the refined root estimate as a bracketed range in a tuple.
root_estimates.push_back(boost::math::tuple<T, T>(root_estimate_bracket.first,
root_estimate_bracket.second));
if(root_estimates.size() >= static_cast<std::size_t>(2U))
{
// Determine the next step size. This is based on the distance between
// the previous two roots, whereby the estimates of the previous roots
// are computed by taking the average of the lower and upper range of
// the root-estimate bracket.
const T r0 = ( boost::math::get<0>(*(root_estimates.rbegin() + 1U))
+ boost::math::get<1>(*(root_estimates.rbegin() + 1U))) / 2;
const T r1 = ( boost::math::get<0>(*root_estimates.rbegin())
+ boost::math::get<1>(*root_estimates.rbegin())) / 2;
const T distance_between_previous_roots = r1 - r0;
step_size = distance_between_previous_roots / 3;
}
}
}
const T norm_g =
((alpha == 0) ? T(-1)
: -boost::math::tgamma(alpha + order) / boost::math::factorial<T>(order - 1));
xi.reserve(root_estimates.size());
wi.reserve(root_estimates.size());
// Calculate the abscissas and weights to full precision.
for(std::size_t i = static_cast<std::size_t>(0U); i < root_estimates.size(); ++i)
{
std::cout << "calculating abscissa and weight for index: " << i << std::endl;
// Calculate the abscissas using iterative root-finding.
// Select the maximum allowed iterations, being at least 20.
// The determination of the maximum allowed iterations is
// based on the number of decimal digits in the numerical
// type T.
const int my_digits10 = static_cast<int>(static_cast<float>(boost::math::tools::digits<T>()) * 0.301F);
const boost::uintmax_t number_of_iterations_allowed = (std::max)(20, my_digits10 / 2);
boost::uintmax_t number_of_iterations_used = number_of_iterations_allowed;
// Perform the root-finding using ACM TOMS 748 from Boost.Math.
const std::pair<T, T>
laguerre_root_bracket = boost::math::tools::toms748_solve(laguerre_object,
boost::math::get<0>(root_estimates[i]),
boost::math::get<1>(root_estimates[i]),
laguerre_function_object<T>::root_tolerance,
number_of_iterations_used);
// Based on the result of *each* root-finding operation, re-assess
// the validity of the Guass-Laguerre abscissas and weights object.
valid &= (number_of_iterations_used < number_of_iterations_allowed);
// Compute the Laguerre root as the average of the values from
// the solved root bracket.
const T laguerre_root = ( laguerre_root_bracket.first
+ laguerre_root_bracket.second) / 2;
// Calculate the weight for this Laguerre root. Here, we calculate
// the derivative of the Laguerre function and the value of the
// previous Laguerre function on the x-axis at the value of this
// Laguerre root.
static_cast<void>(laguerre_object(laguerre_root));
// Store the abscissa and weight for this index.
xi.push_back(laguerre_root);
wi.push_back(norm_g / ((laguerre_object.derivative() * order) * laguerre_object.previous()));
}
}
}
};
namespace
{
template<typename T>
struct gauss_laguerre_ai
{
public:
gauss_laguerre_ai(const T X) : x(X)
{
using std::exp;
using std::sqrt;
zeta = ((sqrt(x) * x) * 2) / 3;
const T zeta_times_48_pow_sixth = sqrt(boost::math::cbrt(zeta * 48));
factor = 1 / ((sqrt(boost::math::constants::pi<T>()) * zeta_times_48_pow_sixth) * (exp(zeta) * gamma_of_five_sixths()));
}
gauss_laguerre_ai(const gauss_laguerre_ai& other) : x (other.x),
zeta (other.zeta),
factor(other.factor) { }
T operator()(const T& t) const
{
using std::sqrt;
return factor / sqrt(boost::math::cbrt(2 + (t / zeta)));
}
private:
const T x;
T zeta;
T factor;
static const T& gamma_of_five_sixths()
{
static const T value = boost::math::tgamma(T(5) / 6);
return value;
}
const gauss_laguerre_ai& operator=(const gauss_laguerre_ai&);
};
template<typename T>
T gauss_laguerre_airy_ai(const T x)
{
static const float digits_factor = static_cast<float>(std::numeric_limits<mp_type>::digits10) / 300.0F;
static const int laguerre_order = static_cast<int>(600.0F * digits_factor);
static const guass_laguerre_abscissas_and_weights<T> abscissas_and_weights(laguerre_order, -T(1) / 6);
T airy_ai_result;
if(abscissas_and_weights.get_valid())
{
const gauss_laguerre_ai<T> this_gauss_laguerre_ai(x);
airy_ai_result =
std::inner_product(abscissas_and_weights.abscissas().begin(),
abscissas_and_weights.abscissas().end(),
abscissas_and_weights.weights().begin(),
T(0),
std::plus<T>(),
[&this_gauss_laguerre_ai](const T& this_abscissa, const T& this_weight) -> T
{
return this_gauss_laguerre_ai(this_abscissa) * this_weight;
});
}
else
{
// TBD: Consider an error message.
airy_ai_result = T(0);
}
return airy_ai_result;
}
}
int main()
{
// Use Gauss-Laguerre integration to compute airy_ai(120 / 7).
// 9 digits
// 3.89904210e-22
// 10 digits
// 3.899042098e-22
// 50 digits.
// 3.8990420982303275013276114626640705170145070824318e-22
// 100 digits.
// 3.899042098230327501327611462664070517014507082431797677146153303523108862015228
// 864136051942933142648e-22
// 200 digits.
// 3.899042098230327501327611462664070517014507082431797677146153303523108862015228
// 86413605194293314264788265460938200890998546786740097437064263800719644346113699
// 77010905030516409847054404055843899790277e-22
// 300 digits.
// 3.899042098230327501327611462664070517014507082431797677146153303523108862015228
// 86413605194293314264788265460938200890998546786740097437064263800719644346113699
// 77010905030516409847054404055843899790277083960877617919088116211775232728792242
// 9346416823281460245814808276654088201413901972239996130752528e-22
// 500 digits.
// 3.899042098230327501327611462664070517014507082431797677146153303523108862015228
// 86413605194293314264788265460938200890998546786740097437064263800719644346113699
// 77010905030516409847054404055843899790277083960877617919088116211775232728792242
// 93464168232814602458148082766540882014139019722399961307525276722937464859521685
// 42826483602153339361960948844649799257455597165900957281659632186012043089610827
// 78871305322190941528281744734605934497977375094921646511687434038062987482900167
// 45127557400365419545e-22
// Mathematica(R) or Wolfram's Alpha:
// N[AiryAi[120 / 7], 300]
std::cout << std::setprecision(digits_characteristics::digits10)
<< gauss_laguerre_airy_ai(mp_type(120) / 7)
<< std::endl;
}
|