QMCPACK
GKIntegration.h
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2016 Jeongnim Kim and QMCPACK developers.
6 //
7 // File developed by: Paul R. C. Kent, kentpr@ornl.gov, Oak Ridge National Laboratory
8 // Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
9 //
10 // File created by: Paul R. C. Kent, kentpr@ornl.gov, Oak Ridge National Laboratory
11 //////////////////////////////////////////////////////////////////////////////////////
12 
13 
14 // http://pathintegrals.info //
15 /////////////////////////////////////////////////////////////
16 
17 /////////////////////////////////////////////////////////////////////////////
18 // //
19 // Adaptive Gauss-Kronrod integration //
20 // //
21 // This C++ version was written by Burkhard Militzer Livermore 02-20-02 //
22 // Based on the GNU scientific library //
23 // //
24 /////////////////////////////////////////////////////////////////////////////
25 
26 #ifndef _GKINTEGRATION_
27 #define _GKINTEGRATION_
28 
29 #include <iterator>
30 #include <list>
31 #include "Standard.h"
32 
33 class GK15
34 {
35 public:
36  static const int n = 8;
37  static const double xgk[n];
38  static const double wg[n / 2];
39  static const double wgk[n];
40 };
41 
42 class GK21
43 {
44 public:
45  static const int n = 11;
46  static const double xgk[n];
47  static const double wg[n / 2];
48  static const double wgk[n];
49 };
50 
51 class GK31
52 {
53 public:
54  static const int n = 16;
55  static const double xgk[n];
56  static const double wg[n / 2];
57  static const double wgk[n];
58 };
59 
60 class GK41
61 {
62 public:
63  static const int n = 21;
64  static const double xgk[n];
65  static const double wg[(n + 1) / 2];
66  static const double wgk[n];
67 };
68 
69 class GK51
70 {
71 public:
72  static const int n = 26;
73  static const double xgk[n];
74  static const double wg[n / 2];
75  static const double wgk[n];
76 };
77 
78 class GK61
79 {
80 public:
81  static const int n = 31;
82  static const double xgk[n];
83  static const double wg[n / 2];
84  static const double wgk[n];
85 };
86 
87 ////////////////////////////////////////////////////////////////////////////////////////
88 
89 template<class F, class GKRule = GK31>
91 {
92 private:
94  {
95  public:
96  IntervalResult(const double a_, const double b_, const double delta_)
97  : a(a_), b(b_), result(0.0), err(0.0), delta(delta_){};
98  double a, b, result, err, delta;
99 
100  double ErrorL() const { return (delta ? err / delta : err); }
101 
102  friend std::ostream& operator<<(std::ostream& os, const IntervalResult& ir)
103  {
104  os << "[a= " << ir.a << " b= " << ir.b << " result= " << ir.result << " error/L= " << ir.err / (ir.b - ir.a)
105  << " error= " << ir.err << " ]";
106  return os;
107  }
108  };
109 
110  std::list<IntervalResult> ir;
111  F& f; // could be not const in case where calling f() actually modifies the object
113 
114 public:
115  GKIntegration(F& f_) : f(f_), relativeErrors(false) {}
116 
119 
120 private:
121  // funnel all calls through this function and branch to specific n knot rule
122  void GK(IntervalResult& r) { GKGeneral(GKRule::n, GKRule::xgk, GKRule::wg, GKRule::wgk, r); }
123 
124  // handle all n knot rule with the passed in positions and weights
125  void GKGeneral(const int n, const double xgk[], const double wg[], const double wgk[], IntervalResult& r)
126  {
127  const double center = 0.5 * (r.a + r.b);
128  const double halfLength = 0.5 * r.delta;
129  const double fCenter = f(center);
130 
131  double resultGauss = 0;
132  double resultKronrod = fCenter * wgk[n - 1];
133 
134  if (n % 2 == 0)
135  {
136  resultGauss = fCenter * wg[n / 2 - 1];
137  }
138 
139  for (int j = 0; j < (n - 1) / 2; j++)
140  {
141  const int jtw = j * 2 + 1; // j=1,2,3 jtw=2,4,6
142  const double xx = halfLength * xgk[jtw];
143  const double fval1 = f(center - xx);
144  const double fval2 = f(center + xx);
145  const double fsum = fval1 + fval2;
146  resultGauss += wg[j] * fsum;
147  resultKronrod += wgk[jtw] * fsum;
148  }
149 
150  for (int j = 0; j < n / 2; j++)
151  {
152  int jtwm1 = j * 2;
153  const double xx = halfLength * xgk[jtwm1];
154  const double fval1 = f(center - xx);
155  const double fval2 = f(center + xx);
156  resultKronrod += wgk[jtwm1] * (fval1 + fval2);
157  };
158 
159  /* scale by the width of the integration region */
160  resultGauss *= halfLength;
161  resultKronrod *= halfLength;
162 
163  r.result = resultKronrod;
164  r.err = std::abs(resultKronrod - resultGauss);
165  //r.err = pow(200.0 * std::abs(resultKronrod - resultGauss), 1.5);
166  // BMWrite(r);
167  }
168 
169  void PrintList()
170  {
171  std::cout << "/------------------------------------------\\" << std::endl;
172  int i = 0;
173  for (typename std::list<IntervalResult>::iterator p = ir.begin(); p != ir.end(); p++)
174  {
175  BMWrite2(i, *p);
176  i++;
177  }
178  std::cout << "\\------------------------------------------/" << std::endl;
179  }
180 
181  //Print interval with maxium error per interval length
182  //(not with maximum error - this is on top of the list)
183  void PrintMax()
184  {
185  typename std::list<IntervalResult>::iterator rMax(ir.begin());
186 
187  for (typename std::list<IntervalResult>::iterator r = ir.begin()++; r != ir.end(); r++)
188  {
189  if ((*r).ErrorL() > (*rMax).ErrorL())
190  rMax = r;
191  }
192 
193  BMWrite(*rMax);
194  }
195 
196  void CheckList()
197  {
198  if (ir.size() == 0)
199  return;
200 
201  for (typename std::list<IntervalResult>::iterator p = ir.begin(); p != ir.end(); p++)
202  {
203  typename std::list<IntervalResult>::iterator pn = p;
204  pn++;
205  if (pn != ir.end())
206  if (((*p).err) < ((*pn).err))
207  {
208  PrintList();
209  BMWrite2(*pn, *p);
210  ::error("Ordering problem in list");
211  }
212  }
213  }
214 
215  void CheckError(const double err)
216  {
217  double errorSum = 0.0;
218 
219  if (ir.size() > 0)
220  {
221  for (typename std::list<IntervalResult>::iterator p = ir.begin(); p != ir.end(); p++)
222  {
223  errorSum += (*p).err;
224  }
225  }
226 
227  if (errorSum == 0.0)
228  {
229  if (err != 0.0)
230  error("CheckError", errorSum, err);
231  }
232  else
233  {
234  if (err / errorSum - 1.0 > 1e-8 && std::abs(err - errorSum) > 1e-14)
235  error("CheckError", errorSum, err, errorSum - err);
236  }
237 
238  BMWrite4("PassedErrorCheck", errorSum, err, errorSum - err);
239  }
240 
241  double RecomputeError()
242  {
243  double errorSum = 0.0;
244 
245  if (ir.size() > 0)
246  {
247  for (typename std::list<IntervalResult>::iterator p = ir.begin(); p != ir.end(); p++)
248  {
249  errorSum += (*p).err;
250  }
251  }
252 
253  return errorSum;
254  }
255 
256  void Insert(const IntervalResult& r)
257  {
258  // std::cout << "Inserting.." << std::endl;
259  // PrintList();
260 
261  if (ir.empty())
262  {
263  ir.push_back(r);
264  return;
265  }
266 
267  if (ir.back().err >= r.err)
268  {
269  ir.push_back(r);
270  return;
271  }
272 
273  if (r.err >= ir.front().err)
274  {
275  ir.push_front(r);
276  return;
277  }
278 
279  // size must be >=2
280  typename std::list<IntervalResult>::iterator p = ir.end();
281  p--;
282 
283  // p cannot become less the begin() because of check above
284  while (r.err > (*p).err)
285  p--;
286 
287  // go one down because insert put the element before p
288  p++;
289  ir.insert(p, r);
290  // CheckList();
291  }
292 
293  double Integrate(const double a,
294  const double b,
295  const double absError,
296  const bool absErrorFlag,
297  const double relError,
298  const bool relErrorFlag,
299  const bool andFlag)
300  {
301  ir.clear();
302 
303  // #define PRINT_IT
304 #ifdef PRINT_IT
305  std::cout << "Beginning integration" << std::endl;
306 #endif
307 
308  double errorUnresolved = 0.0;
309  const int iterationMax = 30;
310  // double lengthMin = (b-a)*pow(0.5,iterationMax);
311  double lengthMin = ldexp(b - a, -iterationMax);
312 
313  IntervalResult r0(a, b, b - a);
314  GK(r0);
315  double result = r0.result;
316  double err = r0.err;
317  double errLast = err;
318 
319  ir.push_back(r0);
320 
321  bool quitFlag;
322  do
323  {
324  // PrintList();
325 
326  // Test if the interval with the biggest error has already been subdivided
327  // the maximum number of times. If this is the case throw it out and print add
328  // this contribution to the 'unresolved' errors to be printed at the end
329  while (ir.size() > 0)
330  {
331  IntervalResult& rTest(ir.front());
332  double lengthTest = rTest.delta;
333  if (lengthTest < lengthMin)
334  {
335  warning("KC:Interval was divided too many times", iterationMax, rTest.a, rTest.b, rTest.err, ir.size());
336  // warning("KC:current result=",result,"error=",err);
337  if (absErrorFlag)
338  warning("Absolute accuracy = ", absError);
339  if (relErrorFlag)
340  warning("Relative accuracy = ", relError, "->absolute accuracy=", relError * std::abs(result));
341  // this means there is a problem with the integrand->you could exit here
342  // exit(1);
343  errorUnresolved += rTest.err;
344  // PrintList();
345  ir.pop_front();
346  }
347  else
348  break;
349  }
350  // do you want to exit with a warning after the first unresolved sub-interval occurred?
351  if (ir.size() == 0 || errorUnresolved > 0.0)
352  break;
353  // or print as many as occur
354  // if (ir.size()==0) break;
355 
356  IntervalResult& r(ir.front());
357 
358  double center = 0.5 * (r.a + r.b);
359  IntervalResult r1(r.a, center, 0.5 * r.delta);
360  IntervalResult r2(center, r.b, 0.5 * r.delta);
361 
362  GK(r1);
363  GK(r2);
364 
365  // must not use r after popping
366  result += r1.result + r2.result - r.result;
367  err += r1.err + r2.err - r.err;
368 
369 #ifdef PRINT_IT
370  std::cout.setf(std::ios::scientific);
371  std::cout << "Refined [ " << r.a << " " << r.b << " ] err/L=" << (r1.err + r2.err) / (r.b - r.a)
372  << " error=" << err << std::endl;
373 #endif
374 
375  // must remove old element first because new ones could move to top
376  ir.pop_front();
377 
378  Insert(r1);
379  Insert(r2);
380 
381  // In rare events, the error decreases over may (>10) orders of magnitude
382  // during the refinement. Rounding errors from the beginning can prevent
383  // err from becoming small enough. Recompute err after a substantial decrease.
384  if (err < 1e-6 * errLast)
385  {
386  err = RecomputeError();
387  errLast = err;
388  }
389 
390  // CheckError(err);
391  // PrintList();
392 
393  const bool relOk = (err < relError * std::abs(result) || result == 0.0);
394  const bool absOk = (err < absError);
395 
396  if (absErrorFlag && relErrorFlag)
397  {
398  quitFlag = andFlag ? (relOk && absOk) : (relOk || absOk);
399  }
400  else
401  {
402  quitFlag = absErrorFlag ? absOk : relOk;
403  }
404 
405  } while (!quitFlag);
406 
407 #ifdef PRINT_IT
408  PrintMax();
409 #endif
410 
411  if (errorUnresolved > 0.0)
412  {
413  warning("KC:Unresolved error sum=", errorUnresolved, "for integration interval", a, b);
414  warning("KC:--> Result=", result, "total error=", err,
415  "rel. error=", ((result != 0.0) ? err / std::abs(result) : 0.0));
416  // if (absErrorFlag) warning("Absolute accuracy = ",absError);
417  // if (relErrorFlag) warning("Relative accuracy = ",relError,
418  // "->absolute accuracy=",relError*std::abs(result));
419  }
420 
421  // CheckList();
422 #ifdef PRINT_IT
423  std::cout << "End integration" << std::endl;
424 #endif
425  double sum = 0.0;
426  int numIntervals = 0;
427  for (typename std::list<IntervalResult>::iterator p = ir.begin(); p != ir.end(); p++)
428  {
429  sum += p->result;
430  numIntervals++;
431  }
432 
433  // if (numIntervals > 2000)
434  // std::cerr << "Number of intervals = " << numIntervals << std::endl;
435 
436  double badSum = std::abs((result - sum) / sum);
437  if ((badSum > 1.0e-7) && (std::abs(result - sum) > absError))
438  {
439  std::cerr << "absError tolerance = " << absError << std::endl;
440  std::cerr << "Percent error = " << badSum * 100.0 << std::endl;
441  std::cerr << "Number of intervals = " << numIntervals << std::endl;
442  std::cerr << "result = " << result << " sum = " << sum << std::endl;
443  }
444 
445 
446  return result;
447  }
448 
449 public:
450  double Integrate(const double a, const double b, const double acc)
451  {
452  return Integrate(a, b, acc, !relativeErrors, acc, relativeErrors, false);
453  }
454  double Integrate(const double a, const double b, const double accAbs, const double accRel, const bool andFlag)
455  {
456  return Integrate(a, b, accAbs, true, accRel, true, andFlag);
457  }
458 };
459 
460 #endif // _GKINTEGRATION_
double Integrate(const double a, const double b, const double absError, const bool absErrorFlag, const double relError, const bool relErrorFlag, const bool andFlag)
static const double wg[n/2]
Definition: GKIntegration.h:47
GKIntegration(F &f_)
static const int n
Definition: GKIntegration.h:72
void SetRelativeErrorMode()
void warning()
Definition: Standard.h:275
std::list< IntervalResult > ir
static const double wg[n/2]
Definition: GKIntegration.h:56
void GK(IntervalResult &r)
static const double xgk[n]
Definition: GKIntegration.h:82
MakeReturn< UnaryNode< FnFabs, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t abs(const Vector< T1, C1 > &l)
static const double xgk[n]
Definition: GKIntegration.h:46
static const double wg[(n+1)/2]
Definition: GKIntegration.h:65
MakeReturn< BinaryNode< FnLdexp, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t, typename CreateLeaf< Vector< T2, C2 > >::Leaf_t > >::Expression_t ldexp(const Vector< T1, C1 > &l, const Vector< T2, C2 > &r)
void CheckError(const double err)
void GKGeneral(const int n, const double xgk[], const double wg[], const double wgk[], IntervalResult &r)
static const double xgk[n]
Definition: GKIntegration.h:37
IntervalResult(const double a_, const double b_, const double delta_)
Definition: GKIntegration.h:96
static const int n
Definition: GKIntegration.h:45
static const int n
Definition: GKIntegration.h:36
#define BMWrite2(i, j)
Definition: Standard.h:141
void SetAbsoluteErrorMode()
static const int n
Definition: GKIntegration.h:81
double RecomputeError()
void error(char const *m)
Definition: Standard.h:204
static const int n
Definition: GKIntegration.h:63
friend std::ostream & operator<<(std::ostream &os, const IntervalResult &ir)
static const double xgk[n]
Definition: GKIntegration.h:55
static const int n
Definition: GKIntegration.h:54
static const double wgk[n]
Definition: GKIntegration.h:57
static const double wgk[n]
Definition: GKIntegration.h:48
#define BMWrite4(i, j, k, l)
Definition: Standard.h:151
double Integrate(const double a, const double b, const double acc)
double Integrate(const double a, const double b, const double accAbs, const double accRel, const bool andFlag)
static const double wg[n/2]
Definition: GKIntegration.h:83
void Insert(const IntervalResult &r)
#define BMWrite(i)
Definition: Standard.h:136
static const double wg[n/2]
Definition: GKIntegration.h:74
static const double wgk[n]
Definition: GKIntegration.h:66
static const double wg[n/2]
Definition: GKIntegration.h:38
static const double wgk[n]
Definition: GKIntegration.h:39
static const double xgk[n]
Definition: GKIntegration.h:73
static const double wgk[n]
Definition: GKIntegration.h:75
static const double wgk[n]
Definition: GKIntegration.h:84
static const double xgk[n]
Definition: GKIntegration.h:64