25#if !defined(LSST_AFW_MATH_INTEGRATE_H)
26#define LSST_AFW_MATH_INTEGRATE_H 1
141 if (false) (*_dbgout)
143 if (false) (*(reg.getDbgout()))
145 if (false) (*(tempreg.getDbgout()))
148 if (_dbgout) (*_dbgout)
150 if (reg.getDbgout()) (*(reg.getDbgout()))
152 if (tempreg.getDbgout()) (*(tempreg.getDbgout()))
178 : _a(
a), _b(
b), _error(0.0), _area(0), _dbgout(dbgout) {}
190 assert(children->size() == 0);
191 if (_splitpoints.size() == 0) {
194 if (_splitpoints.size() > 1) {
195 std::sort(_splitpoints.begin(), _splitpoints.end());
199 if (_a > _splitpoints[0] || _b < _splitpoints.back()) {
202 for (
size_t i = 0; i<_splitpoints.size(); i++) {
208 assert(_splitpoints[0] >= _a);
209 assert(_splitpoints.back() <= _b);
210 children->push_back(
IntRegion<T>(_a, _splitpoints[0], _dbgout));
211 for (
size_t i = 1; i < _splitpoints.size(); i++) {
212 children->push_back(
IntRegion<T>(_splitpoints[i - 1], _splitpoints[i], _dbgout));
214 children->push_back(
IntRegion<T>(_splitpoints.back(), _b, _dbgout));
217 void Bisect() { _splitpoints.push_back((_a + _b) / 2.0); }
219 size_t NSplit()
const {
return _splitpoints.size(); }
221 T const &
Left()
const {
return _a; }
223 T const &
Err()
const {
return _error; }
224 T const &
Area()
const {
return _area; }
233 T _a, _b, _error, _area;
254inline Quad Epsilon<Quad>() {
255 return 3.08148791094e-33;
258inline Quad MinRep<Quad>() {
259 return 2.2250738585072014e-308;
265 if (resasc != 0.0 && err != 0.0) {
266 T const scale = (200.0 * err / resasc);
273 if (resabs > MinRep<T>() / (50.0 * Epsilon<T>())) {
274 T const min_err = 50.0 * Epsilon<T>() * resabs;
297template <
typename UnaryFunctionT,
typename Arg>
299 Arg
const epsabs, Arg
const epsrel,
301 Arg
const a = reg.
Left();
302 Arg
const b = reg.
Right();
304 Arg
const halfLength = 0.5 * (
b -
a);
305 Arg
const absHalfLength = fabs(halfLength);
306 Arg
const center = 0.5 * (
b +
a);
307 Arg
const fCenter = func(center);
312 assert(gkp_wb<Arg>(0).size() == gkp_x<Arg>(0).size() + 1);
313 Arg area1 = gkp_wb<Arg>(0).back() * fCenter;
315 fv1.
reserve(2 * gkp_x<Arg>(0).size() + 1);
316 fv2.
reserve(2 * gkp_x<Arg>(0).size() + 1);
317 for (
size_t k = 0; k < gkp_x<Arg>(0).size(); k++) {
318 Arg
const abscissa = halfLength * gkp_x<Arg>(0)[k];
319 Arg
const fval1 = func(center - abscissa);
320 Arg
const fval2 = func(center + abscissa);
321 area1 += gkp_wb<Arg>(0)[k] * (fval1 + fval2);
325 (*fxmap)[center - abscissa] = fval1;
326 (*fxmap)[center + abscissa] = fval2;
330 nfeval += gkp_x<Arg>(0).size() * 2;
336 bool calcabsasc =
true;
337 Arg resabs = 0.0, resasc = 0.0;
338 for (
int level = 1; level < NGKPLEVELS; level++) {
339 assert(gkp_wa<Arg>(level).size() == fv1.
size());
340 assert(gkp_wa<Arg>(level).size() == fv2.
size());
341 assert(gkp_wb<Arg>(level).size() == gkp_x<Arg>(level).size() + 1);
342 Arg area2 = gkp_wb<Arg>(level).back() * fCenter;
345 resabs = fabs(area2);
347 for (
size_t k = 0; k < fv1.
size(); k++) {
348 area2 += gkp_wa<Arg>(level)[k] * (fv1[k] + fv2[k]);
350 resabs += gkp_wa<Arg>(level)[k] * (fabs(fv1[k]) + fabs(fv2[k]));
353 for (
size_t k = 0; k < gkp_x<Arg>(level).size(); k++) {
354 Arg
const abscissa = halfLength * gkp_x<Arg>(level)[k];
355 Arg
const fval1 = func(center - abscissa);
356 Arg
const fval2 = func(center + abscissa);
357 Arg
const fval = fval1 + fval2;
358 area2 += gkp_wb<Arg>(level)[k] * fval;
360 resabs += gkp_wb<Arg>(level)[k] * (fabs(fval1) + fabs(fval2));
365 (*fxmap)[center - abscissa] = fval1;
366 (*fxmap)[center + abscissa] = fval2;
370 nfeval += gkp_x<Arg>(level).size() * 2;
373 Arg
const mean = area1 * Arg(0.5);
375 resasc = gkp_wb<Arg>(level).back() * fabs(fCenter - mean);
376 for (
size_t k = 0; k < gkp_wa<Arg>(level).size(); k++) {
377 resasc += gkp_wa<Arg>(level)[k] * (fabs(fv1[k] - mean) + fabs(fv2[k] - mean));
379 for (
size_t k = 0; k < gkp_x<Arg>(level).size(); k++) {
380 resasc += gkp_wb<Arg>(level)[k] * (fabs(fv1[k] - mean) + fabs(fv2[k] - mean));
382 resasc *= absHalfLength;
383 resabs *= absHalfLength;
391 integ_dbg2 <<
"at level " << level <<
" area2 = " << area2;
395 if (err < epsabs || err < epsrel * fabs(area2)) {
405 integ_dbg2 <<
"Failed to reach tolerance with highest-order GKP rule";
425template <
typename UnaryFunctionT,
typename Arg>
427 Arg
const epsabs, Arg
const epsrel,
431 assert(epsabs >= 0.0);
432 assert(epsrel > 0.0);
435 bool done =
intGKPNA(func, reg, epsabs, epsrel, fxmap);
438 integ_dbg2 <<
"In adaptive GKP, failed first pass... subdividing\n";
441 int roundoffType1 = 0, errorType = 0;
442 Arg roundoffType2 = 0;
443 size_t iteration = 1;
446 allregions.
push(reg);
447 Arg finalarea = reg.
Area();
448 Arg finalerr = reg.
Err();
449 Arg tolerance =
std::max(epsabs, epsrel * fabs(finalarea));
450 assert(finalerr > tolerance);
452 while (!errorType && finalerr > tolerance) {
454 integ_dbg2 <<
"Current answer = " << finalarea <<
" +- " << finalerr;
455 integ_dbg2 <<
" (tol = " << tolerance <<
")\n";
458 integ_dbg2 <<
"Subdividing largest error region ";
468 Arg factor = 3 * children.
size() * finalerr / tolerance;
469 Arg newepsabs = fabs(parent.
Err() / factor);
470 Arg newepsrel = newepsabs / fabs(parent.
Area());
471 integ_dbg2 <<
"New epsabs, rel = " << newepsabs <<
", " << newepsrel;
474 Arg newarea = Arg(0.0);
476 for (
size_t i = 0; i < children.
size(); i++) {
481 hasConverged =
intGKPNA(func, child, newepsabs, newepsrel);
482 integ_dbg2 <<
"child (" << i + 1 <<
'/' << children.
size() <<
") ";
490 newarea += child.
Area();
491 newerror += child.
Err();
493 integ_dbg2 <<
"Compare: newerr = " << newerror;
496 finalerr += (newerror - parent.
Err());
497 finalarea += newarea - parent.
Area();
499 Arg delta = parent.
Area() - newarea;
500 if (newerror <= parent.
Err() && fabs(delta) <= parent.
Err() && newerror >= 0.99 * parent.
Err()) {
501 integ_dbg2 <<
"roundoff type 1: delta/newarea = ";
506 if (iteration >= 10 && newerror > parent.
Err() && fabs(delta) <= newerror - parent.
Err()) {
507 integ_dbg2 <<
"roundoff type 2: newerror/error = ";
509 roundoffType2 +=
std::min(newerror / parent.
Err() - 1.0, Arg(1.0));
512 tolerance =
std::max(epsabs, epsrel * fabs(finalarea));
513 if (finalerr > tolerance) {
514 if (roundoffType1 >= 200) {
518 if (roundoffType2 >= 200.0) {
522 if (fabs((parent.
Right() - parent.
Left()) / (reg.
Right() - reg.
Left())) < Epsilon<double>()) {
527 for (
size_t i = 0; i < children.
size(); i++) {
528 allregions.
push(children[i]);
536 while (!allregions.
empty()) {
538 finalarea += r.
Area();
542 reg.
SetArea(finalarea, finalerr);
544 if (errorType == 1) {
546 s <<
"Type 1 roundoff's = " << roundoffType1;
547 s <<
", Type 2 = " << roundoffType2 <<
std::endl;
548 s <<
"Roundoff error 1 prevents tolerance from being achieved ";
551 }
else if (errorType == 2) {
553 s <<
"Type 1 roundoff's = " << roundoffType1;
554 s <<
", Type 2 = " << roundoffType2 <<
std::endl;
555 s <<
"Roundoff error 2 prevents tolerance from being achieved ";
558 }
else if (errorType == 3) {
560 s <<
"Bad integrand behavior found in the integration interval ";
570template <
typename UnaryFunctionT>
574 template <
typename Arg>
576 return _f(1.0 /
x - 1.0) / (
x *
x);
579 UnaryFunctionT
const &_f;
591template <
typename UnaryFunctionT>
595 template <
typename Arg>
597 return _f(1.0 /
x + 1.0) / (
x *
x);
600 UnaryFunctionT
const &_f;
618 typename BF::result_type
operator()(
const typename BF::second_argument_type &
x)
const {
627template <
class BF,
class Tp>
629 using Arg =
typename BF::first_argument_type;
633template <
class BF,
class YREG>
636 Int2DAuxType(BF
const &func, YREG
const &yreg,
typename BF::result_type
const &abserr,
637 typename BF::result_type
const &relerr)
638 : _func(func), _yreg(yreg), _abserr(abserr), _relerr(relerr) {}
640 typename BF::result_type
operator()(
typename BF::first_argument_type
x)
const {
641 typename YREG::result_type tempreg = _yreg(
x);
651 typename BF::result_type _abserr, _relerr;
657 typename TF::thirdof3_argument_type, typename TF::result_type> {
660 typename TF::result_type
operator()(
typename TF::secondof3_argument_type
const &x1,
661 typename TF::thirdof3_argument_type
const &x2)
const {
667 typename TF::firstof3_argument_type
_value;
670template <
class TF,
class Tp>
672 using Arg =
typename TF::firstof3_argument_type;
676template <
class TF,
class YREG,
class ZREG>
678 :
public std::unary_function<typename TF::firstof3_argument_type, typename TF::result_type> {
680 Int3DAuxType(
const TF &func,
const YREG &yreg,
const ZREG &zreg,
const typename TF::result_type &abserr,
681 const typename TF::result_type &relerr)
682 : _func(func), _yreg(yreg), _zreg(zreg), _abserr(abserr), _relerr(relerr) {}
684 typename TF::result_type
operator()(
typename TF::firstof3_argument_type
x)
const {
685 typename YREG::result_type tempreg = _yreg(
x);
686 typename TF::result_type
result =
687 int2d(
bind31(_func,
x), tempreg,
bind21(_zreg,
x), _abserr, _relerr);
697 typename TF::result_type _abserr, _relerr;
705template <
typename UnaryFunctionT,
typename Arg>
720 integ_dbg2 <<
"Subdivided into " << children.
size() <<
" children\n";
723 for (
size_t i = 0; i < children.
size(); i++) {
727 answer +=
int1d(func, child, abserr, relerr);
737 assert(reg.
Right() <= 0.0);
739 intGKP(Aux2<UnaryFunctionT>(func), modreg, abserr, relerr);
743 assert(reg.
Left() >= 0.0);
745 intGKP(Aux1<UnaryFunctionT>(func), modreg, abserr, relerr);
750 intGKP(func, reg, abserr, relerr);
766template <
typename UnaryFunctionT,
typename Arg>
770 double eps = 1.0e-6) {
780template <
typename BinaryFunctionT,
typename X,
typename Y>
782 auto outer = [func, x1, x2, eps](
auto y) {
783 auto inner = [func,
y](
auto x) {
return func(
x,
y); };
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Int2DAuxType(BF const &func, YREG const &yreg, typename BF::result_type const &abserr, typename BF::result_type const &relerr)
BF::result_type operator()(typename BF::first_argument_type x) const
Int3DAuxType(const TF &func, const YREG &yreg, const ZREG &zreg, const typename TF::result_type &abserr, const typename TF::result_type &relerr)
TF::result_type operator()(typename TF::firstof3_argument_type x) const
BF::first_argument_type _value
BF::result_type operator()(const typename BF::second_argument_type &x) const
binder2_1(const BF &oper, typename BF::first_argument_type val)
TF::firstof3_argument_type _value
binder3_1(const TF &oper, typename TF::firstof3_argument_type val)
TF::result_type operator()(typename TF::secondof3_argument_type const &x1, typename TF::thirdof3_argument_type const &x2) const
Reports errors that are due to events beyond the control of the program.
def scale(algorithm, min, max=None, frame=None)
bool intGKPNA(UnaryFunctionT func, IntRegion< Arg > ®, Arg const epsabs, Arg const epsrel, std::map< Arg, Arg > *fxmap=nullptr)
Non-adaptive integration of the function f over the region 'reg'.
binder3_1< TF > bind31(const TF &oper, const Tp &x)
void intGKP(UnaryFunctionT func, IntRegion< Arg > ®, Arg const epsabs, Arg const epsrel, std::map< Arg, Arg > *fxmap=nullptr)
An adaptive integration algorithm which computes the integral of f over the region reg.
AuxFunc2< UF > Aux2(UF uf)
Auxiliary function 2.
T rescale_error(T err, T const &resabs, T const &resasc)
AuxFunc1< UF > Aux1(UF uf)
Auxiliary function 1.
binder2_1< BF > bind21(const BF &oper, const Tp &x)
Arg int1d(UnaryFunctionT func, IntRegion< Arg > ®, Arg const &abserr=DEFABSERR, Arg const &relerr=DEFRELERR)
Front end for the 1d integrator.
auto integrate(UnaryFunctionT func, Arg const a, Arg const b, double eps=1.0e-6)
The 1D integrator.
auto integrate2d(BinaryFunctionT func, X x1, X x2, Y y1, Y y2, double eps=1.0e-6)
The 2D integrator.
A base class for image defects.
std::ostream * getDbgout()
void SetArea(const T &a, const T &e)
IntRegion(IntRegion &&)=default
bool operator>(IntRegion< T > const &r2) const
IntRegion & operator=(IntRegion &&)=default
IntRegion(T const a, T const b, std::ostream *dbgout=nullptr)
IntRegion(IntRegion const &)=default
void SubDivide(std::vector< IntRegion< T > > *children)
bool operator<(IntRegion< T > const &r2) const
IntRegion & operator=(IntRegion const &)=default
AuxFunc1(UnaryFunctionT const &f)
auto operator()(Arg x) const
auto operator()(Arg x) const
AuxFunc2(UnaryFunctionT const &f)