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