StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
MathTools.cc
1 // MathTools.cc is a part of the PYTHIA event generator.
2 // Copyright (C) 2020 Torbjorn Sjostrand.
3 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
4 // Please respect the MCnet Guidelines, see GUIDELINES for details.
5 
6 // Function definitions (not found in the header) for some mathematical tools.
7 
8 #include "Pythia8/MathTools.h"
9 
10 namespace Pythia8 {
11 
12 //==========================================================================
13 
14 // The Gamma function for real arguments, using the Lanczos approximation.
15 // Code based on http://en.wikipedia.org/wiki/Lanczos_approximation
16 
17 double GammaCoef[9] = {
18  0.99999999999980993, 676.5203681218851, -1259.1392167224028,
19  771.32342877765313, -176.61502916214059, 12.507343278686905,
20  -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7};
21 
22 double GammaReal(double x) {
23 
24  // Reflection formula (recursive!) for x < 0.5.
25  if (x < 0.5) return M_PI / (sin(M_PI * x) * GammaReal(1 - x));
26 
27  // Iterate through terms.
28  double z = x - 1.;
29  double gamma = GammaCoef[0];
30  for (int i = 1; i < 9; ++i) gamma += GammaCoef[i] / (z + i);
31 
32  // Answer.
33  double t = z + 7.5;
34  gamma *= sqrt(2. * M_PI) * pow(t, z + 0.5) * exp(-t);
35  return gamma;
36 
37 }
38 
39 //--------------------------------------------------------------------------
40 
41 // Polynomial approximation for modified Bessel function of the first kind.
42 // Based on Abramowitz & Stegun, Handbook of mathematical functions (1964).
43 
44 double besselI0(double x){
45 
46  // Parametrize in terms of t.
47  double result = 0.;
48  double t = x / 3.75;
49  double t2 = pow2(t);
50 
51  // Only positive values relevant.
52  if ( t < 0.) ;
53  else if ( t < 1.) {
54  result = 1.0 + 3.5156229 * t2 + 3.0899424 * pow2(t2)
55  + 1.2067492 * pow3(t2) + 0.2659732 * pow4(t2)
56  + 0.0360768 * pow5(t2) + 0.0045813 * pow6(t2);
57  } else {
58  double u = 1. / t;
59  result = exp(x) / sqrt(x) * ( 0.39894228 + 0.01328592 * u
60  + 0.00225319 * pow2(u) - 0.00157565 * pow3(u)
61  + 0.00916281 * pow4(u) - 0.02057706 * pow5(u)
62  + 0.02635537 * pow6(u) - 0.01647633 * pow7(u)
63  + 0.00392377 * pow8(u) );
64  }
65 
66  return result;
67 }
68 
69 //--------------------------------------------------------------------------
70 
71 // Polynomial approximation for modified Bessel function of the first kind.
72 // Based on Abramowitz & Stegun, Handbook of mathematical functions (1964).
73 
74 double besselI1(double x){
75 
76  // Parametrize in terms of t.
77  double result = 0.;
78  double t = x / 3.75;
79  double t2 = pow2(t);
80 
81  // Only positive values relevant.
82  if ( t < 0.) ;
83  else if ( t < 1.) {
84  result = x * ( 0.5 + 0.87890594 * t2 + 0.51498869 * pow2(t2)
85  + 0.15084934 * pow3(t2) + 0.02658733 * pow4(t2)
86  + 0.00301532 * pow5(t2) + 0.00032411 * pow6(t2) );
87  } else {
88  double u = 1. / t;
89  result = exp(x) / sqrt(x) * ( 0.39894228 - 0.03988024 * u
90  - 0.00368018 * pow2(u) + 0.00163801 * pow3(u)
91  - 0.01031555 * pow4(u) + 0.02282967 * pow5(u)
92  - 0.02895312 * pow6(u) + 0.01787654 * pow7(u)
93  - 0.00420059 * pow8(u) );
94  }
95 
96  return result;
97 }
98 
99 //--------------------------------------------------------------------------
100 
101 // Polynomial approximation for modified Bessel function of a second kind.
102 // Based on Abramowitz & Stegun, Handbook of mathematical functions (1964).
103 
104 double besselK0(double x){
105 
106  double result = 0.;
107 
108  // Polynomial approximation valid ony for x > 0.
109  if ( x < 0.) ;
110  else if ( x < 2.) {
111  double x2 = pow2(0.5 * x);
112  result = -log(0.5 * x) * besselI0(x) - 0.57721566
113  + 0.42278420 * x2 + 0.23069756 * pow2(x2)
114  + 0.03488590 * pow3(x2) + 0.00262698 * pow4(x2)
115  + 0.00010750 * pow5(x2) + 0.00000740 * pow6(x2);
116  } else {
117  double z = 2. / x;
118  result = exp(-x) / sqrt(x) * ( 1.25331414 - 0.07832358 * z
119  + 0.02189568 * pow2(z) - 0.01062446 * pow3(z)
120  + 0.00587872 * pow4(z) - 0.00251540 * pow5(z)
121  + 0.00053208 * pow6(z) );
122  }
123 
124  return result;
125 }
126 
127 //--------------------------------------------------------------------------
128 
129 // Polynomial approximation for modified Bessel function of a second kind.
130 // Based on Abramowitz & Stegun, Handbook of mathematical functions (1964).
131 
132 double besselK1(double x){
133 
134  double result = 0.;
135 
136  // Polynomial approximation valid ony for x > 0.
137  if ( x < 0.) ;
138  else if ( x < 2.) {
139  double x2 = pow2(0.5 * x);
140  result = log(0.5 * x) * besselI1(x) + 1./x * ( 1. + 0.15443144 * x2
141  - 0.67278579 * pow2(x2) - 0.18156897 * pow3(x2)
142  - 0.01919402 * pow4(x2) - 0.00110404 * pow5(x2)
143  - 0.00004686 * pow6(x2) );
144  } else {
145  double z = 2. / x;
146  result = exp(-x) / sqrt(x) * ( 1.25331414 + 0.23498619 * z
147  - 0.03655620 * pow2(z) + 0.01504268 * pow3(z)
148  - 0.00780353 * pow4(z) + 0.00325614 * pow5(z)
149  - 0.00068245 * pow6(z) );
150  }
151 
152  return result;
153 }
154 
155 //==========================================================================
156 
157 // Integrate f over the specified range.
158 // Note that f must be a function of one variable. In order to integrate one
159 // variable of function with multiple arguments, like integrating f(x, y) with
160 // respect to x when y is fixed, the other variables can be captured using a
161 // lambda function.
162 
163 bool integrateGauss(double& resultOut, function<double(double)> f,
164  double xLo, double xHi, double tol) {
165 
166  // Boundary check: return zero if xLo >= xHi.
167  if (xLo >= xHi) {
168  resultOut = 0.0;
169  return true;
170  }
171 
172  // Initialize temporary result.
173  double result = 0.0;
174 
175  // 8-point unweighted.
176  static double x8[4]={ 0.96028985649753623, 0.79666647741362674,
177  0.52553240991632899, 0.18343464249564980};
178  static double w8[4]={ 0.10122853629037626, 0.22238103445337447,
179  0.31370664587788729, 0.36268378337836198};
180  // 16-point unweighted.
181  static double x16[8]={ 0.98940093499164993, 0.94457502307323258,
182  0.86563120238783174, 0.75540440835500303, 0.61787624440264375,
183  0.45801677765722739, 0.28160355077925891, 0.09501250983763744};
184  static double w16[8]={ 0.027152459411754095, 0.062253523938647893,
185  0.095158511682492785, 0.12462897125553387, 0.14959598881657673,
186  0.16915651939500254, 0.18260341504492359, 0.18945061045506850};
187 
188  // Set up integration region.
189  double c = 0.001/abs(xHi-xLo);
190  double zLo = xLo;
191  double zHi = xHi;
192 
193  bool nextbin = true;
194  while ( nextbin ) {
195 
196  double zMid = 0.5*(zHi+zLo); // midpoint
197  double zDel = 0.5*(zHi-zLo); // midpoint, relative to zLo
198 
199  // Calculate 8-point and 16-point quadratures.
200  double s8=0.0;
201  for (int i=0;i<4;i++) {
202  double dz = zDel * x8[i];
203  double f1 = f(zMid+dz);
204  double f2 = f(zMid-dz);
205  s8 += w8[i]*(f1 + f2);
206  }
207  s8 *= zDel;
208  double s16=0.0;
209  for (int i=0;i<8;i++) {
210  double dz = zDel * x16[i];
211  double f1 = f(zMid+dz);
212  double f2 = f(zMid-dz);
213  s16 += w16[i]*(f1 + f2);
214  }
215  s16 *= zDel;
216 
217  // Precision in this bin OK, add to cumulative and go to next.
218  if (abs(s16-s8) < tol*(1+abs(s16))) {
219  nextbin=true;
220  result += s16;
221  // Next bin: LO = end of current, HI = end of integration region.
222  zLo=zHi;
223  zHi=xHi;
224  if ( zLo == zHi ) nextbin = false;
225 
226  // Precision in this bin not OK, subdivide.
227  } else {
228  if (1.0 + c*abs(zDel) == 1.0) {
229  // Cannot subdivide further at double precision. Fail.
230  result = 0.0 ;
231  return false;
232  }
233  zHi = zMid;
234  nextbin = true;
235  }
236  }
237 
238  // Write result and return success.
239  resultOut = result;
240  return true;
241 }
242 
243 //==========================================================================
244 
245 // Solve f(x) = target for x in the specified range. Note that f must
246 // be a function of one variable. In order to solve an equation with a
247 // multivariable function, like solving f(x, y) = target for x when y
248 // is fixed, the other variables can be captured using a lambda
249 // function.
250 
251 bool brent(double& solutionOut, function<double(double)> f, double target,
252  double xLo, double xHi, double tol, int maxIter) {
253 
254  // Range checks.
255  if (xLo > xHi) return false;
256 
257  // Evaluate function - targetValue at lower boundary.
258  double f1 = f(xLo) - target;
259  if (abs(f1) < tol) {
260  solutionOut = xLo;
261  return true;
262  }
263  // Evaluate function - targetValue at upper boundary.
264  double f2 = f(xHi) - target;
265  if (abs(f2) < tol) {
266  solutionOut = xHi;
267  return true;
268  }
269 
270  // Check if root is bracketed.
271  if ( f1 * f2 > 0.0) return false;
272 
273  // Start searching for root.
274  double x1 = xLo;
275  double x2 = xHi;
276  double x3 = 0.5 * (xLo + xHi);
277 
278  int iter=0;
279  while(++iter < maxIter) {
280  // Now check at x = x3.
281  double f3 = f(x3) - target;
282  // Check if tolerance on f has been reached.
283  if (abs(f3) < tol) {
284  solutionOut = x3;
285  return true;
286  }
287  // Is root bracketed in lower or upper half?
288  if (f1 * f3 < 0.0) xHi = x3;
289  else xLo = x3;
290  // Check if tolerance on x has been reached.
291  if ((xHi - xLo) < tol * (abs(xHi) < 1.0 ? xHi : 1.0)) {
292  solutionOut = 0.5 * (xLo + xHi);
293  return true;
294  }
295 
296  // Work out next step to take in x.
297  double den = (f2 - f1) * (f3 - f1) * (f2 - f3);
298  double num = x3 * (f1 - f2) * (f2 - f3 + f1) + f2 * x1 * (f2 - f3)
299  + f1 * x2 * (f3 - f1);
300  double dx = xHi - xLo;
301  if (den != 0.0) dx = f3 * num / den;
302 
303  // First attempt, using gradient
304  double x = x3 + dx;
305 
306  // If this was too far, just step to the middle
307  if ((xHi - x) * (x - xLo) < 0.0) {
308  dx = 0.5 * (xHi - xLo);
309  x = xLo + dx;
310  }
311  if (x < x3) {
312  x2 = x3;
313  f2 = f3;
314  }
315  else {
316  x1 = x3;
317  f1 = f3;
318  }
319  x3 = x;
320  }
321 
322  // Maximum number of iterations exceeded.
323  return false;
324 }
325 
326 //==========================================================================
327 
328 // LinearInterpolator class.
329 // Used to interpolate between values in linearly spaced data.
330 
331 //--------------------------------------------------------------------------
332 
333 // Operator to get interpolated value at the specified point.
334 
335 double LinearInterpolator::operator()(double xIn) const {
336 
337  if (xIn == rightSave)
338  return ysSave.back();
339 
340  // Select interpolation bin.
341  double t = (xIn - leftSave) / (rightSave - leftSave);
342  int lastIdx = ysSave.size() - 1;
343  int j = (int)floor(t * lastIdx);
344 
345  // Return zero outside of interpolation range.
346  if (j < 0 || j >= lastIdx) return 0.;
347  // Select position in bin and return linear interpolation.
348  else {
349  double s = (xIn - (leftSave + j * dx())) / dx();
350  return (1 - s) * ysSave[j] + s * ysSave[j + 1];
351  }
352 }
353 
354 //--------------------------------------------------------------------------
355 
356 // Plot the data points of this LinearInterpolator in a histogram.
357 
358 Hist LinearInterpolator::plot(string title) const {
359  return plot(title, leftSave, rightSave);
360 }
361 
362 Hist LinearInterpolator::plot(string title, double xMin, double xMax) const {
363 
364  int nBins = ceil((xMax - xMin) / (rightSave - leftSave) * ysSave.size());
365 
366  Hist result(title, nBins, xMin, xMax, false);
367  double dxNow = (xMax - xMin) / nBins;
368 
369  for (int i = 0; i < nBins; ++i) {
370  double x = xMin + dxNow * (0.5 + i);
371  result.fill(x, operator()(x));
372  }
373 
374  return result;
375 }
376 
377 //==========================================================================
378 
379 } // end namespace Pythia8