2 #include <gsl/gsl_sf_gamma.h>
3 #include <gsl/gsl_sf_expint.h>
4 #include <gsl/gsl_sf_exp.h>
5 #include <gsl/gsl_sf_result.h>
6 #include <gsl/gsl_errno.h>
7 #include <gsl/gsl_machine.h> // Maybe I should rather use DBL_EPSILON instead of GSL_DBL_EPSILON.
11 //#include <gsl/gsl_integration.h>
12 #include <gsl/gsl_errno.h>
16 #include "tiny_inlines.h"
18 // Some magic constants
20 #ifndef COMPLEXPART_REL_ZERO_LIMIT
21 #define COMPLEXPART_REL_ZERO_LIMIT 1e-14
24 #ifndef DELTA_RECURRENT_EXPOVERFLOW_LIMIT
25 #define DELTA_RECURRENT_EXPOVERFLOW_LIMIT 10.
28 gsl_error_handler_t IgnoreUnderflowsGSLErrorHandler
;
30 void IgnoreUnderflowsGSLErrorHandler (const char * reason
,
33 const int gsl_errno
) {
34 if (gsl_errno
== GSL_EUNDRFLW
)
37 gsl_stream_printf ("ERROR", file
, line
, reason
);
40 fprintf (stderr
, "Underflow-ignoring error handler invoked.\n");
46 // DLMF 8.7.3 (latter expression) for complex second argument
47 // BTW if a is large negative, it might take a while to evaluate.
48 // This can't be used for non-positive integer a due to
49 // Г(a) in the formula.
50 int cx_gamma_inc_series_e(const double a
, const complex double z
, qpms_csf_result
* result
) {
51 if (a
<= 0 && a
== (int) a
) {
52 result
->val
= NAN
+ NAN
*I
;
54 GSL_ERROR("Undefined for non-positive integer values", GSL_EDOM
);
56 gsl_sf_result fullgamma
;
57 int retval
= gsl_sf_gamma_e(a
, &fullgamma
);
58 if (GSL_EUNDRFLW
== retval
)
59 result
->err
+= DBL_MIN
;
60 else if (GSL_SUCCESS
!= retval
){
61 result
->val
= NAN
+ NAN
*I
; result
->err
= NAN
;
65 complex double sumprefac
= cpow(z
, a
) * cexp(-z
);
66 double sumprefac_abs
= cabs(sumprefac
);
67 complex double sum
, sumc
; ckahaninit(&sum
, &sumc
);
68 double err
, errc
; kahaninit(&err
, &errc
);
70 bool breakswitch
= false;
71 for (int k
= 0; (!breakswitch
) && (a
+ k
+ 1 <= GSL_SF_GAMMA_XMAX
); ++k
) {
72 gsl_sf_result fullgamma_ak
;
73 if (GSL_SUCCESS
!= (retval
= gsl_sf_gamma_e(a
+k
+1, &fullgamma_ak
))) {
74 result
->val
= NAN
+ NAN
*I
; result
->err
= NAN
;
77 complex double summand
= - cpow(z
, k
) / fullgamma_ak
.val
; // TODO test branch selection here with cimag(z) = -0.0
78 ckahanadd(&sum
, &sumc
, summand
);
79 double summanderr
= fabs(fullgamma_ak
.err
* cabs(summand
/ fullgamma_ak
.val
));
80 // TODO add also the rounding error
81 kahanadd(&err
, &errc
, summanderr
);
82 // TODO put some smarter cutoff break here?
83 if (a
+ k
>= 18 && (cabs(summand
) < err
|| cabs(summand
) < DBL_EPSILON
))
86 sum
*= sumprefac
; // Not sure if not breaking the Kahan summation here
89 errc
*= sumprefac_abs
;
90 ckahanadd(&sum
, &sumc
, 1.);
91 kahanadd(&err
, &errc
, DBL_EPSILON
);
92 result
->err
= cabs(sum
) * fullgamma
.err
+ err
* fabs(fullgamma
.val
);
93 result
->val
= sum
* fullgamma
.val
; // + sumc*fullgamma.val???
96 else GSL_ERROR("Overflow; the absolute value of the z argument is probably too large.", GSL_ETOL
); // maybe different error code...
99 /* Continued fraction which occurs in evaluation
100 * of Q(a,z) or Gamma(a,z).
101 * Borrowed from GSL and adapted for complex z.
103 * 1 (1-a)/z 1/z (2-a)/z 2/z (3-a)/z
104 * F(a,z) = ---- ------- ----- -------- ----- -------- ...
105 * 1 + 1 + 1 + 1 + 1 + 1 +
109 cx_gamma_inc_F_CF(const double a
, const complex double z
, qpms_csf_result
* result
)
111 const int nmax
= 5000;
112 const double small
= DBL_EPSILON
* DBL_EPSILON
* DBL_EPSILON
;
114 complex double hn
= 1.0; /* convergent */
115 complex double Cn
= 1.0 / small
;
116 complex double Dn
= 1.0;
119 /* n == 1 has a_1, b_1, b_0 independent of a,z,
120 so that has been done by hand */
121 for ( n
= 2 ; n
< nmax
; n
++ )
124 complex double delta
;
132 if ( cabs(Dn
) < small
)
135 if ( cabs(Cn
) < small
)
140 if(cabs(delta
-1.0) < DBL_EPSILON
) break;
144 result
->err
= 2.0*GSL_DBL_EPSILON
* cabs(hn
);
145 result
->err
+= GSL_DBL_EPSILON
* (2.0 + 0.5*n
) * cabs(result
->val
);
148 GSL_ERROR ("error in CF for F(a,x)", GSL_EMAXITER
);
153 // Incomplete gamma fuction with complex second argument as continued fraction.
154 int cx_gamma_inc_CF_e(const double a
, const complex double z
, qpms_csf_result
*result
)
158 const complex double am1lgz
= (a
-1.0)*clog(z
); // TODO check branches
159 const int stat_F
= cx_gamma_inc_F_CF(a
, z
, &F
);
160 const int stat_E
= gsl_sf_exp_err_e(creal(am1lgz
- z
), GSL_DBL_EPSILON
*cabs(am1lgz
), &pre
);
161 complex double cpre
= pre
.val
* cexp(I
*cimag(am1lgz
- z
));// TODO add the error estimate for this
162 //complex double cpre = cpow(z, a-1) * cexp(-z);
165 result
->val
= F
.val
* cpre
;
166 result
->err
= fabs(F
.err
* pre
.val
) + fabs(F
.val
* pre
.err
);
167 result
->err
+= 2.0 * GSL_DBL_EPSILON
* fabs(result
->val
);
169 return GSL_ERROR_SELECT_2(stat_F
, stat_E
);
173 // Incomplete gamma function for complex second argument.
174 int complex_gamma_inc_e(double a
, complex double x
, int m
, qpms_csf_result
*result
) {
177 (0 == fabs(cimag(x
)) || // x is real positive; just use the real fun
178 fabs(cimag(x
)) < fabs(creal(x
)) * COMPLEXPART_REL_ZERO_LIMIT
)) {
179 gsl_sf_result real_gamma_inc_result
;
180 retval
= gsl_sf_gamma_inc_e(a
, creal(x
), &real_gamma_inc_result
);
181 result
->val
= real_gamma_inc_result
.val
;
182 result
->err
= real_gamma_inc_result
.err
;
183 } else if (creal(x
) >= 0 && cabs(x
) > 0.5)
184 retval
= cx_gamma_inc_CF_e(a
, x
, result
);
185 else if (QPMS_LIKELY(a
> 0 || fmod(a
, 1.0)))
186 retval
= cx_gamma_inc_series_e(a
, x
, result
);
188 /* FIXME cx_gamma_inc_series_e() probably fails for non-positive integer a.
189 * This does not matter for 2D lattices in 3D space,
190 * but it might cause problems in the other cases.
192 QPMS_NOT_IMPLEMENTED("Incomplete Gamma function with non-positive integer a.");
193 if (m
) { // Non-principal branch.
194 /* This might be sub-optimal, as Γ(a) has probably been already evaluated
195 * somewhere in the functions called above. */
196 gsl_sf_result fullgamma
;
197 int retval_fg
= gsl_sf_gamma_e(a
, &fullgamma
);
198 if (GSL_EUNDRFLW
== retval_fg
)
199 fullgamma
.err
+= DBL_MIN
;
200 else if (GSL_SUCCESS
!= retval_fg
){
201 result
->val
= NAN
+ NAN
*I
; result
->err
= NAN
;
202 return GSL_ERROR_SELECT_2(retval_fg
, retval
);
204 complex double f
= cexp(2*m
*M_PI
*a
*I
);
207 result
->err
+= cabs(f
) * fullgamma
.err
;
208 result
->val
+= f
* fullgamma
.val
;
213 // Exponential integral for complex argument; !UNTESTED! and probably not needed, as I expressed everything in terms of inc. gammas anyways.
214 int complex_expint_n_e(int n
, complex double x
, qpms_csf_result
*result
) {
216 (0 == fabs(cimag(x
)) || // x is real positive; just use the real fun
217 fabs(cimag(x
)) < fabs(creal(x
)) * COMPLEXPART_REL_ZERO_LIMIT
)) {
218 gsl_sf_result real_expint_result
;
219 int retval
= gsl_sf_expint_En_e(n
, creal(x
), &real_expint_result
);
220 result
->val
= real_expint_result
.val
;
221 result
->err
= real_expint_result
.err
;
224 int retval
= complex_gamma_inc_e(-n
+1, x
, 0, result
);
225 complex double f
= cpow(x
, 2*n
-2);
227 result
->err
*= cabs(f
);
232 // inspired by GSL's hyperg_2F1_series
233 int hyperg_2F2_series(const double a
, const double b
, const double c
, const double d
,
234 const double x
, gsl_sf_result
*result
237 double sum_pos
= 1.0;
238 double sum_neg
= 0.0;
239 double del_pos
= 1.0;
240 double del_neg
= 0.0;
246 if(fabs(c
) < GSL_DBL_EPSILON
|| fabs(d
) < GSL_DBL_EPSILON
) {
248 result
->err
= INFINITY
;
249 GSL_ERROR ("error", GSL_EDOM
);
254 result
->val
= sum_pos
- sum_neg
;
255 result
->err
= del_pos
+ del_neg
;
256 result
->err
+= 2.0 * GSL_DBL_EPSILON
* (sum_pos
+ sum_neg
);
257 result
->err
+= 2.0 * GSL_DBL_EPSILON
* (2.0*sqrt(k
)+1.0) * fabs(result
->val
);
258 GSL_ERROR ("error", GSL_EMAXITER
);
261 del
*= (a
+k
)*(b
+k
) * x
/ ((c
+k
) * (d
+k
) * (k
+1.0)); /* Gauss series */
267 else if(del
== 0.0) {
268 /* Exact termination (a or b was a negative integer).
280 * This stopping criteria is taken from the thesis
281 * "Computation of Hypergeometic Functions" by J. Pearson, pg. 31
282 * (http://people.maths.ox.ac.uk/porterm/research/pearson_final.pdf)
283 * and fixes bug #45926
285 if (fabs(del_prev
/ (sum_pos
- sum_neg
)) < GSL_DBL_EPSILON
&&
286 fabs(del
/ (sum_pos
- sum_neg
)) < GSL_DBL_EPSILON
)
290 } while(fabs((del_pos
+ del_neg
)/(sum_pos
-sum_neg
)) > GSL_DBL_EPSILON
);
292 result
->val
= sum_pos
- sum_neg
;
293 result
->err
= del_pos
+ del_neg
;
294 result
->err
+= 2.0 * GSL_DBL_EPSILON
* (sum_pos
+ sum_neg
);
295 result
->err
+= 2.0 * GSL_DBL_EPSILON
* (2.0*sqrt(k
) + 1.0) * fabs(result
->val
);
300 // Complex square root with branch selection
301 static inline complex double csqrt_branch(complex double x
, int xbranch
) {
302 return csqrt(x
) * min1pow(xbranch
);
306 // The Delta_n factor from [Kambe II], Appendix 3
307 // \f[ \Delta_n = \int_n^\infty t^{-1/2 - n} \exp(-t + z^2/(4t))\ud t \f]
308 /* If |Im z| is big, Faddeeva_z might cause double overflow. In such case,
309 * use bigimz = true to use a slightly different formula to initialise
310 * the first two elements.
312 * The actual choice is done outside this function in order to enable
313 * testing/comparison of the results.
315 void ewald3_2_sigma_long_Delta_recurrent(complex double *target
, double *err
,
316 int maxn
, complex double x
, int xbranch
, complex double z
, bool bigimz
) {
317 complex double expfac
= cexp(-x
+ 0.25 * z
*z
/ x
);
318 complex double sqrtx
= csqrt_branch(x
, xbranch
); // TODO check carefully, which branch is needed
319 double w_plus_abs
= NAN
, w_minus_abs
= NAN
; // Used only if err != NULL
320 QPMS_ASSERT(maxn
>= 0);
321 // These are used to fill the first two elements for recurrence
323 complex double w_plus
= Faddeeva_w(+z
/(2*sqrtx
) + I
*sqrtx
, 0);
324 complex double w_minus
= Faddeeva_w(-z
/(2*sqrtx
) + I
*sqrtx
, 0);
326 target
[0] = 0.5 * M_SQRTPI
* expfac
* (w_minus
+ w_plus
);
328 target
[1] = I
/ z
* M_SQRTPI
* expfac
* (w_minus
- w_plus
);
329 if(err
) { w_plus_abs
= cabs(w_plus
); w_minus_abs
= cabs(w_minus
); }
331 /* A different strategy to avoid double overflow using the formula
332 * w(y) = exp(-y*y) * erfc(-I*y):
334 * ž_± = ±z/(2*sqrtx) + I * sqrtx
335 * and expfac = exp(ž**2), where ž**2 = -x + (z*z/4/x),
337 * expfac * w(ž_±) = exp(ž**2 - ž_±**2) erfc(-I * ž_±)
338 * = exp(∓ I * z) erfc(-I * ž_±)
340 complex double w_plus_n
= cexp(- I
* z
) * Faddeeva_erfc(-I
* (+z
/(2*sqrtx
) + I
*sqrtx
), 0);
341 complex double w_minus_n
= cexp(+ I
* z
) * Faddeeva_erfc(-I
* (-z
/(2*sqrtx
) + I
*sqrtx
), 0);
343 target
[0] = 0.5 * M_SQRTPI
* (w_minus_n
+ w_plus_n
);
345 target
[1] = I
/ z
* M_SQRTPI
* (w_minus_n
- w_plus_n
);
347 for(int n
= 1; n
< maxn
; ++n
) { // The rest via recurrence
348 // TODO The cpow(x, 0.5 - n) might perhaps better be replaced with a recurrently computed variant
349 target
[n
+1] = -(4 / (z
*z
)) * (-(0.5 - n
) * target
[n
] + target
[n
-1] - sqrtx
* cpow(x
, -n
) * expfac
);
350 if(isnan(creal(target
[n
+1])) || isnan(cimag(target
[n
+1]))) {
351 QPMS_WARN("Encountered NaN.");
355 // The error estimates for library math functions are based on
356 // https://www.gnu.org/software/libc/manual/html_node/Errors-in-Math-Functions.html
357 // and are not guaranteed to be extremely precise.
358 // The error estimate for Faddeeva's functions is based on the package's authors at
359 // http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package
360 // "we find that the accuracy is typically at at least 13 significant digits in both the real and imaginary parts"
361 // FIXME the error estimate seems might be off by several orders of magnitude (try parameters x = -3, z = 0.5, maxn=20)
362 // FIXME the error estimate does not take into account the alternative recurrence init. formula (bigimz)
363 double expfac_abs
= cabs(expfac
);
364 double w_plus_err
= w_plus_abs
* 1e-13, w_minus_err
= w_minus_abs
* 1e-13; // LPTODO argument error contrib.
365 double expfac_err
= expfac_abs
* (4 * DBL_EPSILON
); // LPTODO add argument error contrib.
366 double z_abs
= cabs(z
);
367 double z_err
= z_abs
* DBL_EPSILON
;
368 double x_abs
= cabs(x
);
370 err
[0] = 0.5 * M_SQRTPI
* (expfac_abs
* (w_minus_err
+ w_plus_err
) + (w_minus_abs
+ w_plus_abs
) * expfac_err
);
372 err
[1] = 2 * err
[0] / z_abs
+ cabs(target
[1]) * z_err
/ (z_abs
*z_abs
);
373 for(int n
= 1; n
< maxn
; ++n
) {
374 err
[n
+1] = (2 * cabs(target
[n
+1]) / z_abs
+ 4 * ((0.5+n
) * err
[n
] + err
[n
-1] +
375 pow(x_abs
, 0.5 - n
) * (2*DBL_EPSILON
* expfac_abs
+ expfac_err
)) // LPTODO not ideal, pow() call is an overkill
376 ) * z_err
/ (z_abs
*z_abs
);
382 void ewald3_2_sigma_long_Delta_series(complex double *target
, double *err
,
383 int maxn
, complex double x
, int xbranch
, complex double z
) {
384 complex double w
= 0.25*z
*z
;
385 double w_abs
= cabs(w
);
388 maxk
= 0; // Delta is equal to the respective incomplete Gamma functions
390 // Estimate a suitable maximum k, using Stirling's formula, so that w**maxk / maxk! is less than DBL_EPSILON
391 // This implementation is quite stupid, but it is still cheap compared to the actual computation, so LPTODO better one
393 double log_w_abs
= log(w_abs
);
394 while (maxk
* (log_w_abs
- log(maxk
) + 1) >= -DBL_MANT_DIG
)
397 // TODO asserts on maxn, maxk
399 complex double *Gammas
;
400 double *Gammas_err
= NULL
, *Gammas_abs
= NULL
;
401 QPMS_CRASHING_CALLOC(Gammas
, maxk
+maxn
+1, sizeof(*Gammas
));
403 QPMS_CRASHING_CALLOC(Gammas_err
, maxk
+maxn
+1, sizeof(*Gammas_err
));
404 QPMS_CRASHING_MALLOC(Gammas_abs
, (maxk
+maxn
+1) * sizeof(*Gammas_abs
));
407 for(int j
= 0; j
<= maxn
+maxk
; ++j
) {
409 QPMS_ENSURE_SUCCESS(complex_gamma_inc_e(0.5-j
, x
, xbranch
, &g
));
412 Gammas_abs
[j
] = cabs(g
.val
);
413 Gammas_err
[j
] = g
.err
;
417 for(int n
= 0; n
<= maxn
; ++n
) target
[n
] = 0;
418 if(err
) for(int n
= 0; n
<= maxn
; ++n
) err
[n
] = 0;
420 complex double wpowk_over_fack
= 1.;
421 double wpowk_over_fack_abs
= 1.;
422 for(int k
= 0; k
<= maxk
; ++k
, wpowk_over_fack
*= w
/k
) { // TODO? Kahan sum, Horner's method?
423 // Also TODO? for small n, continue for higher k if possible/needed
424 for(int n
= 0; n
<= maxn
; ++n
) {
425 target
[n
] += Gammas
[n
+k
] * wpowk_over_fack
;
427 // DBL_EPSILON might not be the best estimate here, but...
428 err
[n
] += wpowk_over_fack_abs
* Gammas_err
[n
+k
] + DBL_EPSILON
* Gammas_abs
[n
+k
];
429 wpowk_over_fack_abs
*= w_abs
/ (k
+1);
434 // TODO add an error estimate for the k-cutoff!!!
442 void ewald3_2_sigma_long_Delta(complex double *target
, double *err
,
443 int maxn
, complex double x
, int xbranch
, complex double z
) {
444 double absz
= cabs(z
);
445 if (absz
< 2.) // TODO take into account also the other parameters
446 ewald3_2_sigma_long_Delta_series(target
, err
, maxn
, x
, xbranch
, z
);
448 ewald3_2_sigma_long_Delta_recurrent(target
, err
, maxn
, x
, xbranch
, z
,
449 fabs(cimag(z
)) > DELTA_RECURRENT_EXPOVERFLOW_LIMIT
);