1 /*
2 * This source code is part of
3 *
4 * E R K A L E
5 * -
6 * DFT from Hel
7 *
8 * Written by Susi Lehtola, 2010-2011
9 * Copyright (c) 2010-2011, Susi Lehtola
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 */
16
17
18
19 #include "mathf.h"
20 #include <cmath>
21 #include <cfloat>
22
23 // For exceptions
24 #include <sstream>
25 #include <stdexcept>
26
27 extern "C" {
28 // For factorials and so on
29 #include <gsl/gsl_sf_gamma.h>
30 // For spline interpolation
31 #include <gsl/gsl_spline.h>
32 // For Bessel functions
33 #include <gsl/gsl_sf_bessel.h>
34 // For hypergeometric functions
35 #include <gsl/gsl_sf_hyperg.h>
36 // For trigonometric functions
37 #include <gsl/gsl_sf_trig.h>
38 // Random number generation
39 #include <gsl/gsl_rng.h>
40 // Random number distributions
41 #include <gsl/gsl_randist.h>
42 }
43
doublefact(int n)44 double doublefact(int n) {
45 if(n<-1) {
46 ERROR_INFO();
47 std::ostringstream oss;
48 oss << "Trying to compute double factorial for n="<<n<<"!";
49 throw std::runtime_error(oss.str());
50 }
51
52 if(n>=-1 && n<=1)
53 return 1.0;
54 else
55 return gsl_sf_doublefact(n);
56 }
57
fact(int n)58 double fact(int n) {
59 if(n<0) {
60 ERROR_INFO();
61 std::ostringstream oss;
62 oss << "Trying to compute factorial for n="<<n<<"\
63 !";
64 throw std::runtime_error(oss.str());
65 }
66
67 return gsl_sf_fact(n);
68 }
69
fact_ratio(int i,int r)70 double fact_ratio(int i, int r) {
71 return gsl_sf_fact(i)/(gsl_sf_fact(r)*gsl_sf_fact(i-2*r));
72 }
73
fgamma(double x)74 double fgamma(double x) {
75 return gsl_sf_gamma(x);
76 }
77
sinc(double x)78 double sinc(double x) {
79 return gsl_sf_sinc(x/M_PI);
80 }
81
bessel_jl(int l,double x)82 double bessel_jl(int l, double x) {
83 // Small x: use series expansion
84 double series=pow(x,l)/doublefact(2*l+1);
85 if(fabs(series)<sqrt(DBL_EPSILON))
86 return series;
87
88 // Large x: truncate due to incorrect GSL asymptotics
89 // (GSL bug https://savannah.gnu.org/bugs/index.php?36152)
90 if(x>1.0/DBL_EPSILON)
91 return 0.0;
92
93 // Default case: use GSL
94 return gsl_sf_bessel_jl(l,x);
95 }
96
boysF(int m,double x)97 double boysF(int m, double x) {
98 // Compute Boys' function
99
100 // Check whether we are operating in the range where the Taylor series is OK
101 if( x<=1.0 ) {
102
103 // Taylor series uses -x
104 x=-x;
105
106 // (-x)^k
107 double xk=1.0;
108 // k!
109 double kf=1.0;
110 // Value of Boys' function
111 double fm=0.0;
112 // index k
113 int k=0;
114
115 while(k<=15) {
116 // \f$ F_m(x) = \sum_{k=0}^\infty \frac {(-x)^k} { k! (2m+2k+1)} \f$
117 fm+=xk/(kf*(2*(m+k)+1));
118
119 k++;
120 xk*=x;
121 kf*=k;
122 }
123
124 return fm;
125
126 } else if(x>=38.0) {
127 // Use asymptotic expansion, which is precise to <1e-16 for F_n(x), n = 0 .. 60
128 return doublefact(2*m-1)/pow(2,m+1)*sqrt(M_PI/pow(x,2*m+1));
129
130 } else
131 // Need to use the exact formula
132 return 0.5*gsl_sf_gamma(m+0.5)*pow(x,-m-0.5)*gsl_sf_gamma_inc_P(m+0.5,x);
133 }
134
boysF_arr(int mmax,double x,arma::vec & F)135 void boysF_arr(int mmax, double x, arma::vec & F) {
136 // Resize array
137 F.zeros(mmax+1);
138 // Exp(-x) for recursion
139 double emx=exp(-x);
140
141 if(x<mmax) {
142 // Fill in highest value
143 F[mmax]=boysF(mmax,x);
144 // and fill in the rest with downward recursion
145 for(int m=mmax-1;m>=0;m--)
146 F[m]=(2*x*F[m+1]+emx)/(2*m+1);
147 } else {
148 // Fill in lowest value
149 F[0]=boysF(0,x);
150 // and fill in the rest with upward recursion
151 for(int m=1;m<=mmax;m++)
152 F[m]=((2*m-1)*F[m-1]-emx)/(2.0*x);
153 }
154 }
155
hyperg_1F1(double a,double b,double x)156 double hyperg_1F1(double a, double b, double x) {
157 // Handle possible underflows with a Kummer transformation
158 if(x>=-500.0) {
159 return gsl_sf_hyperg_1F1(a,b,x);
160 } else {
161 return exp(x)*gsl_sf_hyperg_1F1(b-a,b,-x);
162 }
163 }
164
choose(int m,int n)165 double choose(int m, int n) {
166 if(m<0 || n<0) {
167 ERROR_INFO();
168 throw std::domain_error("Choose called with a negative argument!\n");
169 }
170
171 return gsl_sf_choose(m,n);
172 }
173
getind(int l,int m,int n)174 int getind(int l, int m, int n) {
175 // Silence compiler warning
176 (void) l;
177 // In the loop the indices will be
178 int ii=m+n;
179 int jj=n;
180 // so the corresponding index is
181 return ii*(ii+1)/2 + jj;
182 }
183
max_abs(const arma::mat & R)184 double max_abs(const arma::mat & R) {
185 return arma::max(arma::max(arma::abs(R)));
186 }
187
max_cabs(const arma::cx_mat & R)188 double max_cabs(const arma::cx_mat & R) {
189 return arma::max(arma::max(arma::abs(R)));
190 }
191
rms_norm(const arma::mat & R)192 double rms_norm(const arma::mat & R) {
193 // Calculate \sum_ij R_ij^2
194 double rms=arma::trace(arma::trans(R)*R);
195 // and convert to rms
196 rms=sqrt(rms/(R.n_rows*R.n_cols));
197
198 return rms;
199 }
200
rms_cnorm(const arma::cx_mat & R)201 double rms_cnorm(const arma::cx_mat & R) {
202 // Calculate \sum_ij R_ij^2
203 double rms=std::abs(arma::trace(arma::trans(R)*R));
204 // and convert to rms
205 rms=sqrt(rms/(R.n_rows*R.n_cols));
206
207 return rms;
208 }
209
spline_interpolation(const std::vector<double> & xt,const std::vector<double> & yt,const std::vector<double> & x)210 std::vector<double> spline_interpolation(const std::vector<double> & xt, const std::vector<double> & yt, const std::vector<double> & x) {
211 if(xt.size()!=yt.size()) {
212 ERROR_INFO();
213 std::ostringstream oss;
214 oss << "xt and yt are of different lengths - " << xt.size() << " vs " << yt.size() << "!\n";
215 throw std::runtime_error(oss.str());
216 }
217
218 // Returned data
219 std::vector<double> y(x.size());
220
221 // Index accelerator
222 gsl_interp_accel *acc=gsl_interp_accel_alloc();
223 // Interpolant
224 gsl_interp *interp=gsl_interp_alloc(gsl_interp_cspline,xt.size());
225
226 // Initialize interpolant
227 gsl_interp_init(interp,&(xt[0]),&(yt[0]),xt.size());
228
229 // Perform interpolation.
230 for(size_t i=0;i<x.size();i++) {
231 y[i]=gsl_interp_eval(interp,&(xt[0]),&(yt[0]),x[i],acc);
232 }
233
234 // Free memory
235 gsl_interp_accel_free(acc);
236 gsl_interp_free(interp);
237
238 return y;
239 }
240
spline_interpolation(const arma::vec & xtv,const arma::vec & ytv,const arma::vec & xv)241 arma::vec spline_interpolation(const arma::vec & xtv, const arma::vec & ytv, const arma::vec & xv) {
242 return arma::conv_to<arma::colvec>::from(spline_interpolation(arma::conv_to< std::vector<double> >::from(xtv), arma::conv_to< std::vector<double> >::from(ytv), arma::conv_to< std::vector<double> >::from(xv)));
243 }
244
spline_interpolation(const std::vector<double> & xt,const std::vector<double> & yt,double x)245 double spline_interpolation(const std::vector<double> & xt, const std::vector<double> & yt, double x) {
246 if(xt.size()!=yt.size()) {
247 ERROR_INFO();
248 std::ostringstream oss;
249 oss << "xt and yt are of different lengths - " << xt.size() << " vs " << yt.size() << "!\n";
250 throw std::runtime_error(oss.str());
251 }
252
253 // Index accelerator
254 gsl_interp_accel *acc=gsl_interp_accel_alloc();
255 // Interpolant
256 gsl_interp *interp=gsl_interp_alloc(gsl_interp_cspline,xt.size());
257
258 // Initialize interpolant
259 gsl_interp_init(interp,&(xt[0]),&(yt[0]),xt.size());
260
261 // Perform interpolation.
262 double y=gsl_interp_eval(interp,&(xt[0]),&(yt[0]),x,acc);
263
264 // Free memory
265 gsl_interp_accel_free(acc);
266 gsl_interp_free(interp);
267
268 return y;
269 }
270
randu_mat(size_t M,size_t N,unsigned long int seed)271 arma::mat randu_mat(size_t M, size_t N, unsigned long int seed) {
272 // Use Mersenne Twister algorithm
273 gsl_rng *r = gsl_rng_alloc(gsl_rng_mt19937);
274 // Set seed
275 gsl_rng_set(r,seed);
276
277 // Matrix
278 arma::mat mat(M,N);
279 // Fill it
280 for(size_t i=0;i<M;i++)
281 for(size_t j=0;j<N;j++)
282 mat(i,j)=gsl_rng_uniform(r);
283
284 // Free rng
285 gsl_rng_free(r);
286 return mat;
287 }
288
randn_mat(size_t M,size_t N,unsigned long int seed)289 arma::mat randn_mat(size_t M, size_t N, unsigned long int seed) {
290 // Use Mersenne Twister algorithm
291 gsl_rng *r = gsl_rng_alloc(gsl_rng_mt19937);
292 // Set seed
293 gsl_rng_set(r,seed);
294
295 // Matrix
296 arma::mat mat(M,N);
297 // Fill it
298 for(size_t i=0;i<M;i++)
299 for(size_t j=0;j<N;j++)
300 mat(i,j)=gsl_ran_gaussian(r,1.0);
301
302 // Free rng
303 gsl_rng_free(r);
304 return mat;
305 }
306
real_orthogonal(size_t N,unsigned long int seed)307 arma::mat real_orthogonal(size_t N, unsigned long int seed) {
308 arma::mat U(N,N);
309 U.zeros();
310
311 // Generate totally random matrix
312 arma::mat A=randn_mat(N,N,seed);
313
314 // Perform QR decomposition on matrix
315 arma::mat Q, R;
316 bool ok=arma::qr(Q,R,A);
317 if(!ok) {
318 ERROR_INFO();
319 throw std::runtime_error("QR decomposition failure in complex_unitary.\n");
320 }
321
322 // Check that Q is orthogonal
323 arma::mat test=Q*arma::trans(Q);
324 for(size_t i=0;i<test.n_cols;i++)
325 test(i,i)-=1.0;
326 double n=rms_norm(test);
327 if(n>10*DBL_EPSILON) {
328 ERROR_INFO();
329 throw std::runtime_error("Generated matrix is not unitary!\n");
330 }
331
332 return Q;
333 }
334
complex_unitary(size_t N,unsigned long int seed)335 arma::cx_mat complex_unitary(size_t N, unsigned long int seed) {
336 arma::cx_mat U(N,N);
337 U.zeros();
338
339 // Generate totally random matrix
340 arma::cx_mat A=std::complex<double>(1.0,0.0)*randn_mat(N,N,seed) + std::complex<double>(0.0,1.0)*randn_mat(N,N,seed+1);
341
342 // Perform QR decomposition on matrix
343 arma::cx_mat Q, R;
344 bool ok=arma::qr(Q,R,A);
345 if(!ok) {
346 ERROR_INFO();
347 throw std::runtime_error("QR decomposition failure in complex_unitary.\n");
348 }
349
350 // Check that Q is unitary
351 arma::cx_mat test=Q*arma::trans(Q);
352 for(size_t i=0;i<test.n_cols;i++)
353 test(i,i)-=1.0;
354 double n=rms_cnorm(test);
355 if(n>10*DBL_EPSILON) {
356 ERROR_INFO();
357 throw std::runtime_error("Generated matrix is not unitary!\n");
358 }
359
360 return Q;
361 }
362
round(double x,unsigned n)363 double round(double x, unsigned n) {
364 double fac=pow(10.0,n);
365 return round(fac*x)/fac;
366 }
367
find_minima(const arma::vec & x,const arma::vec & y,size_t runave,double thr)368 arma::vec find_minima(const arma::vec & x, const arma::vec & y, size_t runave, double thr) {
369 if(x.n_elem != y.n_elem) {
370 ERROR_INFO();
371 throw std::runtime_error("Input vectors are of inconsistent size!\n");
372 }
373
374 // Create averaged vectors
375 arma::vec xave(x.n_elem-2*runave);
376 arma::vec yave(y.n_elem-2*runave);
377 if(runave==0) {
378 xave=x;
379 yave=y;
380 } else {
381 for(arma::uword i=runave;i<x.n_elem-runave;i++) {
382 xave(i-runave)=arma::mean(x.subvec(i-runave,i+runave));
383 yave(i-runave)=arma::mean(y.subvec(i-runave,i+runave));
384 }
385 }
386
387 // Find minima
388 std::vector<size_t> minloc;
389 if(yave(0)<yave(1))
390 minloc.push_back(0);
391
392 for(arma::uword i=1;i<yave.n_elem-1;i++)
393 if(yave(i)<yave(i-1) && yave(i)<yave(i+1))
394 minloc.push_back(i);
395
396 if(yave(yave.n_elem-1)<yave(yave.n_elem-2))
397 minloc.push_back(yave.n_elem-1);
398
399 // Check the minimum values
400 for(size_t i=minloc.size()-1;i<minloc.size();i--)
401 if(yave(minloc[i]) >= thr)
402 minloc.erase(minloc.begin()+i);
403
404 // Returned minima
405 arma::vec ret(minloc.size());
406 for(size_t i=0;i<minloc.size();i++)
407 ret(i)=xave(minloc[i]);
408
409 return ret;
410 }
411
stencil(double z,const arma::vec & x,arma::mat & w)412 void stencil(double z, const arma::vec & x, arma::mat & w) {
413 /*
414 z: location where approximations are wanted
415 x: grid points at which value of function is known
416 w: matrix holding weights for grid points for different orders
417 of the derivatives: w(x, order)
418
419 This implementation is based on B. Fornberg, "Calculation of
420 weights in finite difference formulas", SIAM Rev. 40, 685
421 (1998).
422 */
423
424 /* Extract variables */
425 size_t n = w.n_rows - 1;
426 size_t m = w.n_cols - 1;
427
428 /* Sanity check */
429 if(w.n_rows != x.n_elem)
430 throw std::logic_error("Grid points and weight matrix sizes aren't compatible!\n");
431
432 /* Initialize elements */
433 double c1 = 1.0;
434 double c4 = x(0)-z;
435 w.zeros();
436 w(0,0)=1.0;
437
438 for(size_t i=1;i<=n;i++) {
439 size_t mn = std::min(i,m);
440 double c2 = 1.0;
441 double c5 = c4;
442 c4 = x(i) - z;
443
444 for(size_t j=0;j<i;j++) {
445 double c3 = x(i) - x(j);
446 c2 *= c3;
447
448 if(j == i-1) {
449 for(size_t k=mn;k>0;k--)
450 w(i,k) = c1*(k*w(i-1,k-1) - c5*w(i-1,k))/c2;
451 w(i,0)=-c1*c5*w(i-1,0)/c2;
452 }
453 for(size_t k=mn;k>0;k--)
454 w(j,k)=(c4*w(j,k) - k*w(j,k-1))/c3;
455 w(j,0)=c4*w(j,0)/c3;
456 }
457 c1 = c2;
458 }
459 }
460