LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
Integrate.h
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
25 #if !defined(LSST_AFW_MATH_INTEGRATE_H)
26 #define LSST_AFW_MATH_INTEGRATE_H 1
27 
35 #include <functional>
36 #include <vector>
37 #include <queue>
38 #include <map>
39 #include <cmath>
40 #include <algorithm>
41 #include <assert.h>
42 #include <limits>
43 #include <ostream>
44 #include <sstream>
45 #include <complex>
46 #include <stdexcept>
47 
48 #include "lsst/pex/exceptions.h"
49 
51 
52 // == The following is Mike Jarvis original comment ==
53 //
54 // Basic Usage:
55 //
56 // First, define a function object, which should derive from
57 // std::unary_function<double,double>. For example, to integrate a
58 // Gaussian, use something along the lines of this:
59 //
60 // class Gauss :
61 // public std::unary_function<double,double>
62 // {
63 // public :
64 //
65 // Gauss(double _mu, double _sig) :
66 // mu(_mu), sig(_sig), sigsq(_sig*_sig) {}
67 //
68 // double operator()(double x) const
69 // {
70 // const double SQRTTWOPI = 2.50662827463;
71 // return exp(-pow(x-mu,2)/2./sigsq)/SQRTTWOPI/sig;
72 // }
73 //
74 // private :
75 // double mu,sig,sigsq;
76 // };
77 //
78 // Next, make an IntRegion object with the bounds of the integration region.
79 // You need to give it the type to use for the bounds and the value of the
80 // functions (which need to be the same currently - some day I'll allow
81 // complex functions...).
82 //
83 // For example, to integrate something from -1 to 1, use:
84 //
85 // integ::IntRegion<double> reg1(-1.,1.);
86 //
87 // (The integ:: is because everything here is in the integ namespace to
88 // help prevent name conflicts with other header files.)
89 //
90 // If a value is > 1.e10 or < -1.e10, then these values are taken to be
91 // infinity, rather than the actual value.
92 // So to integrate from 0 to infinity:
93 //
94 // integ::IntRegion<double> reg2(0.,1.e100);
95 //
96 // Or, you can use the variable integ::MOCK_INF which might be clearer.
97 //
98 //
99 // Finally, to perform the integral, the line would be:
100 //
101 // double integ1 = int1d(Gauss(0.,1.),reg1,1.e-10,1.e-4);
102 // double integ2 = int1d(Gauss(0.,2.),reg2,1.e-10,1.e-4);
103 //
104 // which should yield 0.68 and 0.5 in our case.
105 //
106 // Those last two numbers indicate the precision required.
107 // 1.e-10 is the required absolute error, and
108 // 1.e-4 is the required relative error.
109 //
110 // If you want, you can omit these and 1.e-15,1.e-6 will be used as the
111 // default precision (which are generally fine for most purposes).
112 //
113 // The absolute error only comes into play for results which are close to
114 // 0 to prevent requiring an error of 0 for integrals which evaluate to 0
115 // or very close to it.
116 //
117 //
118 //
119 // Advanced Usage:
120 //
121 // When an integration fails to converge with the usual GKP algorithm,
122 // it splits the region into 2 (or more) and tries again with each sub-region.
123 // The default is to just bisect the region (or something similarly smart for
124 // infinite regions), but if you know of a good place to split the region,
125 // you can tell it using:
126 //
127 // reg.AddSplit(10.)
128 //
129 // For example, if you know that you have a singularity somewhere in your
130 // region, it would help the program a lot to split there, so you
131 // should add that as a split point. Zeros can also be good choices.
132 //
133 // In addition to the integral being returned from int1d, int2d, or int3d as
134 // the return value, the value is also stored in the region itself.
135 // You can access it using:
136 //
137 // reg.Area();
138 //
139 // There is also an estimate of the error in the value:
140 //
141 // reg.Err();
142 //
143 // (It is intended to be an overestimate of the actual error,
144 // but it doesn't always get it completely right.)
145 //
146 //
147 //
148 // Two- and Three-Dimensional Integrals:
149 //
150 // These are slightly more complicated. The easiest case is when the
151 // bounds of the integral are a rectangle or 3d box. In this case,
152 // you can still use the regular IntRegion. The only new thing then
153 // is the definition of the function. For example, to integrate
154 // int(3x^2 + xy + y , x=0..1, y=0..1):
155 //
156 // struct Integrand :
157 // public std::binary_function<double,double,double>
158 // {
159 // double operator()(double x, double y) const
160 // { return x*(3.*x + y) + y; }
161 // };
162 //
163 // integ::IntRegion<double> reg3(0.,1.);
164 // double integ3 = int2d(Integrand(),reg3,reg3);
165 //
166 // (Which should give 1.75 as the result.)
167 //
168 //
169 //
170 
171 namespace lsst {
172 namespace afw {
173 namespace math {
174 
175 double const MOCK_INF = 1.e10;
176 
177 #ifdef NDEBUG
178 #define integ_dbg1 if (false) (*_dbgout)
179 #define integ_dbg2 if (false) (*(reg.getDbgout()))
180 #define integ_dbg3 if (false) (*(tempreg.getDbgout()))
181 #else
182 #define integ_dbg1 if (_dbgout) (*_dbgout)
183 #define integ_dbg2 if (reg.getDbgout()) (*(reg.getDbgout()))
184 #define integ_dbg3 if (tempreg.getDbgout()) (*(tempreg.getDbgout()))
185 #endif
186 
187 //#define COUNTFEVAL
188 // If defined, then count the number of function evaluations
189 
190 namespace details {
191 template <class T> inline T norm(const T& x) { return x*x; }
192 using std::norm;
193 template <class T> inline T real(const T& x) { return x; }
194 using std::real;
195 #ifdef COUNTFEVAL
196  int nfeval = 0;
197 #endif
198 }
199 
200 
201 template <class T>
202 struct IntRegion {
203 
204 public:
205  IntRegion(T const a, T const b, std::ostream* dbgout = 0) :
206  _a(a), _b(b), _error(0.0), _area(0), _dbgout(dbgout) {}
207 
208  bool operator<(IntRegion<T> const &r2) const { return _error < r2._error; }
209  bool operator>(IntRegion<T> const &r2) const { return _error > r2._error; }
210 
211  void SubDivide(std::vector<IntRegion<T> >* children) {
212  assert(children->size() == 0);
213  if (_splitpoints.size() == 0) { Bisect(); }
214  if (_splitpoints.size() > 1) {
215  std::sort(_splitpoints.begin(), _splitpoints.end());
216  }
217 
218 #if 0
219  if (_a > _splitpoints[0] || _b < _splitpoints.back()) {
220  std::cerr << "a, b = " << _a << ', ' << _b << std::endl;
221  std::cerr << "_splitpoints = ";
222  for (size_t i = 0; i<_splitpoints.size(); i++) {
223  std::cerr << _splitpoints[i] << " ";
224  }
225  std::cerr << std::endl;
226  }
227 #endif
228  assert(_splitpoints[0] >= _a);
229  assert(_splitpoints.back() <= _b);
230  children->push_back(IntRegion<T>(_a, _splitpoints[0], _dbgout));
231  for (size_t i = 1; i<_splitpoints.size(); i++) {
232  children->push_back(IntRegion<T>(_splitpoints[i-1], _splitpoints[i], _dbgout));
233  }
234  children->push_back(IntRegion<T>(_splitpoints.back(), _b, _dbgout));
235  }
236 
237  void Bisect() { _splitpoints.push_back((_a + _b)/2.0); }
238  void AddSplit(const T x) { _splitpoints.push_back(x); }
239  size_t NSplit() const { return _splitpoints.size(); }
240 
241  T const &Left() const { return _a; }
242  T const &Right() const {return _b; }
243  T const &Err() const { return _error; }
244  T const &Area() const { return _area; }
245  void SetArea(const T& a, const T& e) {
246  _area = a;
247  _error = e;
248  }
249 
250  std::ostream* getDbgout() { return _dbgout; }
251 
252 private:
254  std::vector<T> _splitpoints;
255  std::ostream* _dbgout;
256 
257 };
258 
259 
260 double const DEFABSERR = 1.e-15;
261 double const DEFRELERR = 1.e-6;
262 
263 namespace details {
264 
265 template <class T>
266 inline T Epsilon() { return std::numeric_limits<T>::epsilon(); }
267 template <class T>
268 inline T MinRep() { return std::numeric_limits<T>::min(); }
269 
270 
271 #ifdef EXTRA_PREC_H
272 template <>
273 inline Quad Epsilon<Quad>() { return 3.08148791094e-33; }
274 template <>
275 inline Quad MinRep<Quad>() { return 2.2250738585072014e-308; }
276 #endif
277 
278 
279 template <class T>
280 inline T rescale_error (T err, T const &resabs, T const &resasc) {
281  if (resasc != 0.0 && err != 0.0) {
282  T const scale = (200.0 * err / resasc);
283  if (scale < 1.0) {
284  err = resasc * scale * sqrt(scale);
285  }
286  else {
287  err = resasc;
288  }
289  }
290  if (resabs > MinRep<T>() / (50.0 * Epsilon<T>())) {
291  T const min_err = 50.0 * Epsilon<T>() * resabs;
292  if (min_err > err) {
293  err = min_err;
294  }
295  }
296  return err;
297 }
298 
315 template <class UF>
316 inline bool intGKPNA(
317  UF const &func, IntRegion<typename UF::result_type>& reg,
318  typename UF::result_type const epsabs,
319  typename UF::result_type const epsrel,
320  std::map<typename UF::result_type, typename UF::result_type>* fxmap = 0) {
321 
322  typedef typename UF::result_type UfResult;
323  UfResult const a = reg.Left();
324  UfResult const b = reg.Right();
325 
326  UfResult const halfLength = 0.5 * (b - a);
327  UfResult const absHalfLength = fabs (halfLength);
328  UfResult const center = 0.5 * (b + a);
329  UfResult const fCenter = func(center);
330 #ifdef COUNTFEVAL
331  nfeval++;
332 #endif
333 
334  assert(gkp_wb<UfResult>(0).size() == gkp_x<UfResult>(0).size() + 1);
335  UfResult area1 = gkp_wb<UfResult>(0).back() * fCenter;
336  std::vector<UfResult> fv1, fv2;
337  fv1.reserve(2*gkp_x<UfResult>(0).size() + 1);
338  fv2.reserve(2*gkp_x<UfResult>(0).size() + 1);
339  for (size_t k = 0; k<gkp_x<UfResult>(0).size(); k++) {
340  UfResult const abscissa = halfLength * gkp_x<UfResult>(0)[k];
341  UfResult const fval1 = func(center - abscissa);
342  UfResult const fval2 = func(center + abscissa);
343  area1 += gkp_wb<UfResult>(0)[k] * (fval1 + fval2);
344  fv1.push_back(fval1);
345  fv2.push_back(fval2);
346  if (fxmap) {
347  (*fxmap)[center - abscissa] = fval1;
348  (*fxmap)[center + abscissa] = fval2;
349  }
350  }
351 #ifdef COUNTFEVAL
352  nfeval += gkp_x<UfResult>(0).size()*2;
353 #endif
354 
355  integ_dbg2 << "level 0 rule: area = " << area1 << std::endl;
356 
357  UfResult err = 0;
358  bool calcabsasc = true;
359  UfResult resabs = 0.0, resasc = 0.0;
360  for (int level = 1; level < NGKPLEVELS; level++) {
361  assert(gkp_wa<UfResult>(level).size() == fv1.size());
362  assert(gkp_wa<UfResult>(level).size() == fv2.size());
363  assert(gkp_wb<UfResult>(level).size() == gkp_x<UfResult>(level).size() + 1);
364  UfResult area2 = gkp_wb<UfResult>(level).back() * fCenter;
365  // resabs = approximation to integral of abs(f)
366  if (calcabsasc) {
367  resabs = fabs(area2);
368  }
369  for (size_t k = 0; k < fv1.size(); k++) {
370  area2 += gkp_wa<UfResult>(level)[k] * (fv1[k] + fv2[k]);
371  if (calcabsasc) {
372  resabs += gkp_wa<UfResult>(level)[k] * (fabs(fv1[k]) + fabs(fv2[k]));
373  }
374  }
375  for (size_t k = 0; k < gkp_x<UfResult>(level).size(); k++) {
376  UfResult const abscissa = halfLength * gkp_x<UfResult>(level)[k];
377  UfResult const fval1 = func(center - abscissa);
378  UfResult const fval2 = func(center + abscissa);
379  UfResult const fval = fval1 + fval2;
380  area2 += gkp_wb<UfResult>(level)[k] * fval;
381  if (calcabsasc) {
382  resabs += gkp_wb<UfResult>(level)[k] * (fabs(fval1) + fabs(fval2));
383  }
384  fv1.push_back(fval1);
385  fv2.push_back(fval2);
386  if (fxmap) {
387  (*fxmap)[center - abscissa] = fval1;
388  (*fxmap)[center + abscissa] = fval2;
389  }
390  }
391 #ifdef COUNTFEVAL
392  nfeval += gkp_x<UfResult>(level).size()*2;
393 #endif
394  if (calcabsasc) {
395  UfResult const mean = area1*UfResult(0.5);
396  // resasc = approximation to the integral of abs(f-mean)
397  resasc = gkp_wb<UfResult>(level).back() * fabs(fCenter - mean);
398  for (size_t k = 0; k<gkp_wa<UfResult>(level).size(); k++) {
399  resasc += gkp_wa<UfResult>(level)[k] * (fabs(fv1[k] - mean) + fabs(fv2[k] - mean));
400  }
401  for (size_t k = 0; k<gkp_x<UfResult>(level).size(); k++) {
402  resasc += gkp_wb<UfResult>(level)[k] * (fabs(fv1[k] - mean) + fabs(fv2[k] - mean));
403  }
404  resasc *= absHalfLength;
405  resabs *= absHalfLength;
406  }
407  area2 *= halfLength;
408  err = rescale_error (fabs(area2 - area1), resabs, resasc) ;
409  if (err < resasc) {
410  calcabsasc = false;
411  }
412 
413  integ_dbg2 << "at level " << level << " area2 = " << area2;
414  integ_dbg2 << " +- " << err << std::endl;
415 
416  // test for convergence.
417  if (err < epsabs || err < epsrel * fabs (area2)) {
418  reg.SetArea(area2, err);
419  return true;
420  }
421  area1 = area2;
422  }
423 
424  // failed to converge
425  reg.SetArea(area1, err);
426 
427  integ_dbg2 << "Failed to reach tolerance with highest-order GKP rule";
428 
429  return false;
430 }
431 
432 
449 template <class UF>
450 inline void intGKP (
451  UF const &func, IntRegion<typename UF::result_type> &reg,
452  typename UF::result_type const epsabs,
453  typename UF::result_type const epsrel,
454  std::map<typename UF::result_type, typename UF::result_type>* fxmap = 0) {
455 
456  typedef typename UF::result_type UfResult;
457  integ_dbg2 << "Start intGKP\n";
458 
459  assert(epsabs >= 0.0);
460  assert(epsrel > 0.0);
461 
462  // perform the first integration
463  bool done = intGKPNA(func, reg, epsabs, epsrel, fxmap);
464  if (done) return;
465 
466  integ_dbg2 << "In adaptive GKP, failed first pass... subdividing\n";
467  integ_dbg2 << "Intial range = " << reg.Left() << ".." << reg.Right() << std::endl;
468 
469  int roundoffType1 = 0, errorType = 0;
470  UfResult roundoffType2 = 0;
471  size_t iteration = 1;
472 
473  std::priority_queue<IntRegion<UfResult>, std::vector<IntRegion<UfResult> > > allregions;
474  allregions.push(reg);
475  UfResult finalarea = reg.Area();
476  UfResult finalerr = reg.Err();
477  UfResult tolerance = std::max(epsabs, epsrel * fabs(finalarea));
478  assert(finalerr > tolerance);
479 
480  while (!errorType && finalerr > tolerance) {
481  // Bisect the subinterval with the largest error estimate
482  integ_dbg2 << "Current answer = " << finalarea << " +- " << finalerr;
483  integ_dbg2 << " (tol = " << tolerance << ")\n";
484  IntRegion<UfResult> parent = allregions.top();
485  allregions.pop();
486  integ_dbg2 << "Subdividing largest error region ";
487  integ_dbg2 << parent.Left() << ".." << parent.Right() << std::endl;
488  integ_dbg2 << "parent area = " << parent.Area();
489  integ_dbg2 << " +- " << parent.Err() << std::endl;
490  std::vector<IntRegion<UfResult> > children;
491  parent.SubDivide(&children);
492  // For "GKP", there are only two, but for GKPOSC, there is one
493  // for each oscillation in region
494 
495  // Try to do at least 3x better with the children
496  UfResult factor = 3*children.size()*finalerr/tolerance;
497  UfResult newepsabs = fabs(parent.Err()/factor);
498  UfResult newepsrel = newepsabs/fabs(parent.Area());
499  integ_dbg2 << "New epsabs, rel = " << newepsabs << ", " << newepsrel;
500  integ_dbg2 << " (" << children.size() << " children)\n";
501 
502  UfResult newarea = UfResult(0.0);
503  UfResult newerror = 0.0;
504  for (size_t i = 0; i<children.size(); i++) {
505  IntRegion<UfResult>& child = children[i];
506  integ_dbg2 << "Integrating child " << child.Left();
507  integ_dbg2 << ".." << child.Right() << std::endl;
508  bool hasConverged;
509  hasConverged = intGKPNA(func, child, newepsabs, newepsrel);
510  integ_dbg2 << "child (" << i + 1 << '/' << children.size() << ") ";
511  if (hasConverged) {
512  integ_dbg2 << " converged.";
513  } else {
514  integ_dbg2 << " failed.";
515  }
516  integ_dbg2 << " Area = " << child.Area() << " +- " << child.Err() << std::endl;
517 
518  newarea += child.Area();
519  newerror += child.Err();
520  }
521  integ_dbg2 << "Compare: newerr = " << newerror;
522  integ_dbg2 << " to parent err = " << parent.Err() << std::endl;
523 
524  finalerr += (newerror - parent.Err());
525  finalarea += newarea - parent.Area();
526 
527  UfResult delta = parent.Area() - newarea;
528  if (newerror <= parent.Err() && fabs (delta) <= parent.Err()
529  && newerror >= 0.99 * parent.Err()) {
530  integ_dbg2 << "roundoff type 1: delta/newarea = ";
531  integ_dbg2 << fabs(delta)/fabs(newarea);
532  integ_dbg2 << ", newerror/error = " << newerror/parent.Err() << std::endl;
533  roundoffType1++;
534  }
535  if (iteration >= 10 && newerror > parent.Err() &&
536  fabs(delta) <= newerror - parent.Err()) {
537  integ_dbg2 << "roundoff type 2: newerror/error = ";
538  integ_dbg2 << newerror/parent.Err() << std::endl;
539  roundoffType2 += std::min(newerror/parent.Err() - 1.0, UfResult(1.0));
540  }
541 
542  tolerance = std::max(epsabs, epsrel * fabs(finalarea));
543  if (finalerr > tolerance) {
544  if (roundoffType1 >= 200) {
545  errorType = 1; // round off error
546  integ_dbg2 << "GKP: Round off error 1\n";
547  }
548  if (roundoffType2 >= 200.0) {
549  errorType = 2; // round off error
550  integ_dbg2 << "GKP: Round off error 2\n";
551  }
552  if (fabs((parent.Right() - parent.Left())/(reg.Right() - reg.Left()))
553  < Epsilon<double>()) {
554  errorType = 3; // found singularity
555  integ_dbg2 << "GKP: Probable singularity\n";
556  }
557  }
558  for (size_t i = 0; i<children.size(); i++) {
559  allregions.push(children[i]);
560  }
561  iteration++;
562  }
563 
564  // Recalculate finalarea in case there are any slight rounding errors
565  finalarea = 0.0;
566  finalerr = 0.0;
567  while (!allregions.empty()) {
568  IntRegion<UfResult> const &r = allregions.top();
569  finalarea += r.Area();
570  finalerr += r.Err();
571  allregions.pop();
572  }
573  reg.SetArea(finalarea, finalerr);
574 
575  if (errorType == 1) {
576  std::ostringstream s;
577  s << "Type 1 roundoff's = " << roundoffType1;
578  s << ", Type 2 = " << roundoffType2 << std::endl;
579  s << "Roundoff error 1 prevents tolerance from being achieved ";
580  s << "in intGKP\n";
581  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, s.str());
582  } else if (errorType == 2) {
583  std::ostringstream s;
584  s << "Type 1 roundoff's = " << roundoffType1;
585  s << ", Type 2 = " << roundoffType2 << std::endl;
586  s << "Roundoff error 2 prevents tolerance from being achieved ";
587  s << "in intGKP\n";
588  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, s.str());
589  } else if (errorType == 3) {
590  std::ostringstream s;
591  s << "Bad integrand behavior found in the integration interval ";
592  s << "in intGKP\n";
593  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, s.str());
594  }
595 }
596 
597 
602 template <class UF>
603 struct AuxFunc1 : // f(1/x-1) for int(a..infinity)
604  public std::unary_function<typename UF::argument_type, typename UF::result_type> {
605 public:
606  AuxFunc1(const UF& f) : _f(f) {}
607  typename UF::result_type operator()(typename UF::argument_type x) const {
608  return _f(1.0/x - 1.0)/(x*x);
609  }
610 private:
611  UF const &_f;
612 };
613 
614 
619 template <class UF>
620 AuxFunc1<UF> inline Aux1(UF uf) { return AuxFunc1<UF>(uf); }
621 
622 
623 template <class UF>
624 struct AuxFunc2 : // f(1/x+1) for int(-infinity..b)
625  public std::unary_function<typename UF::argument_type, typename UF::result_type> {
626 public:
627  AuxFunc2(UF const &f) : _f(f) {}
628  typename UF::result_type operator()(typename UF::argument_type x) const {
629  return _f(1.0/x + 1.0)/(x*x);
630  }
631 private:
632  UF const &_f;
633 };
634 
635 
640 template <class UF>
641 AuxFunc2<UF> inline Aux2(UF uf) { return AuxFunc2<UF>(uf); }
642 
643 
644 
649 template <class T>
650 struct ConstantReg1 : public std::unary_function<T, IntRegion<T> > {
651  ConstantReg1(T a, T b) : ir(a, b) {}
652  ConstantReg1(IntRegion<T> const &r) : ir(r) {}
653  IntRegion<T> operator()(T) const { return ir; }
655 };
656 
657 template <class T>
658 struct ConstantReg2 : public std::binary_function<T, T, IntRegion<T> > {
659  ConstantReg2(T a, T b) : ir(a, b) {}
660  ConstantReg2(IntRegion<T> const &r) : ir(r) {}
661  IntRegion<T> operator()(T x, T y) const { return ir; }
663 };
664 
665 
666 // pulled from MoreFunctional.h. Needed in class Int2DAuxType and Int3DAuxType
667 template <class BF>
668 class binder2_1
669  : public std::unary_function<typename BF::second_argument_type,
670  typename BF::result_type> {
671 public:
672  binder2_1(const BF& oper,
673  typename BF::first_argument_type val)
674  : _oper(oper), _value(val) {}
675  typename BF::result_type
676  operator()(const typename BF::second_argument_type& x) const {
677  return _oper(_value, x);
678  }
679 protected:
680  BF _oper;
681  typename BF::first_argument_type _value;
682 };
683 
684 template <class BF, class Tp>
685 inline binder2_1<BF> bind21(const BF& oper, const Tp& x) {
686  typedef typename BF::first_argument_type Arg;
687  return binder2_1<BF>(oper, static_cast<Arg>(x));
688 }
689 
690 
691 template <class BF, class YREG>
692 class Int2DAuxType : public std::unary_function<typename BF::first_argument_type, typename BF::result_type> {
693 public:
694  Int2DAuxType(BF const &func, YREG const &yreg,
695  typename BF::result_type const &abserr,
696  typename BF::result_type const &relerr) :
697  _func(func), _yreg(yreg), _abserr(abserr), _relerr(relerr) {}
698 
699  typename BF::result_type operator()(typename BF::first_argument_type x) const {
700  typename YREG::result_type tempreg = _yreg(x);
701  typename BF::result_type result =
702  int1d(bind21(_func, x), tempreg, _abserr, _relerr);
703  integ_dbg3 << "Evaluated int2dAux at x = " << x;
704  integ_dbg3 << ": f = " << result << " +- " << tempreg.Err() << std::endl;
705  return result;
706  }
707 
708 private:
709  BF const &_func;
710  YREG const &_yreg;
711  typename BF::result_type _abserr, _relerr;
712 };
713 
714 
715 // pulled from MoreFunctional.h. Needed in class Int3DAuxtype
716 template <class TF>
717 class binder3_1
718  : public std::binary_function<typename TF::secondof3_argument_type,
719  typename TF::thirdof3_argument_type,
720  typename TF::result_type> {
721 public:
722  binder3_1(const TF& oper,
723  typename TF::firstof3_argument_type val)
724  : _oper(oper), _value(val) {}
725  typename TF::result_type
726  operator()(typename TF::secondof3_argument_type const &x1,
727  typename TF::thirdof3_argument_type const &x2) const {
728  return _oper(_value, x1, x2);
729  }
730 protected:
731  TF _oper;
732  typename TF::firstof3_argument_type _value;
733 };
734 
735 template <class TF, class Tp>
736 inline binder3_1<TF>
737 bind31(const TF& oper, const Tp& x) {
738  typedef typename TF::firstof3_argument_type Arg;
739  return binder3_1<TF>(oper, static_cast<Arg>(x));
740 }
741 
742 
743 template <class TF, class YREG, class ZREG>
745  public std::unary_function<typename TF::firstof3_argument_type, typename TF::result_type> {
746 public:
747  Int3DAuxType(const TF& func, const YREG& yreg, const ZREG& zreg,
748  const typename TF::result_type& abserr,
749  const typename TF::result_type& relerr) :
750  _func(func), _yreg(yreg), _zreg(zreg), _abserr(abserr), _relerr(relerr) {}
751 
752  typename TF::result_type operator()(typename TF::firstof3_argument_type x) const {
753  typename YREG::result_type tempreg = _yreg(x);
754  typename TF::result_type result =
755  int2d(bind31(_func, x), tempreg, bind21(_zreg, x), _abserr, _relerr);
756  integ_dbg3 << "Evaluated int3dAux at x = " << x;
757  integ_dbg3 << ": f = " << result << " +- " << tempreg.Err() << std::endl;
758  return result;
759  }
760 
761 private:
762  const TF& _func;
763  const YREG& _yreg;
764  const ZREG& _zreg;
765  typename TF::result_type _abserr, _relerr;
766 };
767 
768 } // end namespace details
769 
770 
771 
775 template <class UF>
776 inline typename UF::result_type int1d(
777  UF const &func,
779  typename UF::result_type const &abserr = DEFABSERR,
780  typename UF::result_type const &relerr = DEFRELERR) {
781 
782  typedef typename UF::result_type UfResult;
783  using namespace details;
784 
785  integ_dbg2 << "start int1d: " << reg.Left() << ".." << reg.Right() << std::endl;
786 
787  if ((reg.Left() <= -MOCK_INF && reg.Right() > 0) || (reg.Right() >= MOCK_INF && reg.Left() < 0)) {
788  reg.AddSplit(0);
789  }
790 
791  if (reg.NSplit() > 0) {
792  std::vector<IntRegion<UfResult> > children;
793  reg.SubDivide(&children);
794  integ_dbg2 << "Subdivided into " << children.size() << " children\n";
795  UfResult answer = UfResult();
796  UfResult err = 0;
797  for (size_t i = 0; i<children.size(); i++) {
798  IntRegion<UfResult>& child = children[i];
799  integ_dbg2 << "i = " << i;
800  integ_dbg2 << ": bounds = " << child.Left() << ", " << child.Right() << std::endl;
801  answer += int1d(func, child, abserr, relerr);
802  err += child.Err();
803  integ_dbg2 << "subint = " << child.Area() << " +- " << child.Err() << std::endl;
804  }
805  reg.SetArea(answer, err);
806  return answer;
807 
808  } else {
809  if (reg.Left() <= -MOCK_INF) {
810  integ_dbg2 << "left = -infinity, right = " << reg.Right() << std::endl;
811  assert(reg.Right() <= 0.0);
812  IntRegion<UfResult> modreg(1.0/(reg.Right() - 1.0), 0.0, reg.getDbgout());
813  intGKP(Aux2<UF>(func), modreg, abserr, relerr);
814  reg.SetArea(modreg.Area(), modreg.Err());
815  } else if (reg.Right() >= MOCK_INF) {
816  integ_dbg2 << "left = " << reg.Left() << ", right = infinity\n";
817  assert(reg.Left() >= 0.0);
818  IntRegion<UfResult> modreg(0.0, 1.0/(reg.Left() + 1.0), reg.getDbgout());
819  intGKP(Aux1<UF>(func), modreg, abserr, relerr);
820  reg.SetArea(modreg.Area(), modreg.Err());
821  } else {
822  integ_dbg2 << "left = " << reg.Left();
823  integ_dbg2 << ", right = " << reg.Right() << std::endl;
824  intGKP(func, reg, abserr, relerr);
825  }
826  integ_dbg2 << "done int1d answer = " << reg.Area();
827  integ_dbg2 << " +- " << reg.Err() << std::endl;
828  return reg.Area();
829  }
830 }
831 
832 
836 template <class BF, class YREG>
837 inline typename BF::result_type int2d(
838  BF const &func,
840  YREG const &yreg,
841  typename BF::result_type const &abserr = DEFABSERR,
842  typename BF::result_type const &relerr = DEFRELERR) {
843 
844  using namespace details;
845  integ_dbg2 << "Starting int2d: range = ";
846  integ_dbg2 << reg.Left() << ".." << reg.Right() << std::endl;
847  Int2DAuxType<BF, YREG> faux(func, yreg, abserr*1.0e-3, relerr*1.0e-3);
848  typename BF::result_type answer = int1d(faux, reg, abserr, relerr);
849  integ_dbg2 << "done int2d answer = " << answer << " +- " << reg.Err() << std::endl;
850  return answer;
851 }
852 
856 template <class TF, class YREG, class ZREG>
857 inline typename TF::result_type int3d(
858  TF const &func,
860  YREG const &yreg,
861  ZREG const &zreg,
862  typename TF::result_type const &abserr = DEFABSERR,
863  typename TF::result_type const &relerr = DEFRELERR) {
864 
865  using namespace details;
866  integ_dbg2 << "Starting int3d: range = ";
867  integ_dbg2 << reg.Left() << ".." << reg.Right() << std::endl;
868  Int3DAuxType<TF, YREG, ZREG> faux(func, yreg, zreg, abserr*1.e-3, relerr*1.e-3);
869  typename TF::result_type answer = int1d(faux, reg, abserr, relerr);
870  integ_dbg2 << "done int3d answer = " << answer << " +- " << reg.Err() << std::endl;
871  return answer;
872 }
873 
877 template <class BF>
878 inline typename BF::result_type int2d(
879  BF const &func,
882  typename BF::result_type const &abserr = DEFABSERR,
883  typename BF::result_type const &relerr = DEFRELERR) {
884  using namespace details;
885  return int2d(func, reg, ConstantReg1<typename BF::result_type>(yreg), abserr, relerr);
886 }
887 
891 template <class TF>
892 inline typename TF::result_type int3d(
893  TF const &func,
897  typename TF::result_type const &abserr = DEFABSERR,
898  typename TF::result_type const &relerr = DEFRELERR) {
899  using namespace details;
900  return int3d(func, reg, ConstantReg1<typename TF::result_type>(yreg),
901  ConstantReg2<typename TF::result_type>(zreg), abserr, relerr);
902 }
903 
904 
905 
906 
907 
908 // =============================================================
916 template<typename UnaryFunctionT>
917 typename UnaryFunctionT::result_type integrate(UnaryFunctionT func,
918  typename UnaryFunctionT::argument_type const a,
919  typename UnaryFunctionT::argument_type const b,
920  double eps = 1.0e-6) {
921 
922  typedef typename UnaryFunctionT::argument_type Arg;
923  IntRegion<Arg> region(a, b);
924 
925  return int1d(func, region, DEFABSERR, eps);
926 }
927 
928 
929 
930 namespace details {
931 
945 template<typename BinaryFunctionT>
947  public std::unary_function<typename BinaryFunctionT::second_argument_type,
948  typename BinaryFunctionT::result_type> {
949 public:
950  FunctionWrapper(BinaryFunctionT func,
951  typename BinaryFunctionT::first_argument_type const x1,
952  typename BinaryFunctionT::first_argument_type const x2,
953  double const eps = 1.0e-6) :
954  _func(func), _x1(x1), _x2(x2), _eps(eps) {}
955  typename BinaryFunctionT::result_type operator()(typename
956  BinaryFunctionT::second_argument_type const y) const {
957  return integrate(std::bind2nd(_func, y), _x1, _x2, _eps);
958  }
959 private:
960  BinaryFunctionT _func;
961  typename BinaryFunctionT::first_argument_type _x1, _x2;
962  double _eps;
963 };
964 
965 } // end of namespace afw::math::details
966 
967 
968 
969 // =============================================================
976 template<typename BinaryFunctionT>
977 typename BinaryFunctionT::result_type integrate2d(BinaryFunctionT func,
978  typename BinaryFunctionT::first_argument_type const x1,
979  typename BinaryFunctionT::first_argument_type const x2,
980  typename BinaryFunctionT::second_argument_type const y1,
981  typename BinaryFunctionT::second_argument_type const y2,
982  double eps = 1.0e-6) {
983  using namespace details;
984  // note the more stringent eps requirement to ensure the requested limit
985  // can be reached.
986  FunctionWrapper<BinaryFunctionT> fwrap(func, x1, x2, eps);
987  return integrate(fwrap, y1, y2, eps);
988 }
989 
990 
991 }}} // end namespaces lsst/afw/math
992 
993 #endif
int y
void SetArea(const T &a, const T &e)
Definition: Integrate.h:245
BF::result_type int2d(BF const &func, IntRegion< typename BF::result_type > &reg, YREG const &yreg, typename BF::result_type const &abserr=DEFABSERR, typename BF::result_type const &relerr=DEFRELERR)
Front end for the 2d integrator.
Definition: Integrate.h:837
void AddSplit(const T x)
Definition: Integrate.h:238
Auxiliary struct 1.
Definition: Integrate.h:603
TF::firstof3_argument_type _value
Definition: Integrate.h:732
BinaryFunctionT::first_argument_type _x1
Definition: Integrate.h:961
BF::result_type operator()(typename BF::first_argument_type x) const
Definition: Integrate.h:699
double const DEFRELERR
Definition: Integrate.h:261
binder2_1(const BF &oper, typename BF::first_argument_type val)
Definition: Integrate.h:672
binder2_1< BF > bind21(const BF &oper, const Tp &x)
Definition: Integrate.h:685
BinaryFunctionT::result_type integrate2d(BinaryFunctionT func, typename BinaryFunctionT::first_argument_type const x1, typename BinaryFunctionT::first_argument_type const x2, typename BinaryFunctionT::second_argument_type const y1, typename BinaryFunctionT::second_argument_type const y2, double eps=1.0e-6)
The 2D integrator.
Definition: Integrate.h:977
#define integ_dbg3
Definition: Integrate.h:184
BF::first_argument_type _value
Definition: Integrate.h:681
T norm(const T &x)
Definition: Integrate.h:191
BinaryFunctionT::result_type operator()(typename BinaryFunctionT::second_argument_type const y) const
Definition: Integrate.h:955
ConstantReg2(IntRegion< T > const &r)
Definition: Integrate.h:660
UF::result_type operator()(typename UF::argument_type x) const
Definition: Integrate.h:607
bool operator>(IntRegion< T > const &r2) const
Definition: Integrate.h:209
ConstantReg1(IntRegion< T > const &r)
Definition: Integrate.h:652
AuxFunc1< UF > Aux1(UF uf)
Auxiliary function 1.
Definition: Integrate.h:620
binder3_1(const TF &oper, typename TF::firstof3_argument_type val)
Definition: Integrate.h:722
TF::result_type operator()(typename TF::firstof3_argument_type x) const
Definition: Integrate.h:752
std::vector< T > _splitpoints
Definition: Integrate.h:254
UF::result_type int1d(UF const &func, IntRegion< typename UF::result_type > &reg, typename UF::result_type const &abserr=DEFABSERR, typename UF::result_type const &relerr=DEFRELERR)
Front end for the 1d integrator.
Definition: Integrate.h:776
BF::result_type operator()(const typename BF::second_argument_type &x) const
Definition: Integrate.h:676
double const MOCK_INF
Definition: Integrate.h:175
BinaryFunctionT::first_argument_type _x2
Definition: Integrate.h:961
TF::result_type operator()(typename TF::secondof3_argument_type const &x1, typename TF::thirdof3_argument_type const &x2) const
Definition: Integrate.h:726
std::ostream * getDbgout()
Definition: Integrate.h:250
AuxFunc2< UF > Aux2(UF uf)
Auxiliary function 2.
Definition: Integrate.h:641
binder3_1< TF > bind31(const TF &oper, const Tp &x)
Definition: Integrate.h:737
FunctionWrapper(BinaryFunctionT func, typename BinaryFunctionT::first_argument_type const x1, typename BinaryFunctionT::first_argument_type const x2, double const eps=1.0e-6)
Definition: Integrate.h:950
double const DEFABSERR
Definition: Integrate.h:260
IntRegion(T const a, T const b, std::ostream *dbgout=0)
Definition: Integrate.h:205
void SubDivide(std::vector< IntRegion< T > > *children)
Definition: Integrate.h:211
UF::result_type operator()(typename UF::argument_type x) const
Definition: Integrate.h:628
size_t NSplit() const
Definition: Integrate.h:239
int x
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
Int3DAuxType(const TF &func, const YREG &yreg, const ZREG &zreg, const typename TF::result_type &abserr, const typename TF::result_type &relerr)
Definition: Integrate.h:747
T const & Left() const
Definition: Integrate.h:241
T const & Right() const
Definition: Integrate.h:242
Int2DAuxType(BF const &func, YREG const &yreg, typename BF::result_type const &abserr, typename BF::result_type const &relerr)
Definition: Integrate.h:694
#define integ_dbg2
Definition: Integrate.h:183
IntRegion< T > operator()(T x, T y) const
Definition: Integrate.h:661
bool intGKPNA(UF const &func, IntRegion< typename UF::result_type > &reg, typename UF::result_type const epsabs, typename UF::result_type const epsrel, std::map< typename UF::result_type, typename UF::result_type > *fxmap=0)
Non-adaptive integration of the function f over the region &#39;reg&#39;.
Definition: Integrate.h:316
afw::table::Key< double > b
T const & Area() const
Definition: Integrate.h:244
T const & Err() const
Definition: Integrate.h:243
TF::result_type int3d(TF const &func, IntRegion< typename TF::result_type > &reg, YREG const &yreg, ZREG const &zreg, typename TF::result_type const &abserr=DEFABSERR, typename TF::result_type const &relerr=DEFRELERR)
Front end for the 3d integrator.
Definition: Integrate.h:857
T rescale_error(T err, T const &resabs, T const &resasc)
Definition: Integrate.h:280
IntRegion< T > operator()(T) const
Definition: Integrate.h:653
ImageT val
Definition: CR.cc:154
T real(const T &x)
Definition: Integrate.h:193
void intGKP(UF const &func, IntRegion< typename UF::result_type > &reg, typename UF::result_type const epsabs, typename UF::result_type const epsrel, std::map< typename UF::result_type, typename UF::result_type > *fxmap=0)
An adaptive integration algorithm which computes the integral of f over the region reg...
Definition: Integrate.h:450
std::ostream *const dbgout
Definition: dbg.h:64
Wrap an integrand in a call to a 1D integrator: romberg()
Definition: Integrate.h:946
UnaryFunctionT::result_type integrate(UnaryFunctionT func, typename UnaryFunctionT::argument_type const a, typename UnaryFunctionT::argument_type const b, double eps=1.0e-6)
The 1D integrator.
Definition: Integrate.h:917
Include files required for standard LSST Exception handling.
std::ostream * _dbgout
Definition: Integrate.h:255
Helpers for constant regions for int2d, int3d:
Definition: Integrate.h:650