1 #include "ctl.h"
2
3 #ifdef CTL_HAS_COMPLEX_INTEGRATION
4
5 /*
6 * Copyright (c) 2005 Steven G. Johnson
7 *
8 * Portions (see comments) based on HIntLib (also distributed under
9 * the GNU GPL, v2 or later), copyright (c) 2002-2005 Rudolf Schuerer.
10 * (http://www.cosy.sbg.ac.at/~rschuer/hintlib/)
11 *
12 * Portions (see comments) based on GNU GSL (also distributed under
13 * the GNU GPL, v2 or later), copyright (c) 1996-2000 Brian Gough.
14 * (http://www.gnu.org/software/gsl/)
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 */
31
32 /* As integrator.c, but integrates complex-valued integrands */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <math.h>
37 #include <limits.h>
38 #include <float.h>
39
40 /* Adaptive multidimensional integration on hypercubes (or, really,
41 hyper-rectangles) using cubature rules.
42
43 A cubature rule takes a function and a hypercube and evaluates
44 the function at a small number of points, returning an estimate
45 of the integral as well as an estimate of the error, and also
46 a suggested dimension of the hypercube to subdivide.
47
48 Given such a rule, the adaptive integration is simple:
49
50 1) Evaluate the cubature rule on the hypercube(s).
51 Stop if converged.
52
53 2) Pick the hypercube with the largest estimated error,
54 and divide it in two along the suggested dimension.
55
56 3) Goto (1).
57
58 */
59
60 /* numeric type for integrand */
61 #include <complex.h>
62 typedef complex double num;
63 #define num_abs cabs
64
65 typedef num (*integrand)(unsigned ndim, const double *x, void *);
66
67 /* Integrate the function f from xmin[dim] to xmax[dim], with at most
68 maxEval function evaluations (0 for no limit), until the given
69 absolute or relative error is achieved. val returns the integral,
70 and err returns the estimate for the absolute error in val. The
71 return value of the function is 0 on success and non-zero if there
72 was an error. */
73 static int adapt_integrate(integrand f, void *fdata, unsigned dim, const double *xmin,
74 const double *xmax, unsigned maxEval, double reqAbsError,
75 double reqRelError, num *val, double *err);
76
77 /***************************************************************************/
78 /* Basic datatypes */
79
80 typedef struct {
81 num val;
82 double err;
83 } esterr;
84
relError(esterr ee)85 static double relError(esterr ee) { return (ee.val == 0.0 ? HUGE_VAL : num_abs(ee.err / ee.val)); }
86
87 typedef struct {
88 unsigned dim;
89 double *data; /* length 2*dim = center followed by half-widths */
90 double vol; /* cache volume = product of widths */
91 } hypercube;
92
compute_vol(const hypercube * h)93 static double compute_vol(const hypercube *h) {
94 unsigned i;
95 double vol = 1;
96 for (i = 0; i < h->dim; ++i)
97 vol *= 2 * h->data[i + h->dim];
98 return vol;
99 }
100
make_hypercube(unsigned dim,const double * center,const double * halfwidth)101 static hypercube make_hypercube(unsigned dim, const double *center, const double *halfwidth) {
102 unsigned i;
103 hypercube h;
104 h.dim = dim;
105 h.data = (double *)malloc(sizeof(double) * dim * 2);
106 for (i = 0; i < dim; ++i) {
107 h.data[i] = center[i];
108 h.data[i + dim] = halfwidth[i];
109 }
110 h.vol = compute_vol(&h);
111 return h;
112 }
113
make_hypercube_range(unsigned dim,const double * xmin,const double * xmax)114 static hypercube make_hypercube_range(unsigned dim, const double *xmin, const double *xmax) {
115 hypercube h = make_hypercube(dim, xmin, xmax);
116 unsigned i;
117 for (i = 0; i < dim; ++i) {
118 h.data[i] = 0.5 * (xmin[i] + xmax[i]);
119 h.data[i + dim] = 0.5 * (xmax[i] - xmin[i]);
120 }
121 h.vol = compute_vol(&h);
122 return h;
123 }
124
destroy_hypercube(hypercube * h)125 static void destroy_hypercube(hypercube *h) {
126 free(h->data);
127 h->dim = 0;
128 }
129
130 typedef struct {
131 hypercube h;
132 esterr ee;
133 unsigned splitDim;
134 } region;
135
make_region(const hypercube * h)136 static region make_region(const hypercube *h) {
137 region R;
138 R.h = make_hypercube(h->dim, h->data, h->data + h->dim);
139 R.splitDim = 0;
140 return R;
141 }
142
destroy_region(region * R)143 static void destroy_region(region *R) { destroy_hypercube(&R->h); }
144
cut_region(region * R,region * R2)145 static void cut_region(region *R, region *R2) {
146 unsigned d = R->splitDim, dim = R->h.dim;
147 *R2 = *R;
148 R->h.data[d + dim] *= 0.5;
149 R->h.vol *= 0.5;
150 R2->h = make_hypercube(dim, R->h.data, R->h.data + dim);
151 R->h.data[d] -= R->h.data[d + dim];
152 R2->h.data[d] += R->h.data[d + dim];
153 }
154
155 typedef struct rule_s {
156 unsigned dim; /* the dimensionality */
157 unsigned num_points; /* number of evaluation points */
158 unsigned (*evalError)(struct rule_s *r, integrand f, void *fdata, const hypercube *h, esterr *ee);
159 void (*destroy)(struct rule_s *r);
160 } rule;
161
destroy_rule(rule * r)162 static void destroy_rule(rule *r) {
163 if (r->destroy) r->destroy(r);
164 free(r);
165 }
166
eval_region(region R,integrand f,void * fdata,rule * r)167 static region eval_region(region R, integrand f, void *fdata, rule *r) {
168 R.splitDim = r->evalError(r, f, fdata, &R.h, &R.ee);
169 return R;
170 }
171
172 /***************************************************************************/
173 /* Functions to loop over points in a hypercube. */
174
175 /* Based on orbitrule.cpp in HIntLib-0.0.10 */
176
177 /* ls0 returns the least-significant 0 bit of n (e.g. it returns
178 0 if the LSB is 0, it returns 1 if the 2 LSBs are 01, etcetera). */
179
180 #if (defined(__GNUC__) || defined(__ICC)) && (defined(__i386__) || defined(__x86_64__))
181 /* use x86 bit-scan instruction, based on count_trailing_zeros()
182 macro in GNU GMP's longlong.h. */
ls0(unsigned n)183 static unsigned ls0(unsigned n) {
184 unsigned count;
185 n = ~n;
186 __asm__("bsfl %1,%0" : "=r"(count) : "rm"(n));
187 return count;
188 }
189 #else
ls0(unsigned n)190 static unsigned ls0(unsigned n) {
191 const unsigned bits[256] = {
192 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0,
193 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1,
194 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0,
195 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,
196 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0,
197 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1,
198 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0,
199 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,
200 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8,
201 };
202 unsigned bit = 0;
203 while ((n & 0xff) == 0xff) {
204 n >>= 8;
205 bit += 8;
206 }
207 return bit + bits[n & 0xff];
208 }
209 #endif
210
211 /**
212 * Evaluate the integral on all 2^n points (+/-r,...+/-r)
213 *
214 * A Gray-code ordering is used to minimize the number of coordinate updates
215 * in p.
216 */
evalR_Rfs(integrand f,void * fdata,unsigned dim,double * p,const double * c,const double * r)217 static num evalR_Rfs(integrand f, void *fdata, unsigned dim, double *p, const double *c,
218 const double *r) {
219 num sum = 0;
220 unsigned i;
221 unsigned signs = 0; /* 0/1 bit = +/- for corresponding element of r[] */
222
223 /* We start with the point where r is ADDed in every coordinate
224 (this implies signs=0). */
225 for (i = 0; i < dim; ++i)
226 p[i] = c[i] + r[i];
227
228 /* Loop through the points in Gray-code ordering */
229 for (i = 0;; ++i) {
230 unsigned mask, d;
231
232 sum += f(dim, p, fdata);
233
234 d = ls0(i); /* which coordinate to flip */
235 if (d >= dim) break;
236
237 /* flip the d-th bit and add/subtract r[d] */
238 mask = 1U << d;
239 signs ^= mask;
240 p[d] = (signs & mask) ? c[d] - r[d] : c[d] + r[d];
241 }
242 return sum;
243 }
244
evalRR0_0fs(integrand f,void * fdata,unsigned dim,double * p,const double * c,const double * r)245 static num evalRR0_0fs(integrand f, void *fdata, unsigned dim, double *p, const double *c,
246 const double *r) {
247 unsigned i, j;
248 num sum = 0;
249
250 for (i = 0; i < dim - 1; ++i) {
251 p[i] = c[i] - r[i];
252 for (j = i + 1; j < dim; ++j) {
253 p[j] = c[j] - r[j];
254 sum += f(dim, p, fdata);
255 p[i] = c[i] + r[i];
256 sum += f(dim, p, fdata);
257 p[j] = c[j] + r[j];
258 sum += f(dim, p, fdata);
259 p[i] = c[i] - r[i];
260 sum += f(dim, p, fdata);
261
262 p[j] = c[j]; /* Done with j -> Restore p[j] */
263 }
264 p[i] = c[i]; /* Done with i -> Restore p[i] */
265 }
266 return sum;
267 }
268
evalR0_0fs4d(integrand f,void * fdata,unsigned dim,double * p,const double * c,num * sum0_,const double * r1,num * sum1_,const double * r2,num * sum2_)269 static unsigned evalR0_0fs4d(integrand f, void *fdata, unsigned dim, double *p, const double *c,
270 num *sum0_, const double *r1, num *sum1_, const double *r2,
271 num *sum2_) {
272 double maxdiff = 0;
273 unsigned i, dimDiffMax = 0;
274 num sum0, sum1 = 0, sum2 = 0; /* copies for aliasing, performance */
275
276 double ratio = r1[0] / r2[0];
277
278 ratio *= ratio;
279 sum0 = f(dim, p, fdata);
280
281 for (i = 0; i < dim; i++) {
282 num f1a, f1b, f2a, f2b;
283 double diff;
284
285 p[i] = c[i] - r1[i];
286 sum1 += (f1a = f(dim, p, fdata));
287 p[i] = c[i] + r1[i];
288 sum1 += (f1b = f(dim, p, fdata));
289 p[i] = c[i] - r2[i];
290 sum2 += (f2a = f(dim, p, fdata));
291 p[i] = c[i] + r2[i];
292 sum2 += (f2b = f(dim, p, fdata));
293 p[i] = c[i];
294
295 diff = num_abs(f1a + f1b - 2 * sum0 - ratio * (f2a + f2b - 2 * sum0));
296
297 if (diff > maxdiff) {
298 maxdiff = diff;
299 dimDiffMax = i;
300 }
301 }
302
303 *sum0_ += sum0;
304 *sum1_ += sum1;
305 *sum2_ += sum2;
306
307 return dimDiffMax;
308 }
309
310 #define num0_0(dim) (1U)
311 #define numR0_0fs(dim) (2 * (dim))
312 #define numRR0_0fs(dim) (2 * (dim) * (dim - 1))
313 #define numR_Rfs(dim) (1U << (dim))
314
315 /***************************************************************************/
316 /* Based on rule75genzmalik.cpp in HIntLib-0.0.10: An embedded
317 cubature rule of degree 7 (embedded rule degree 5) due to A. C. Genz
318 and A. A. Malik. See:
319
320 A. C. Genz and A. A. Malik, "An imbedded [sic] family of fully
321 symmetric numerical integration rules," SIAM
322 J. Numer. Anal. 20 (3), 580-588 (1983).
323 */
324
325 typedef struct {
326 rule parent;
327
328 /* temporary arrays of length dim */
329 double *widthLambda, *widthLambda2, *p;
330
331 /* dimension-dependent constants */
332 double weight1, weight3, weight5;
333 double weightE1, weightE3;
334 } rule75genzmalik;
335
336 #define real(x) ((double)(x))
337 #define to_int(n) ((int)(n))
338
isqr(int x)339 static int isqr(int x) { return x * x; }
340
destroy_rule75genzmalik(rule * r_)341 static void destroy_rule75genzmalik(rule *r_) {
342 rule75genzmalik *r = (rule75genzmalik *)r_;
343 free(r->p);
344 }
345
rule75genzmalik_evalError(rule * r_,integrand f,void * fdata,const hypercube * h,esterr * ee)346 static unsigned rule75genzmalik_evalError(rule *r_, integrand f, void *fdata, const hypercube *h,
347 esterr *ee) {
348 /* lambda2 = sqrt(9/70), lambda4 = sqrt(9/10), lambda5 = sqrt(9/19) */
349 const double lambda2 = 0.3585685828003180919906451539079374954541;
350 const double lambda4 = 0.9486832980505137995996680633298155601160;
351 const double lambda5 = 0.6882472016116852977216287342936235251269;
352 const double weight2 = 980. / 6561.;
353 const double weight4 = 200. / 19683.;
354 const double weightE2 = 245. / 486.;
355 const double weightE4 = 25. / 729.;
356
357 rule75genzmalik *r = (rule75genzmalik *)r_;
358 unsigned i, dimDiffMax, dim = r_->dim;
359 num sum1 = 0.0, sum2 = 0.0, sum3 = 0.0, sum4, sum5, result, res5th;
360 const double *center = h->data;
361 const double *halfwidth = h->data + dim;
362
363 for (i = 0; i < dim; ++i)
364 r->p[i] = center[i];
365
366 for (i = 0; i < dim; ++i)
367 r->widthLambda2[i] = halfwidth[i] * lambda2;
368 for (i = 0; i < dim; ++i)
369 r->widthLambda[i] = halfwidth[i] * lambda4;
370
371 /* Evaluate function in the center, in f(lambda2,0,...,0) and
372 f(lambda3=lambda4, 0,...,0). Estimate dimension with largest error */
373 dimDiffMax = evalR0_0fs4d(f, fdata, dim, r->p, center, &sum1, r->widthLambda2, &sum2,
374 r->widthLambda, &sum3);
375
376 /* Calculate sum4 for f(lambda4, lambda4, 0, ...,0) */
377 sum4 = evalRR0_0fs(f, fdata, dim, r->p, center, r->widthLambda);
378
379 /* Calculate sum5 for f(lambda5, lambda5, ..., lambda5) */
380 for (i = 0; i < dim; ++i)
381 r->widthLambda[i] = halfwidth[i] * lambda5;
382 sum5 = evalR_Rfs(f, fdata, dim, r->p, center, r->widthLambda);
383
384 /* Calculate fifth and seventh order results */
385
386 result = h->vol * (r->weight1 * sum1 + weight2 * sum2 + r->weight3 * sum3 + weight4 * sum4 +
387 r->weight5 * sum5);
388 res5th = h->vol * (r->weightE1 * sum1 + weightE2 * sum2 + r->weightE3 * sum3 + weightE4 * sum4);
389
390 ee->val = result;
391 ee->err = num_abs(res5th - result);
392
393 return dimDiffMax;
394 }
395
make_rule75genzmalik(unsigned dim)396 static rule *make_rule75genzmalik(unsigned dim) {
397 rule75genzmalik *r;
398
399 if (dim < 2) return 0; /* this rule does not support 1d integrals */
400
401 /* Because of the use of a bit-field in evalR_Rfs, we are limited
402 to be < 32 dimensions (or however many bits are in unsigned).
403 This is not a practical limitation...long before you reach
404 32 dimensions, the Genz-Malik cubature becomes excruciatingly
405 slow and is superseded by other methods (e.g. Monte-Carlo). */
406 if (dim >= sizeof(unsigned) * 8) return 0;
407
408 r = (rule75genzmalik *)malloc(sizeof(rule75genzmalik));
409 r->parent.dim = dim;
410
411 r->weight1 = (real(12824 - 9120 * to_int(dim) + 400 * isqr(to_int(dim))) / real(19683));
412 r->weight3 = real(1820 - 400 * to_int(dim)) / real(19683);
413 r->weight5 = real(6859) / real(19683) / real(1U << dim);
414 r->weightE1 = (real(729 - 950 * to_int(dim) + 50 * isqr(to_int(dim))) / real(729));
415 r->weightE3 = real(265 - 100 * to_int(dim)) / real(1458);
416
417 r->p = (double *)malloc(sizeof(double) * dim * 3);
418 r->widthLambda = r->p + dim;
419 r->widthLambda2 = r->p + 2 * dim;
420
421 r->parent.num_points = num0_0(dim) + 2 * numR0_0fs(dim) + numRR0_0fs(dim) + numR_Rfs(dim);
422
423 r->parent.evalError = rule75genzmalik_evalError;
424 r->parent.destroy = destroy_rule75genzmalik;
425
426 return (rule *)r;
427 }
428
429 /***************************************************************************/
430 /* 1d 15-point Gaussian quadrature rule, based on qk15.c and qk.c in
431 GNU GSL (which in turn is based on QUADPACK). */
432
rule15gauss_evalError(rule * r,integrand f,void * fdata,const hypercube * h,esterr * ee)433 static unsigned rule15gauss_evalError(rule *r, integrand f, void *fdata, const hypercube *h,
434 esterr *ee) {
435 /* Gauss quadrature weights and kronrod quadrature abscissae and
436 weights as evaluated with 80 decimal digit arithmetic by
437 L. W. Fullerton, Bell Labs, Nov. 1981. */
438 const unsigned n = 8;
439 const double xgk[8] = {
440 /* abscissae of the 15-point kronrod rule */
441 0.991455371120812639206854697526329,
442 0.949107912342758524526189684047851,
443 0.864864423359769072789712788640926,
444 0.741531185599394439863864773280788,
445 0.586087235467691130294144838258730,
446 0.405845151377397166906606412076961,
447 0.207784955007898467600689403773245,
448 0.000000000000000000000000000000000
449 /* xgk[1], xgk[3], ... abscissae of the 7-point gauss rule.
450 xgk[0], xgk[2], ... to optimally extend the 7-point gauss rule */
451 };
452 static const double wg[4] = {
453 /* weights of the 7-point gauss rule */
454 0.129484966168869693270611432679082, 0.279705391489276667901467771423780,
455 0.381830050505118944950369775488975, 0.417959183673469387755102040816327};
456 static const double wgk[8] = {
457 /* weights of the 15-point kronrod rule */
458 0.022935322010529224963732008058970, 0.063092092629978553290700663189204,
459 0.104790010322250183839876322541518, 0.140653259715525918745189590510238,
460 0.169004726639267902826583426598550, 0.190350578064785409913256402421014,
461 0.204432940075298892414161999234649, 0.209482141084727828012999174891714};
462
463 const double center = h->data[0];
464 const double halfwidth = h->data[1];
465 double fv1[7], fv2[7];
466 const num f_center = f(1, ¢er, fdata);
467 num result_gauss = f_center * wg[n / 2 - 1];
468 num result_kronrod = f_center * wgk[n - 1];
469 double result_abs = num_abs(result_kronrod);
470 num mean;
471 double result_asc, err;
472 unsigned j;
473
474 for (j = 0; j < (n - 1) / 2; ++j) {
475 int j2 = 2 * j + 1;
476 double x, w = halfwidth * xgk[j2];
477 num f1, f2, fsum;
478 x = center - w;
479 fv1[j2] = f1 = f(1, &x, fdata);
480 x = center + w;
481 fv2[j2] = f2 = f(1, &x, fdata);
482 fsum = f1 + f2;
483 result_gauss += wg[j] * fsum;
484 result_kronrod += wgk[j2] * fsum;
485 result_abs += wgk[j2] * (num_abs(f1) + num_abs(f2));
486 }
487
488 for (j = 0; j < n / 2; ++j) {
489 int j2 = 2 * j;
490 double x, w = halfwidth * xgk[j2];
491 num f1, f2;
492 x = center - w;
493 fv1[j2] = f1 = f(1, &x, fdata);
494 x = center + w;
495 fv2[j2] = f2 = f(1, &x, fdata);
496 result_kronrod += wgk[j2] * (f1 + f2);
497 result_abs += wgk[j2] * (num_abs(f1) + num_abs(f2));
498 }
499
500 ee->val = result_kronrod * halfwidth;
501
502 /* compute error estimate: */
503 mean = result_kronrod * 0.5;
504 result_asc = wgk[n - 1] * num_abs(f_center - mean);
505 for (j = 0; j < n - 1; ++j)
506 result_asc += wgk[j] * (num_abs(fv1[j] - mean) + num_abs(fv2[j] - mean));
507 err = num_abs(result_kronrod - result_gauss) * halfwidth;
508 result_abs *= halfwidth;
509 result_asc *= halfwidth;
510 if (result_asc != 0 && err != 0) {
511 double scale = pow((200 * err / result_asc), 1.5);
512 if (scale < 1)
513 err = result_asc * scale;
514 else
515 err = result_asc;
516 }
517 if (result_abs > DBL_MIN / (50 * DBL_EPSILON)) {
518 double min_err = 50 * DBL_EPSILON * result_abs;
519 if (min_err > err) err = min_err;
520 }
521 ee->err = err;
522
523 return 0; /* no choice but to divide 0th dimension */
524 }
525
make_rule15gauss(unsigned dim)526 static rule *make_rule15gauss(unsigned dim) {
527 rule *r;
528 if (dim != 1) return 0; /* this rule is only for 1d integrals */
529 r = (rule *)malloc(sizeof(rule));
530 r->dim = dim;
531 r->num_points = 15;
532 r->evalError = rule15gauss_evalError;
533 r->destroy = 0;
534 return r;
535 }
536
537 /***************************************************************************/
538 /* binary heap implementation (ala _Introduction to Algorithms_ by
539 Cormen, Leiserson, and Rivest), for use as a priority queue of
540 regions to integrate. */
541
542 typedef region heap_item;
543 #define KEY(hi) ((hi).ee.err)
544
545 typedef struct {
546 unsigned n, nalloc;
547 heap_item *items;
548 esterr ee;
549 } heap;
550
heap_resize(heap * h,unsigned nalloc)551 static void heap_resize(heap *h, unsigned nalloc) {
552 h->nalloc = nalloc;
553 h->items = (heap_item *)realloc(h->items, sizeof(heap_item) * nalloc);
554 }
555
heap_alloc(unsigned nalloc)556 static heap heap_alloc(unsigned nalloc) {
557 heap h;
558 h.n = 0;
559 h.nalloc = 0;
560 h.items = 0;
561 h.ee.val = h.ee.err = 0;
562 heap_resize(&h, nalloc);
563 return h;
564 }
565
566 /* note that heap_free does not deallocate anything referenced by the items */
heap_free(heap * h)567 static void heap_free(heap *h) {
568 h->n = 0;
569 heap_resize(h, 0);
570 }
571
heap_push(heap * h,heap_item hi)572 static void heap_push(heap *h, heap_item hi) {
573 int insert;
574
575 h->ee.val += hi.ee.val;
576 h->ee.err += hi.ee.err;
577 insert = h->n;
578 if (++(h->n) > h->nalloc) heap_resize(h, h->n * 2);
579
580 while (insert) {
581 int parent = (insert - 1) / 2;
582 if (KEY(hi) <= KEY(h->items[parent])) break;
583 h->items[insert] = h->items[parent];
584 insert = parent;
585 }
586 h->items[insert] = hi;
587 }
588
heap_pop(heap * h)589 static heap_item heap_pop(heap *h) {
590 heap_item ret;
591 int i, n, child;
592
593 if (!(h->n)) {
594 fprintf(stderr, "attempted to pop an empty heap\n");
595 exit(EXIT_FAILURE);
596 }
597
598 ret = h->items[0];
599 h->items[i = 0] = h->items[n = --(h->n)];
600 while ((child = i * 2 + 1) < n) {
601 int largest;
602 heap_item swap;
603
604 if (KEY(h->items[child]) <= KEY(h->items[i]))
605 largest = i;
606 else
607 largest = child;
608 if (++child < n && KEY(h->items[largest]) < KEY(h->items[child])) largest = child;
609 if (largest == i) break;
610 swap = h->items[i];
611 h->items[i] = h->items[largest];
612 h->items[i = largest] = swap;
613 }
614
615 h->ee.val -= ret.ee.val;
616 h->ee.err -= ret.ee.err;
617 return ret;
618 }
619
620 /***************************************************************************/
621
622 /* adaptive integration, analogous to adaptintegrator.cpp in HIntLib */
623
ruleadapt_integrate(rule * r,integrand f,void * fdata,const hypercube * h,unsigned maxEval,double reqAbsError,double reqRelError,esterr * ee)624 static int ruleadapt_integrate(rule *r, integrand f, void *fdata, const hypercube *h,
625 unsigned maxEval, double reqAbsError, double reqRelError,
626 esterr *ee) {
627 unsigned maxIter; /* maximum number of adaptive subdivisions */
628 heap regions;
629 unsigned i;
630 int status = -1; /* = ERROR */
631
632 if (maxEval) {
633 if (r->num_points > maxEval) return status; /* ERROR */
634 maxIter = (maxEval - r->num_points) / (2 * r->num_points);
635 }
636 else
637 maxIter = UINT_MAX;
638
639 regions = heap_alloc(1);
640
641 heap_push(®ions, eval_region(make_region(h), f, fdata, r));
642 /* another possibility is to specify some non-adaptive subdivisions:
643 if (initialRegions != 1)
644 partition(h, initialRegions, EQUIDISTANT, ®ions, f,fdata, r); */
645
646 for (i = 0; i < maxIter; ++i) {
647 region R, R2;
648 if (regions.ee.err <= reqAbsError || relError(regions.ee) <= reqRelError) {
649 status = 0; /* converged! */
650 break;
651 }
652 R = heap_pop(®ions); /* get worst region */
653 cut_region(&R, &R2);
654 heap_push(®ions, eval_region(R, f, fdata, r));
655 heap_push(®ions, eval_region(R2, f, fdata, r));
656 }
657
658 ee->val = ee->err = 0; /* re-sum integral and errors */
659 for (i = 0; i < regions.n; ++i) {
660 ee->val += regions.items[i].ee.val;
661 ee->err += regions.items[i].ee.err;
662 destroy_region(®ions.items[i]);
663 }
664 /* printf("regions.nalloc = %d\n", regions.nalloc); */
665 heap_free(®ions);
666
667 return status;
668 }
669
adapt_integrate(integrand f,void * fdata,unsigned dim,const double * xmin,const double * xmax,unsigned maxEval,double reqAbsError,double reqRelError,num * val,double * err)670 static int adapt_integrate(integrand f, void *fdata, unsigned dim, const double *xmin,
671 const double *xmax, unsigned maxEval, double reqAbsError,
672 double reqRelError, num *val, double *err) {
673 rule *r;
674 hypercube h;
675 esterr ee;
676 int status;
677
678 if (dim == 0) { /* trivial integration */
679 *val = f(0, xmin, fdata);
680 *err = 0;
681 return 0;
682 }
683 r = dim == 1 ? make_rule15gauss(dim) : make_rule75genzmalik(dim);
684 if (!r) {
685 *val = 0;
686 *err = HUGE_VAL;
687 return -2; /* ERROR */
688 }
689 h = make_hypercube_range(dim, xmin, xmax);
690 status = ruleadapt_integrate(r, f, fdata, &h, maxEval, reqAbsError, reqRelError, &ee);
691 *val = ee.val;
692 *err = ee.err;
693 destroy_hypercube(&h);
694 destroy_rule(r);
695 return status;
696 }
697
698 /***************************************************************************/
699
700 /* Compile with -DTEST_INTEGRATOR for a self-contained test program.
701
702 Usage: ./integrator <dim> <tol> <integrand> <maxeval>
703
704 where <dim> = # dimensions, <tol> = relative tolerance,
705 <integrand> is either 0/1/2 for the three test integrands (see below),
706 and <maxeval> is the maximum # function evaluations (0 for none).
707 */
708
709 #ifdef TEST_INTEGRATOR
710
711 int count = 0;
712 int which_integrand = 0;
713 const double radius = 0.50124145262344534123412; /* random */
714
715 /* Simple constant function */
fconst(double x[],size_t dim,void * params)716 num fconst(double x[], size_t dim, void *params) { return 1; }
717
718 /*** f0, f1, f2, and f3 are test functions from the Monte-Carlo
719 integration routines in GSL 1.6 (monte/test.c). Copyright (c)
720 1996-2000 Michael Booth, GNU GPL. ****/
721
722 /* Simple product function */
f0(unsigned dim,const double * x,void * params)723 num f0(unsigned dim, const double *x, void *params) {
724 double prod = 1.0;
725 unsigned int i;
726 for (i = 0; i < dim; ++i)
727 prod *= 2.0 * x[i];
728 return prod;
729 }
730
731 /* Gaussian centered at 1/2. */
f1(unsigned dim,const double * x,void * params)732 num f1(unsigned dim, const double *x, void *params) {
733 double a = *(double *)params;
734 double sum = 0.;
735 unsigned int i;
736 for (i = 0; i < dim; i++) {
737 double dx = x[i] - 0.5;
738 sum += dx * dx;
739 }
740 return (pow(M_2_SQRTPI / (2. * a), (double)dim) * exp(-sum / (a * a)));
741 }
742
743 /* double gaussian */
f2(unsigned dim,const double * x,void * params)744 num f2(unsigned dim, const double *x, void *params) {
745 double a = *(double *)params;
746 double sum1 = 0.;
747 double sum2 = 0.;
748 unsigned int i;
749 for (i = 0; i < dim; i++) {
750 double dx1 = x[i] - 1. / 3.;
751 double dx2 = x[i] - 2. / 3.;
752 sum1 += dx1 * dx1;
753 sum2 += dx2 * dx2;
754 }
755 return 0.5 * pow(M_2_SQRTPI / (2. * a), dim) * (exp(-sum1 / (a * a)) + exp(-sum2 / (a * a)));
756 }
757
758 /* Tsuda's example */
f3(unsigned dim,const double * x,void * params)759 num f3(unsigned dim, const double *x, void *params) {
760 double c = *(double *)params;
761 double prod = 1.;
762 unsigned int i;
763 for (i = 0; i < dim; i++)
764 prod *= c / (c + 1) * pow((c + 1) / (c + x[i]), 2.0);
765 return prod;
766 }
767
768 /*** end of GSL test functions ***/
769
f_test(unsigned dim,const double * x,void * data)770 num f_test(unsigned dim, const double *x, void *data) {
771 double val;
772 unsigned i;
773 ++count;
774 switch (which_integrand) {
775 case 0: /* simple smooth (separable) objective: prod. cos(x[i]). */
776 val = 1;
777 for (i = 0; i < dim; ++i)
778 val *= cos(x[i]);
779 break;
780 case 1: { /* integral of exp(-x^2), rescaled to (0,infinity) limits */
781 double scale = 1.0;
782 val = 0;
783 for (i = 0; i < dim; ++i) {
784 double z = (1 - x[i]) / x[i];
785 val += z * z;
786 scale *= M_2_SQRTPI / (x[i] * x[i]);
787 }
788 val = exp(-val) * scale;
789 break;
790 }
791 case 2: /* discontinuous objective: volume of hypersphere */
792 val = 0;
793 for (i = 0; i < dim; ++i)
794 val += x[i] * x[i];
795 val = val < radius * radius;
796 break;
797 case 3: val = f0(dim, x, data); break;
798 case 4: val = f1(dim, x, data); break;
799 case 5: val = f2(dim, x, data); break;
800 case 6: val = f3(dim, x, data); break;
801 default: fprintf(stderr, "unknown integrand %d\n", which_integrand); exit(EXIT_FAILURE);
802 }
803 /* if (count < 100) printf("%d: f(%g, ...) = %g\n", count, x[0], val); */
804 return val;
805 }
806
807 /* surface area of n-dimensional unit hypersphere */
S(unsigned n)808 static double S(unsigned n) {
809 double val;
810 int fact = 1;
811 if (n % 2 == 0) { /* n even */
812 val = 2 * pow(M_PI, n * 0.5);
813 n = n / 2;
814 while (n > 1)
815 fact *= (n -= 1);
816 val /= fact;
817 }
818 else { /* n odd */
819 val = (1 << (n / 2 + 1)) * pow(M_PI, n / 2);
820 while (n > 2)
821 fact *= (n -= 2);
822 val /= fact;
823 }
824 return val;
825 }
826
exact_integral(unsigned dim,const double * xmax)827 static num exact_integral(unsigned dim, const double *xmax) {
828 unsigned i;
829 double val;
830 switch (which_integrand) {
831 case 0:
832 val = 1;
833 for (i = 0; i < dim; ++i)
834 val *= sin(xmax[i]);
835 break;
836 case 2: val = dim == 0 ? 1 : S(dim) * pow(radius * 0.5, dim) / dim; break;
837 default: val = 1.0;
838 }
839 return val;
840 }
841
main(int argc,char ** argv)842 int main(int argc, char **argv) {
843 double *xmin, *xmax;
844 double tol, err;
845 num val;
846 unsigned i, dim, maxEval;
847 double fdata;
848
849 dim = argc > 1 ? atoi(argv[1]) : 2;
850 tol = argc > 2 ? atof(argv[2]) : 1e-2;
851 which_integrand = argc > 3 ? atoi(argv[3]) : 0;
852 maxEval = argc > 4 ? atoi(argv[4]) : 0;
853
854 fdata = which_integrand == 6 ? (1.0 + sqrt(10.0)) / 9.0 : 0.1;
855
856 xmin = (double *)malloc(dim * sizeof(double));
857 xmax = (double *)malloc(dim * sizeof(double));
858 for (i = 0; i < dim; ++i) {
859 xmin[i] = 0;
860 xmax[i] = 1 + (which_integrand >= 1 ? 0 : 0.4 * sin(i));
861 }
862
863 printf("%u-dim integral, tolerance = %g, integrand = %d\n", dim, tol, which_integrand);
864 adapt_integrate(f_test, &fdata, dim, xmin, xmax, maxEval, 0, tol, &val, &err);
865 printf("integration val = %g, est. err = %g, true err = %g\n", val, err,
866 num_abs(val - exact_integral(dim, xmax)));
867 printf("#evals = %d\n", count);
868
869 free(xmax);
870 free(xmin);
871
872 return 0;
873 }
874
875 #else
876
877 /*************************************************************************/
878 /* libctl interface */
879
880 static int adapt_integrate(integrand f, void *fdata, unsigned dim, const double *xmin,
881 const double *xmax, unsigned maxEval, double reqAbsError,
882 double reqRelError, num *val, double *err);
883
884 typedef struct {
885 cmultivar_func f;
886 void *fdata;
887 } cnum_wrap_data;
888
cnum_wrap(unsigned ndim,const double * x,void * fdata_)889 static num cnum_wrap(unsigned ndim, const double *x, void *fdata_) {
890 cnum_wrap_data *fdata = (cnum_wrap_data *)fdata_;
891 cnumber val = fdata->f(ndim, (double *)x, fdata->fdata);
892 return (cnumber_re(val) + I * cnumber_im(val));
893 }
894
cadaptive_integration(cmultivar_func f,number * xmin,number * xmax,integer n,void * fdata,number abstol,number reltol,integer maxnfe,number * esterr,integer * errflag)895 cnumber cadaptive_integration(cmultivar_func f, number *xmin, number *xmax, integer n, void *fdata,
896 number abstol, number reltol, integer maxnfe, number *esterr,
897 integer *errflag) {
898 num val;
899 cnum_wrap_data wdata;
900 wdata.f = f;
901 wdata.fdata = fdata;
902 *errflag =
903 adapt_integrate(cnum_wrap, &wdata, n, xmin, xmax, maxnfe, abstol, reltol, &val, esterr);
904 return make_cnumber(creal(val), cimag(val));
905 }
906
cf_scm_wrapper(integer n,number * x,void * f_scm_p)907 static cnumber cf_scm_wrapper(integer n, number *x, void *f_scm_p) {
908 SCM *f_scm = (SCM *)f_scm_p;
909 return scm2cnumber(gh_call1(*f_scm, make_number_list(n, x)));
910 }
911
cadaptive_integration_scm(SCM f_scm,SCM xmin_scm,SCM xmax_scm,SCM abstol_scm,SCM reltol_scm,SCM maxnfe_scm)912 SCM cadaptive_integration_scm(SCM f_scm, SCM xmin_scm, SCM xmax_scm, SCM abstol_scm, SCM reltol_scm,
913 SCM maxnfe_scm) {
914 integer n, maxnfe, errflag, i;
915 number *xmin, *xmax, abstol, reltol;
916 cnumber integral;
917
918 n = list_length(xmin_scm);
919 abstol = fabs(ctl_convert_number_to_c(abstol_scm));
920 reltol = fabs(ctl_convert_number_to_c(reltol_scm));
921 maxnfe = ctl_convert_integer_to_c(maxnfe_scm);
922
923 if (list_length(xmax_scm) != n) {
924 fprintf(stderr, "adaptive_integration: xmin/xmax dimension mismatch\n");
925 return SCM_UNDEFINED;
926 }
927
928 xmin = (number *)malloc(sizeof(number) * n);
929 xmax = (number *)malloc(sizeof(number) * n);
930 if (!xmin || !xmax) {
931 fprintf(stderr, "adaptive_integration: error, out of memory!\n");
932 exit(EXIT_FAILURE);
933 }
934
935 for (i = 0; i < n; ++i) {
936 xmin[i] = number_list_ref(xmin_scm, i);
937 xmax[i] = number_list_ref(xmax_scm, i);
938 }
939
940 integral = cadaptive_integration(cf_scm_wrapper, xmin, xmax, n, &f_scm, abstol, reltol, maxnfe,
941 &abstol, &errflag);
942
943 free(xmax);
944 free(xmin);
945
946 switch (errflag) {
947 case 3: fprintf(stderr, "adaptive_integration: invalid inputs\n"); return SCM_UNDEFINED;
948 case 1: fprintf(stderr, "adaptive_integration: maxnfe too small\n"); break;
949 case 2: fprintf(stderr, "adaptive_integration: lenwork too small\n"); break;
950 }
951
952 return gh_cons(cnumber2scm(integral), ctl_convert_number_to_scm(abstol));
953 }
954
955 #endif
956
957 #endif /* CTL_HAS_COMPLEX_INTEGRATION */
958