Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| /** | |
| * \file Integrator.h | |
| * This file contains routines for numerical integration using Gauss-Kronrod quadrature. | |
| * \sa R Piessens, E de Doncker-Kapenger, C Ueberhuber, D Kahaner, QUADPACK, A Subroutine Package | |
| * for Automatic Integration, Springer Verlag, 1983. | |
| */ | |
| #ifndef EIGEN_INTEGRATOR_H | |
| #define EIGEN_INTEGRATOR_H | |
| namespace Numer | |
| { | |
| /** | |
| * \ingroup Quadrature_Module | |
| * | |
| * \brief This class provides numerical integration functionality. | |
| * | |
| * Memory management and additional information (e.g. number of function calls, estimated error), | |
| * are provided by the class in a way that can support the future porting of additional quadrature | |
| * functions from the QUADPACK library. | |
| * | |
| * \todo Ensure only appropriates types are used for Scalar_, e.g. prohibit integers. | |
| */ | |
| template <typename Scalar_> | |
| class Integrator | |
| { | |
| public: | |
| typedef Scalar_ Scalar; | |
| typedef Eigen::DenseIndex DenseIndex; | |
| /** | |
| * \brief The local Gauss-Kronrod quadrature rule to use. | |
| */ | |
| enum QuadratureRule | |
| { | |
| GaussKronrod15 = 1, /**< Use 7, 15 points. */ | |
| GaussKronrod21 = 2, /**< Use 10, 21 points. */ | |
| GaussKronrod31 = 3, /**< Use 15, 31 points. */ | |
| GaussKronrod41 = 4, /**< Use 20, 41 points. */ | |
| GaussKronrod51 = 5, /**< Use 25, 51 points. */ | |
| GaussKronrod61 = 6, /**< Use 30, 61 points. */ | |
| GaussKronrod71 = 7, /**< Use 35, 71 points. */ | |
| GaussKronrod81 = 8, /**< Use 40, 81 points. */ | |
| GaussKronrod91 = 9, /**< Use 45, 91 points. */ | |
| GaussKronrod101 = 10, /**< Use 50, 101 points. */ | |
| GaussKronrod121 = 11, /**< Use 60, 121 points. */ | |
| GaussKronrod201 = 12 /**< Use 100, 201 points. */ | |
| }; | |
| /** | |
| * \brief Prepares an Integrator for a call to a quadrature function. | |
| * | |
| * \param[in] maxSubintervals The maximum number of subintervals allowed in the subdivision process | |
| * of quadrature functions. This corresponds to the amount of memory allocated for said | |
| * functions. | |
| */ | |
| Integrator(const int maxSubintervals) | |
| : m_maxSubintervals(maxSubintervals) | |
| { | |
| assert(maxSubintervals >= 1); // \todo use Eigen assert. | |
| m_errorListIndices.resize(maxSubintervals, 1); | |
| m_lowerList.resize(maxSubintervals, 1); | |
| m_upperList.resize(maxSubintervals, 1); | |
| m_integralList.resize(maxSubintervals, 1); | |
| m_errorList.resize(maxSubintervals, 1); | |
| } | |
| /** | |
| * \brief This function calculates an approximation I' to a given definite integral I, the | |
| * integral of f from lowerLimit to upperLimit, hopefully satisfying | |
| * abs(I - I') <= max(desiredAbsoluteError, desiredRelativeError * abs(I)). | |
| * | |
| * This function is best suited for integrands without singularities or discontinuities, which | |
| * are too difficult for non-adaptive quadrature, and, in particular, for integrands with | |
| * oscillating behavior of a non-specific type. | |
| * | |
| * \param[in,out] f The function defining the integrand function. | |
| * \param[in] lowerLimit The lower limit of integration. | |
| * \param[in] upperLimit The upper limit of integration. | |
| * \param[in] desiredAbsoluteError The absolute accuracy requested. | |
| * \param[in] desiredRelativeError The relative accuracy requested. | |
| * If desiredAbsoluteError <= 0 and desiredRelativeError < 50 * machinePrecision, | |
| * the routine will end with errorCode = 6. | |
| * \param[in] quadratureRule The local Gauss-Kronrod quadrature rule to use. | |
| * | |
| * \returns The approximation to the integral. | |
| */ | |
| template <typename FunctionType> | |
| Scalar quadratureAdaptive( | |
| const FunctionType& f, const Scalar lowerLimit, const Scalar upperLimit, | |
| const Scalar desiredAbsoluteError = Scalar(0.), const Scalar desiredRelativeError = Scalar(0.), | |
| const QuadratureRule quadratureRule = Integrator<Scalar>::QuadratureRule(1)) | |
| { | |
| if ((desiredAbsoluteError <= Scalar(0.) && desiredRelativeError < Eigen::NumTraits<Scalar>::epsilon()) | |
| || m_maxSubintervals < 1) | |
| { | |
| m_errorCode = 6; | |
| return Scalar(0.); | |
| } | |
| m_errorCode = 0; | |
| m_numEvaluations = 0; | |
| m_lowerList[0] = lowerLimit; | |
| m_upperList[0] = upperLimit; | |
| m_integralList[0] = Scalar(0.); | |
| m_errorList[0] = Scalar(0.); | |
| m_errorListIndices[0] = 0; | |
| m_errorListIndices[1] = 1; | |
| Scalar absDiff; | |
| Scalar absResult; | |
| // First approximation to the integral | |
| Scalar integral = quadratureKronrod( | |
| f, lowerLimit, upperLimit, m_estimatedError, absDiff, absResult, quadratureRule); | |
| m_numSubintervals = 1; | |
| m_integralList[0] = integral; | |
| m_errorList[0] = m_estimatedError; | |
| // Test on accuracy. | |
| using std::abs; | |
| Scalar errorBound = (std::max)(desiredAbsoluteError, desiredRelativeError * abs(integral)); | |
| if (m_maxSubintervals == 1) | |
| { | |
| m_errorCode = 1; | |
| } | |
| else if (m_estimatedError <= Eigen::NumTraits<Scalar>::epsilon() * Scalar(50.) * absDiff | |
| && m_estimatedError > errorBound) | |
| { | |
| m_errorCode = 2; | |
| } | |
| if (m_errorCode != 0 | |
| || (m_estimatedError <= errorBound && m_estimatedError != absResult) | |
| || m_estimatedError == Scalar(0.)) | |
| { | |
| if (quadratureRule == GaussKronrod15) | |
| { | |
| m_numEvaluations = m_numEvaluations * 30 + 15; | |
| } | |
| else | |
| { | |
| m_numEvaluations = (quadratureRule * 10 + 1) * (m_numEvaluations * 2 + 1); | |
| } | |
| return integral; | |
| } | |
| // The sum of the integrals over the subintervals. | |
| Scalar area = integral; | |
| Scalar errorSum = m_estimatedError; | |
| // The maximum interval error. | |
| Scalar errorMax = m_estimatedError; | |
| // An index into m_errorList at the interval with largest error estimate. | |
| DenseIndex maxErrorIndex = 0; | |
| DenseIndex nrMax = 0; | |
| int roundOff1 = 0; | |
| int roundOff2 = 0; | |
| // Main loop for the integration | |
| for (m_numSubintervals = 2; m_numSubintervals <= m_maxSubintervals; ++m_numSubintervals) | |
| { | |
| const DenseIndex numSubintervalsIndex = m_numSubintervals - 1; | |
| // Bisect the subinterval with the largest error estimate. | |
| Scalar lower1 = m_lowerList[maxErrorIndex]; | |
| Scalar upper2 = m_upperList[maxErrorIndex]; | |
| Scalar upper1 = (lower1 + upper2) * Scalar(.5); | |
| Scalar lower2 = upper1; | |
| Scalar error1; | |
| Scalar error2; | |
| Scalar absDiff1; | |
| Scalar absDiff2; | |
| const Scalar area1 = quadratureKronrod( | |
| f, lower1, upper1, error1, absResult, absDiff1, quadratureRule); | |
| const Scalar area2 = quadratureKronrod( | |
| f, lower2, upper2, error2, absResult, absDiff2, quadratureRule); | |
| // Improve previous approximations to integral and error and test for accuracy. | |
| ++(m_numEvaluations); | |
| const Scalar area12 = area1 + area2; | |
| const Scalar error12 = error1 + error2; | |
| errorSum += error12 - errorMax; | |
| area += area12 - m_integralList[maxErrorIndex]; | |
| if (absDiff1 != error1 && absDiff2 != error2) | |
| { | |
| if (abs(m_integralList[maxErrorIndex] - area12) <= abs(area12) * Scalar(1.e-5) | |
| && error12 >= errorMax * Scalar(.99)) | |
| { | |
| ++roundOff1; | |
| } | |
| if (m_numSubintervals > 10 && error12 > errorMax) | |
| { | |
| ++roundOff2; | |
| } | |
| } | |
| m_integralList[maxErrorIndex] = area1; | |
| m_integralList[numSubintervalsIndex] = area2; | |
| errorBound = (std::max)(desiredAbsoluteError, desiredRelativeError * abs(area)); | |
| if (errorSum > errorBound) | |
| { | |
| // Set error flag in the case that the number of subintervals has reached the max allowable. | |
| if (m_numSubintervals == m_maxSubintervals) | |
| { | |
| m_errorCode = 1; | |
| } | |
| // Test for roundoff error and set error flag. | |
| else if (roundOff1 >= 6 || roundOff2 >= 20) | |
| { | |
| m_errorCode = 2; | |
| } | |
| // Set m_error_code in the case of poor integrand behaviour within | |
| // the integration range. | |
| else if ((std::max)(abs(lower1), abs(upper2)) | |
| <= (Eigen::NumTraits<Scalar>::epsilon() * Scalar(100.) + Scalar(1.)) | |
| * (abs(lower2) + (std::numeric_limits<Scalar>::min)() * Scalar(1.e3) )) | |
| { | |
| m_errorCode = 3; | |
| } | |
| } | |
| // Append the newly-created intervals to the list. | |
| if (error2 > error1) | |
| { | |
| m_lowerList[numSubintervalsIndex] = lower1; | |
| m_lowerList[maxErrorIndex] = lower2; | |
| m_upperList[numSubintervalsIndex] = upper1; | |
| m_integralList[maxErrorIndex] = area2; | |
| m_integralList[numSubintervalsIndex] = area1; | |
| m_errorList[maxErrorIndex] = error2; | |
| m_errorList[numSubintervalsIndex] = error1; | |
| } | |
| else | |
| { | |
| m_lowerList[numSubintervalsIndex] = lower2; | |
| m_upperList[maxErrorIndex] = upper1; | |
| m_upperList[numSubintervalsIndex] = upper2; | |
| m_errorList[maxErrorIndex] = error1; | |
| m_errorList[numSubintervalsIndex] = error2; | |
| } | |
| // Maintain the descending ordering in the list of error estimates and select the subinterval | |
| // with the largest error estimate, (the next subinterval to be bisected). | |
| quadratureSort(maxErrorIndex, errorMax,nrMax); | |
| if (m_errorCode != 0 || errorSum <= errorBound || m_numSubintervals == m_maxSubintervals) | |
| { | |
| break; | |
| } | |
| } | |
| integral = Scalar(0.); | |
| for (int k = 0; k < m_numSubintervals; ++k) | |
| { | |
| integral += m_integralList[k]; | |
| } | |
| m_estimatedError = errorSum; | |
| if (quadratureRule == GaussKronrod15) | |
| { | |
| m_numEvaluations = m_numEvaluations * 30 + 15; | |
| } | |
| else | |
| { | |
| m_numEvaluations = (quadratureRule * 10 + 1) * (m_numEvaluations * 2 + 1); | |
| } | |
| return integral; | |
| } | |
| /** | |
| * \brief Returns the estimated absolute error from the last integration. | |
| * | |
| * \returns The value returned will only be valid after calling quadratureAdaptive at least once. | |
| */ | |
| inline Scalar estimatedError() const {return m_estimatedError;} | |
| /** | |
| * \brief Returns the error code. | |
| * | |
| * \returns The value returned will only be valid after calling quadratureAdaptive at least once. | |
| */ | |
| inline DenseIndex errorCode() const {return m_errorCode;} | |
| private: | |
| /** | |
| * \brief This routine maintains the descending ordering in the list of the local error | |
| * estimates resulting from the interval subdivision process. | |
| * | |
| * At each call two error estimates are inserted using the sequential search method, top-down | |
| * for the largest error estimate and bottom-up for the smallest error estimate. | |
| * | |
| * \param[in,out] maxErrorIndex The index to the nrMax-th largest error estimate currently in the list. | |
| * \param[in,out] errorMax The nrMax-th largest error estimate. errorMaxIndex = errorList(maxError). | |
| * \param[in,out] nrMax The integer value such that maxError = errorListIndices(nrMax). | |
| */ | |
| void quadratureSort(DenseIndex& maxErrorIndex, Scalar& errorMax, DenseIndex& nrMax) | |
| { | |
| if (m_numSubintervals <= 2) | |
| { | |
| m_errorListIndices[0] = 0; | |
| m_errorListIndices[1] = 1; | |
| maxErrorIndex = m_errorListIndices[nrMax]; | |
| errorMax = m_errorList[maxErrorIndex]; | |
| return; | |
| } | |
| // This part of the routine is only executed if, due to a difficult integrand, subdivision has | |
| // increased the error estimate. In the normal case the insert procedure should start after the | |
| // nrMax-th largest error estimate. | |
| DenseIndex i = 0; | |
| DenseIndex succeed = 0; | |
| const Scalar errorMaximum = m_errorList[maxErrorIndex]; | |
| if (nrMax != 1) | |
| { | |
| for (i = 1; i < nrMax; ++i) | |
| { | |
| succeed = m_errorListIndices[nrMax - 1]; | |
| if (errorMaximum <= m_errorList[succeed]) | |
| { | |
| break; | |
| } | |
| m_errorListIndices[nrMax] = succeed; | |
| --nrMax; | |
| } | |
| } | |
| // Compute the number of elements in the list to be maintained in descending order. This number | |
| // depends on the number of subdivisions remaining allowed. | |
| DenseIndex topBegin = m_numSubintervals - 1; | |
| DenseIndex bottomEnd = topBegin - 1; | |
| DenseIndex start = nrMax + 1; | |
| if (m_numSubintervals > m_maxSubintervals / 2 + 2) | |
| { | |
| topBegin = m_maxSubintervals + 3 - m_numSubintervals + 1; | |
| } | |
| // Insert errorMax by traversing the list top-down, starting comparison from the element | |
| // errorlist(m_errorListIndices(nrMax+1)). | |
| if (start <= bottomEnd) | |
| { | |
| for (i = start; i <= bottomEnd; ++i) | |
| { | |
| succeed = m_errorListIndices[i]; | |
| if (errorMaximum >= m_errorList[succeed]) | |
| { | |
| break; | |
| } | |
| m_errorListIndices[i - 1] = succeed; | |
| } | |
| } | |
| if (start > bottomEnd) | |
| { | |
| m_errorListIndices[bottomEnd] = maxErrorIndex; | |
| m_errorListIndices[topBegin] = m_numSubintervals - 1; | |
| maxErrorIndex = m_errorListIndices[nrMax]; | |
| errorMax = m_errorList[maxErrorIndex]; | |
| return; | |
| } | |
| // Insert errorMin by traversing the list bottom-up. | |
| m_errorListIndices[i - 1] = maxErrorIndex; | |
| DenseIndex tempIndex = bottomEnd; | |
| for (DenseIndex j = i; j <= bottomEnd; ++j) | |
| { | |
| succeed = m_errorListIndices[tempIndex]; | |
| if (m_errorList[m_numSubintervals - 1] < m_errorList[succeed]) | |
| { | |
| m_errorListIndices[tempIndex + 1] = m_numSubintervals - 1; | |
| maxErrorIndex = m_errorListIndices[nrMax]; | |
| errorMax = m_errorList[maxErrorIndex]; | |
| return; | |
| } | |
| m_errorListIndices[tempIndex + 1] = succeed; | |
| --tempIndex; | |
| } | |
| m_errorListIndices[i] = m_numSubintervals - 1; | |
| maxErrorIndex = m_errorListIndices[nrMax]; | |
| errorMax = m_errorList[maxErrorIndex]; | |
| return; | |
| } | |
| /** | |
| * \brief This function calculates an approximation I' to a given definite integral I, the | |
| * integral of f from lowerLimit to upperLimit and provides an error estimate. | |
| * | |
| * \param[in] f The variable representing the function f(x) be integrated. | |
| * \param[in] lowerLimit The lower limit of integration. | |
| * \param[in] upperLimit The upper limit of integration. | |
| * \param[in,out] errorEstimate Estimate of the modulus of the absolute error, not to exceed | |
| * abs(I - I'). | |
| * \param[in,out] absIntegral The approximation to the integral of abs(f) from lowerLimit to | |
| * upperLimit. | |
| * \param[in,out] absDiffIntegral The approximation to the integral of | |
| * abs(f - I/(upperLimit - lowerLimit)). | |
| * | |
| * \returns The approximation I' to the integral I. It is computed by applying the 15, 21, 31, | |
| * 41, 51, 61, 71, 81, 91, 101, 121, 201-point kronrod rule obtained by optimal addition | |
| * of abscissae to the 7, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 100-point Gauss rule. | |
| * | |
| * \detail This series of functions represents a priority queue data structure: | |
| * - Apply Gauss-Kronrod on the whole initial interval and estimate the error. | |
| * - Split the interval symmetrically in two and again apply Gauss-Kronrod on the intervals, | |
| * estimate the errors and add a pair (or tuple) "interval, error" to the priority queue. | |
| * - If the total error is smaller than the requested tolerance, or if the maximum number | |
| * of subdivisions is reached, discontinue the process. | |
| * - Otherwise, pop the top element from the queue (highest error), split the interval in two, | |
| * and apply Gauss-Kronrod to the new intervals, add those two new elements to the priority | |
| * queue and repeat from the previous step. | |
| */ | |
| template <typename FunctionType> | |
| Scalar quadratureKronrod( | |
| const FunctionType& f, const Scalar lowerLimit, const Scalar upperLimit, | |
| Scalar& estimatedError, Scalar& absIntegral, Scalar& absDiffIntegral, | |
| const QuadratureRule quadratureRule) | |
| { | |
| switch (quadratureRule) | |
| { | |
| case GaussKronrod15: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod15, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod15, QuadratureKronrod<Scalar>::weightsGauss15, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod21: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod21, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod21, QuadratureKronrod<Scalar>::weightsGauss21, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod31: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod31, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod31, QuadratureKronrod<Scalar>::weightsGauss31, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod41: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod41, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod41, QuadratureKronrod<Scalar>::weightsGauss41, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod51: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod51, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod51, QuadratureKronrod<Scalar>::weightsGauss51, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod61: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod61, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod61, QuadratureKronrod<Scalar>::weightsGauss61, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod71: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod71, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod71, QuadratureKronrod<Scalar>::weightsGauss71, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod81: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod81, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod81, QuadratureKronrod<Scalar>::weightsGauss81, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod91: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod91, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod91, QuadratureKronrod<Scalar>::weightsGauss91, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod101: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod101, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod101, QuadratureKronrod<Scalar>::weightsGauss101, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod121: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod121, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod121, QuadratureKronrod<Scalar>::weightsGauss121, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| case GaussKronrod201: | |
| return quadratureKronrodHelper( | |
| QuadratureKronrod<Scalar>::abscissaeGaussKronrod201, | |
| QuadratureKronrod<Scalar>::weightsGaussKronrod201, QuadratureKronrod<Scalar>::weightsGauss201, | |
| f, lowerLimit, upperLimit, estimatedError, absIntegral, absDiffIntegral, quadratureRule); | |
| default: | |
| return Scalar(0.); | |
| } | |
| } | |
| template <typename FunctionType, int numKronrodRows, int numGaussRows, int alignment> | |
| Scalar quadratureKronrodHelper( | |
| Eigen::Array<Scalar, numKronrodRows, 1, alignment, numKronrodRows, 1> abscissaeGaussKronrod, | |
| Eigen::Array<Scalar, numKronrodRows, 1, alignment, numKronrodRows, 1> weightsGaussKronrod, | |
| Eigen::Array<Scalar, numGaussRows, 1, alignment, numGaussRows, 1> weightsGauss, const FunctionType& f, const Scalar lowerLimit, | |
| const Scalar upperLimit, Scalar& estimatedError, Scalar& absIntegral, Scalar& absDiffIntegral, | |
| const QuadratureRule quadratureRule) | |
| { | |
| // Half-length of the interval. | |
| const Scalar halfLength = (upperLimit - lowerLimit) * Scalar(.5); | |
| // Midpoint of the interval. | |
| const Scalar center = (lowerLimit + upperLimit) * Scalar(.5); | |
| DenseIndex size1 = numKronrodRows - 1; | |
| DenseIndex size2 = numGaussRows - 1; | |
| // Points to be evaluated. | |
| Eigen::Array<Scalar, 2 * numKronrodRows - 1, 1> points; | |
| // points[0] = center | |
| // points[1], points[2], ..., points[size1]: center - abscissa | |
| // points[size1 + 1], ..., points[2 * size1]: center + abscissa | |
| points[0] = center; | |
| for (DenseIndex j = 1; j <= size1; ++j) | |
| { | |
| const Scalar abscissa = halfLength * abscissaeGaussKronrod[j - 1]; | |
| points[j] = center - abscissa; | |
| points[size1 + j] = center + abscissa; | |
| } | |
| // Evaluate points. | |
| f.eval(points.data(), 2 * numKronrodRows - 1); | |
| Eigen::Array<Scalar, 2 * numKronrodRows - 1, 1>& fPoints = points; // Alias of points | |
| const Scalar fCenter = fPoints[0]; | |
| Eigen::Map< Eigen::Array<Scalar, numKronrodRows - 1, 1> > f1Array(&fPoints[1], size1, 1); | |
| Eigen::Map< Eigen::Array<Scalar, numKronrodRows - 1, 1> > f2Array(&fPoints[size1 + 1], size1, 1); | |
| // The result of the Gauss formula. | |
| Scalar resultGauss; | |
| if (quadratureRule % 2 == 0) | |
| { | |
| resultGauss = Scalar(0.); | |
| } | |
| else | |
| { | |
| resultGauss = weightsGauss[size2] * fCenter; | |
| } | |
| // The result of the Kronrod formula. | |
| Scalar resultKronrod = weightsGaussKronrod[size1] * fCenter; | |
| using std::abs; | |
| absIntegral = abs(resultKronrod); | |
| resultKronrod += ((f1Array + f2Array) * weightsGaussKronrod.head(size1)).sum(); | |
| // Approximation to the mean value of f over the interval (lowerLimit, upperLimit), | |
| // i.e. I / (upperLimit - lowerLimit) | |
| Scalar resultMeanKronrod = resultKronrod * Scalar(.5); | |
| absDiffIntegral = weightsGaussKronrod[size1] * (abs(fCenter - resultMeanKronrod)); | |
| for (DenseIndex j = 0; j < size1; ++j) | |
| { | |
| if (j % 2) | |
| resultGauss += weightsGauss[j / 2] * (f1Array[j] + f2Array[j]); | |
| absIntegral += weightsGaussKronrod[j] * (abs(f1Array[j]) + abs(f2Array[j])); | |
| absDiffIntegral += weightsGaussKronrod[j] * (abs(f1Array[j] - resultMeanKronrod) + abs(f2Array[j] - resultMeanKronrod)); | |
| } | |
| Scalar result = resultKronrod * halfLength; | |
| absIntegral *= abs(halfLength); | |
| absDiffIntegral *= abs(halfLength); | |
| estimatedError = abs((resultKronrod - resultGauss) * halfLength); | |
| if (absDiffIntegral != Scalar(0.) && estimatedError != Scalar(0.)) | |
| { | |
| const Scalar tmp = estimatedError * Scalar(200.) / absDiffIntegral; | |
| estimatedError = absDiffIntegral | |
| * (std::min)(Scalar(1.), tmp * std::sqrt(tmp)); | |
| } | |
| if (absIntegral | |
| > (std::numeric_limits<Scalar>::min)() / (Eigen::NumTraits<Scalar>::epsilon() * Scalar(50.) )) | |
| { | |
| estimatedError = (std::max)( | |
| Eigen::NumTraits<Scalar>::epsilon() * static_cast<Scalar>(50.) * absIntegral, | |
| estimatedError); | |
| } | |
| return result; | |
| } | |
| /** | |
| * \brief An Array of dimension m_maxSubintervals for error estimates. | |
| * | |
| * The first k elements are indices to the error estimates over the subintervals, such that | |
| * errorList(errorListIndices(0)), ..., errorList(errorListIndices(k - 1)) forms a decreasing | |
| * sequence, with k = m_numSubintervals if m_numSubintervals <= (m_maxSubintervals/2 + 2), | |
| * otherwise k = m_maxSubintervals + 1 - m_numSubintervals. | |
| */ | |
| Eigen::Array<DenseIndex, Eigen::Dynamic, 1> m_errorListIndices; | |
| /** | |
| * \brief An Array of dimension m_maxSubintervals for subinterval left endpoints. | |
| * | |
| * The first m_numSubintervals elements are the lower end points of the subintervals in the | |
| * partition of the given integration range (lowerLimit, upperLimit). | |
| */ | |
| Eigen::Array<Scalar, Eigen::Dynamic, 1> m_lowerList; | |
| /** | |
| * \brief An Array of dimension m_maxSubintervals for subinterval upper endpoints. | |
| * | |
| * The first m_numSubintervals elements are the upper end points of the subintervals in the | |
| * partition of the given integration range (lowerLimit, upperLimit). | |
| */ | |
| Eigen::Array<Scalar, Eigen::Dynamic, 1> m_upperList; | |
| /** | |
| * \brief An Array of dimension m_maxSubintervals for integral approximations. | |
| * | |
| * The first m_numSubintervals elements are the integral approximations on the subintervals. | |
| */ | |
| Eigen::Array<Scalar, Eigen::Dynamic, 1> m_integralList; | |
| /** | |
| * \brief An Array of dimension m_maxSubintervals for error estimates. | |
| * | |
| * The first m_numSubintervals elements of which are the moduli of the absolute error estimates | |
| * on the subintervals. | |
| */ | |
| Eigen::Array<Scalar, Eigen::Dynamic, 1> m_errorList; | |
| /** | |
| * \brief Gives an upper bound on the number of subintervals. Must be at least 1. | |
| */ | |
| DenseIndex m_maxSubintervals; | |
| /** | |
| * \brief Estimate of the modulus of the absolute error, which should equal or exceed abs(I - I'). | |
| */ | |
| Scalar m_estimatedError; | |
| /** | |
| * \brief The number of integrand evaluations. | |
| */ | |
| DenseIndex m_numEvaluations; | |
| /** | |
| * \brief Error messages generated by the routine. | |
| * | |
| * errorCode = 0 Indicates normal and reliable termination of the routine. (It is assumed that | |
| * the requested accuracy has been achieved.) | |
| * errorCode > 0 Any errorCode greater than zero indicates abnormal termination of the routine. | |
| * (The estimates for integral and m_estimatedError are less reliable and the | |
| * requested accuracy has not been achieved.) | |
| * errorCode = 1 The maximum number of subdivisions allowed has been achieved. One can allow more | |
| * subdivisions by increasing the value of m_maxSubintervals. However, if this | |
| * yields no improvement it is advised to analyze the integrand in order to | |
| * determine the integration difficulaties. If the position of a local difficulty | |
| * can be determined, (i.e. singularity or discontinuity within the interval), one | |
| * will probably gain from splitting up the interval at this point and calling the | |
| * integrator on the subranges. If possible, an appropriate special-purpose | |
| * integrator should be used which is designed for handling the type of difficulty | |
| * involved. | |
| * errorCode = 2 The occurrence of roundoff error is detected, preventing the requested tolerance | |
| * from being achieved. | |
| * errorCode = 3 Extremely bad integrand behaviour occurs at points in the integration interval. | |
| * errorCode = 4 Roundoff error on extrapolation | |
| * errorCode = 5 Divergent integral (or very slowly convergent integral) | |
| * errorCode = 6 The input is invalid, because (desiredAbsoluteError <= 0 and | |
| * desiredRealtiveError < 50 * relativeMachineAccuracy, or | |
| * m_maxSubintervals < 1. | |
| * errorCode = 7 Applies to (D)QAWF only - limiting number of cycles has been attained | |
| * | |
| * \todo make relativeMachineAccuracy a member variable. | |
| */ | |
| DenseIndex m_errorCode; | |
| /** | |
| * \brief The number of subintervals actually produced in the subdivision process. | |
| */ | |
| DenseIndex m_numSubintervals; | |
| }; | |
| } | |
| #endif // EIGEN_INTEGRATOR_H |