In this section a code is developed that approximates I = to an accuracy
specified by the user. This is done by splitting the interval [a,b] into pieces and apply- ing a basic formula to each piece. The interval is split in a way adapted to the behavior of f(x). A fundamental tool is an error estimate. With the capability of estimating the error of integrating over a subinterval, we ask if the error is acceptable, and if it is not, the subinterval is split again. As we have seen, for a formula of even modest degree of precision, reducing the length of the interval increases substantially the accuracy of the approximation. Proceeding in this manner, the formula is applied over long subinter- vals where f(x) is easy to approximate and over short ones where it is difficult. Codes like the one developed here are in very wide use, being found, for example, in the collection of state-of-the-art codes QUADPACK [12], libraries like NAG and IMSL, and computing environments like MATLAB.
When the code is supplied absolute and relative error tolerances ABSERR and RELERR, it attempts to calculate a value ANSWER such that
|I - ANSWER| < max(ABSERR, RELERR × |I|). The computational form of this uses an error estimate
and replaces I by ANSWER:
|ERREST| < max(ABSERR, RELERR × |ANSWER|). (5.18)
We cannot hope to get a more accurate approximate integral than the correctly rounded true value, so it makes no sense to take RELERR < u, the unit roundoff of the computer used. Indeed, we require RELERR > 10u so that we do not work with error estimates that are nothing but roundoff. We also require ABSERR > 0 so as to deal with the rare situation that I = 0.
The method employed in the code breaks the interval [a,b] into pieces [α,β] on which the basic quadrature rule is sufficiently accurate. To decide this we must be able to estimate the error of the rule. This is done with a basic principle of numerical analysis, namely to estimate the error of a result by comparing it to a more accurate result. Besides the approximation
another approximation is formed that is believed to be more accurate. Then
5.2 ADAPTIVE QUADRATURE 185
To keep down the cost of estimating the error, we use evaluations of f(x) in both formulas. As a simple example, we might take Q to be the trapezoid rule and to be Simpson’s rule. The trapezoid rule is based on the values f(α) and f(β). The error estimate is computed with Simpson’s rule, which needs only the one additional value f((α+β)/2 Simpson’s rule is considerably more accurate than the trapezoid rule, giving us reason to hope that the estimate will be a good one.
The code Adapt uses for Q the three-point Gaussian quadrature formula of Ex- ample 5.4 that has degree of precision d1 = 5. A formula of much higher degree of
precision is used for The error analysis based on best possible polynomial approx- imation gives us confidence that will be more accurate than Q. It would be possible to use another Gaussian rule for but the N-point Gaussian rule for N 3 uses N completely different {xi} (except possibly x = 0). To keep the number off evaluations
to a minimum, another approach is taken. In 1964 Kronrod derived for each N-point Gaussian rule a corresponding rule of degree 3N + 1 or 3N + 2 (depending on whether N is even or odd). The idea was to start with the N Gaussian nodes and add N + 1 others chosen to obtain the highest possible degree of precision. Formulas for N < 40 are tabulated in [10]. The N = 3 case is
where
x1 = (as for three-point Gaussian quadrature)
x2 = 0.9604912687080202 x3 = 0.4342437493468026 A1 = 0.2684880898683334 A2 = 0.1046562260264672 A3 = 0.4013974147759622 A4 = 0.4509165386584744.
The basic idea of adaptive quadrature is to approximate the integral over a subin- terval (α,β). If the approximation is not accurate enough, the interval is split into (usually) two parts and the integral approximated over each subinterval. Eventually, accurate approximations are computed on all the subintervals of [a,b] that are added up to approximate the integral over [a,b] or the cost is deemed too high and the compu- tation terminated. The error terms of the formulas quantify the benefit of splitting an interval; recall from (5.16) that
Figure 5.5 Example where quadrature should be adaptive (for efficiency).
Clearly, halving β - α will generally result in much more accurate approximations. This process will resort to short intervals only where f(x) changes rapidly and long ones elsewhere. For example, the function f(x) graphed in Figure 5.5 is likely to require short intervals near 0 and [2, 3], but not elsewhere. A process like the one described is called adaptive because where f(x) is evaluated in the approximation of its integral depends on its behavior.
It is important to understand that quadrature formulas of the kind taken up in this chapter sample f at only a finite number of points, and if these points are not representative of the behavior of the curve f(x), the result will not be accurate. What is even worse is that the error estimate comes from comparing the result to that of another formula of the same kind, so it is possible that both are inaccurate. Because of this any quadrature formula and error estimate of the kind taken up here is doomed to be completely wrong on some problems. As a concrete example, for the Gauss- Kronrod (N = 3) case, let
Clearly, f(x) > 0 on [-1,1], so is a positive number. Yet both formulas
see only f(xi) = 0, hence calculate The result is terribly wrong and the
error estimate does not detect this. Applying a quadrature code blindly can get you into trouble!
The core of an adaptive quadrature code is a function that approximates
by Q and estimates the error of the approximation by Suppose we want
I = to an accuracy
TOL := max(ABSERR, RELERR × |ANSWER|).
A state-of-the-art code like those of QUADPACK [12] proceeds as follows. At a given stage of the process the interval [a,b] is partitioned into subintervals a = α1 < β1 =
5.2 ADAPTIVE QUADRATURE 187
α2 < β2 = α3 < ··· < βN = b; there is an estimate Qj available for the integral of
f(x) over each subinterval [αj,βj] and an estimate Ej available of the error of Qj. By
adding up the Qj and Ej, an approximation Q = ANSWER to I is available along with
an estimate E = ERREST of its error. If the current approximation to I is not good enough, the subinterval [αj, βj] with the largest error |Ej| is selected for improvement.
It is split into two pieces [αj, (αj + βj) /2], [(αj + βj) /2, βj], and approximate integrals
over these pieces and estimates of their error are computed. The two subintervals and the associated quantities replace the subinterval [αj, βj] and its associated quantities.
This global adaptive procedure is extremely efficient, but at a cost of keeping track of all the subintervals and the information associated with them. In the code Adapt the adaptation is more local and the implementation a little simpler. The difference in Adapt is that when a subinterval is integrated with enough accuracy, it is no longer a candidate for improvement. Thus a queue is kept of subintervals on which the es- timated error of the integral over the subinterval is considered to be too large. At a typical stage of the process, the code tests whether the current approximation to I is sufficiently accurate to satisfy the user’s error request. If it is not, the next subinterval
[αj, βj] is removed from the queue, split in half, and approximations to the integral
over each half, along with error estimates, are computed. They are placed at the end of
the queue. If an approximation to an integral is estimated to have an error
no more than |(β - α) / (b - a)] × TOL, it is accepted and [α, β] is not examined again. This is more stringent than necessary because adding up approximations
that satisfy this condition will result in an error such that
The global adaptation used in the collection QUADPACK [12] subdivides until
TOL. The more local adaptation of Adapt subdivides until TOL. Because
is always at least as big as and perhaps much larger, the local process
works harder but should provide answers that are more accurate than the specified tolerance TOL.
Let us now formalize the algorithm. To start the process, form the approximation Q to the integral over [a,b] and its estimated error ERREST. If |ERREST| < TOL, we take ANSWER = Q as the result and return. If ERREST does not pass the error test, a, b, Q, and E = ERREST are placed in the queue and the following loop is entered:
remove α, β, Q, E from top of queue
compute QL with estimated error EL
compute QR with estimated error ER
ANSWER := ANSWER + (( QL + QR) - Q) ERREST := ERREST + ((EL + ER) - E)
if EL is too big, add α, (α + β)/2, QL, EL to end of queue if ER is too big, add (α + β)/2, β, QR, ER to end of queue.
This procedure is repeated until one of the following events occurs: 1. The queue becomes empty.
2. |ERREST| < TOL.
3. The queue becomes larger than the space allocated. 4. Too many f evaluations are made.
The first two cases represent a success. The last two represent a failure in the sense that the code has failed to achieve the desired accuracy in the work allotted. It may be that the estimated error, although larger than specified by means of the tolerances, is acceptable and the answer reported will suffice. Even when it is not, an inaccurate value of the integral may be useful as a indication of the size of the integral when selecting appropriate tolerances.
Notice the way quantities have been grouped in the computation of ANSWER and ERREST. The quantity Q and the sum QL + QR both approximate the integral off over
[α, β]. They normally agree in a few leading digits, so their difference involves cancel- lation and is computed without arithmetic error. Because the difference is normally a small correction to ANSWER, it is possible to make a great many corrections without accumulation of arithmetic error. If the quantities are not grouped, the correction of a “small” ANSWER may be inaccurate.
5.3
CODES FOR ADAPTIVE QUADRATURE
The code presented here uses the three-point Gaussian rule to estimate integrals along with the seven-point Kronrod rule to estimate errors. In FORTRAN the routine Adapt has a typical call
CALL ADAPT (F, A, B, ABSERR, RELERR, ANSWER, ERREST, FLAG) and in C++,
flag = Adapt(f, a, b, abserr, relerr, answer, errest); and it is
flag = Adapt(f, a, b, abserr, relerr, &answer, &errest); in the C version.
The first argument, F or f, is the name of the function that provides values of the integral. In FORTRAN F must be declared in an EXTERNAL statement. The next four variables are input parameters: A and B are the (finite) end points of the integration interval, ABSERR and RELERR are the required absolute and relative er- ror tolerances. The remaining variables are output quantities: ANSWER contains the approximation to the integral and ERREST an estimate for the error of the approxi- mation. The value of FLAG is 0 for a normal return with (5.18) satisfied. FLAG > 0