%% CHEBFUN GUIDE 2: INTEGRATION AND DIFFERENTIATION
% Lloyd N. Trefethen, November 2009, revised February 2011
%% 2.1 sum
% We have seen that the "sum" command returns the definite integral of a
% chebfun over its range of definition. The integral is normally
% calculated by an FFT-based variant of Clenshaw-Curtis quadrature, as
% described first in [Gentleman 1972]. This formula is applied on each fun
% (i.e., each smooth piece of the chebfun), and then the results are added
% up.
%%
% Here is an example whose answer is known exactly:
f = chebfun('log(1+tan(x))',[0 pi/4]);
format long
I = sum(f)
Iexact = pi*log(2)/8
%%
% Here is an example whose answer is not known exactly, given as the first
% example in the section "Numerical Mathematics in Mathematica" in The
% Mathematica Book [Wolfram 2003].
f = chebfun('sin(sin(x))',[0 1]);
sum(f)
%%
% All these digits match the result 0.4306061031206906049... reported by
% Mathematica.
%%
% Here is another example:
F = @(t) 2*exp(-t.^2)/sqrt(pi);
f = chebfun(F,[0,1]);
I = sum(f)
%%
% The reader may recognize this as the integral that defines the error
% function evaluated at t=1:
Iexact = erf(1)
%%
% It is interesting to compare the times involved in evaluating this number
% in various ways. Matlab's specialized erf code is the fastest:
tic, erf(1), toc
%%
% Using Matlab's various quadrature commands
% is understandably slower:
tol = 3e-14;
tic, I = quad(F,0,1,tol); t = toc;
fprintf(' QUAD: I = %17.15f time = %6.4f secs\n',I,t)
tic, I = quadl(F,0,1,tol); t = toc;
fprintf(' QUADL: I = %17.15f time = %6.4f secs\n',I,t)
tic, I = quadgk(F,0,1,'abstol',tol,'reltol',tol); t = toc;
fprintf('QUADGK: I = %17.15f time = %6.4f secs\n',I,t)
%%
% The timing for Chebfun comes out very competitive:
tic, I = sum(chebfun(F,[0,1])); t = toc;
fprintf('CHEBFUN: I = %17.15f time = %6.4f secs\n',I,t)
%%
% Here is a similar comparison for a function that is more difficult,
% because of the absolute value, which leads with "splitting on" to a
% chebfun consisting of a number of funs.
F = @(x) abs(besselj(0,x));
f = chebfun(@(x) abs(besselj(0,x)),[0 20],'splitting','on');
plot(f)
%%
tol = 3e-14;
tic, I = quad(F,0,20,tol); t = toc;
fprintf(' QUAD: I = %17.15f time = %5.3f secs\n',I,t)
tic, I = quadl(F,0,20,tol); t = toc;
fprintf(' QUADL: I = %17.15f time = %5.3f secs\n',I,t)
tic, I = quadgk(F,0,20,'abstol',tol,'reltol',tol); t = toc;
fprintf(' QUADGK: I = %17.15f time = %5.3f secs\n',I,t)
tic, I = sum(chebfun(@(x) abs(besselj(0,x)),[0,20],'splitting','on')); t = toc;
fprintf('CHEBFUN: I = %17.15f time = %5.3f secs\n',I,t)
%%
% This last example highlights the piecewise-smooth aspect of Chebfun
% integration. Here is another example of a piecewise smooth problem.
x = chebfun('x');
f = sech(3*sin(10*x));
g = sin(9*x);
h = min(f,g);
plot(h)
%%
% Here is the integral:
tic, sum(h), toc
%%
% For another example of a definite integral we turn to an integrand given
% as example F21F in [Kahaner 1971]. We treat it first in the default mode
% of splitting off:
x = chebfun('x',[0 1]);
f = sech(10*(x-0.2)).^2 + sech(100*(x-0.4)).^4 + sech(1000*(x-0.6)).^6;
%%
% The function has three spikes, each ten times narrower than the last:
plot(f)
%%
% The length of the global polynomial representation is accordingly quite
% large, but the integral comes out correct to full precision:
length(f)
sum(f)
%%
% With splitting on, we get a much shorter chebfun since the narrow spike
% is isolated; and the integral is the same:
splitting on
f = sech(10*(x-0.2)).^2 + sech(100*(x-0.4)).^4 + sech(1000*(x-0.6)).^6;
splitting off
length(f)
sum(f)
%%
% Incidentally, if you are dealing with functions with narrow spikes like
% this, it is a good idea to increase the value of "minsamples" as
% described in Section 8.6.
%%
% As mentioned in Chapter 1 and described in more detail in Chapter 9,
% Chebfun has some capability of dealing with functions that blow up to
% infinity. Here for example is a familiar integral:
f = chebfun(@(x) 1./sqrt(x),[0 1],'blowup',2);
sum(f)
%%
% Certain integrals over infinite domains can also be computed,
% though the error is often large:
f = chebfun(@(x) 1./x.^2.5,[1 inf]);
sum(f)
%%
% Chebfun is not a specialized item of quadrature software; it is a general
% system for manipulating functions in which quadrature is just one of many
% capabilities. Nevertheless Chebfun compares reasonably well as a
% quadrature engine against specialized software. This was the conclusion
% of the Oxford MSc thesis by Phil Assheton [Assheton 2008], which compared
% Chebfun experimentally to quadrature codes including Matlab's quad and
% quadl, Gander and Gautschi's adaptsim and adaptlob, Espelid's modsim,
% modlob, coteda, and coteglob, QUADPACK's QAG and QAGS, and the NAG
% Library's d01ah. In both reliability and speed, Chebfun was found to be
% competitive with these alternatives. The overall winner was coteda
% [Espelid 2003], which was typically about twice as fast as Chebfun. For
% further comparisons of quadrature codes, together with the development of
% some improved codes based on a philosophy that has something in common
% with Chebfun, see [Gonnet 2009]. See also "Using Chebfun as an
% integrator" in the Quadrature section of the online Chebfun Examples
% collection.
%% 2.2 norm, mean, std, var
% A special case of an integral is the "norm" command, which for a chebfun
% returns by default the 2-norm, i.e., the square root of the integral of
% the square of the absolute value over the region of definition. Here is
% a well-known example:
norm(chebfun('sin(pi*theta)'))
%%
% If we take the sign of the sine, the norm increases to sqrt(2):
norm(chebfun('sign(sin(pi*theta))','splitting','on'))
%%
% Here is a function that is infinitely differentiable but not analytic.
f = chebfun('exp(-1./sin(10*x).^2)');
plot(f)
%%
% Here are the norms of f and its tenth power:
norm(f), norm(f.^10)
%% 2.3 cumsum
% In Matlab, "cumsum" gives the cumulative sum of a
% vector,
v = [1 2 3 5]
cumsum(v)
%%
% The continuous analogue of this operation is indefinite integration. If f
% is a fun of length n, then cumsum(f) is a fun of length n+1. For a
% chebfun consisting of several funs, the integration is performed on each
% piece.
%%
% For example, returning to an integral computed above, we can make our own
% error function like this:
t = chebfun('t',[-5 5]);
f = 2*exp(-t.^2)/sqrt(pi);
fint = cumsum(f);
plot(fint,'m')
ylim([-0.2 2.2]), grid on
%%
% The default indefinite integral takes the value 0 at the left endpoint,
% but in this case we would like 0 to appear at t=0:
fint = fint - fint(0);
plot(fint,'m')
ylim([-1.2 1.2]), grid on
%%
% The agreement with the built-in error function is convincing:
[fint((1:5)') erf((1:5)')]
%%
% Here is the integral of an oscillatory step function:
x = chebfun('x',[0 6]);
f = x.*sign(sin(x.^2)); subplot(1,2,1), plot(f)
g = cumsum(f); subplot(1,2,2), plot(g,'m')
%%
% And here is an example from number theory. The logarithmic integral,
% Li(x), is the indefinite integral from 0 to x of 1/log(s). It is an
% approximation to pi(x), the number of primes less than or equal to x. To
% avoid the singularity at x=0 we begin our integral at the point mu =
% 1.451... where Li(x) is zero, known as Soldner's constant. The test value
% Li(2) is correct except in the last digit:
mu = 1.45136923488338105; % Soldner's constant
xmax = 400;
Li = cumsum(chebfun(@(x) 1./log(x),[mu xmax]));
lengthLi = length(Li)
Li2 = Li(2)
%%
% (Chebfun has no trouble if xmax is increased
% to 10^5 or 10^10.) Here is a plot comparing Li(x) with pi(x):
clf, plot(Li,'m')
p = primes(xmax);
hold on, plot(p,1:length(p),'.k')
%%
% The Prime Number Theorem implies that pi(x) ~ Li(x) as x -> infinity.
% Littlewood proved in 1914 that although Li(x) is greater than pi(x) at
% first, the two curves eventually cross each other infinitely often. It is
% known that the first crossing occurs somewhere between x=1e14 and x=2e316
% [Kotnik 2008].
%%
% The "mean", "std", and "var" commands have also been overloaded
% for chebfuns and are based on integrals. For example
mean(chebfun('cos(x).^2',[0,10*pi]))
%% 2.4 diff
% In Matlab, "diff" gives finite differences of a vector:
v = [1 2 3 5]
diff(v)
%%
% The continuous analogue of this operation is differentiation. For
% example:
f = chebfun('cos(pi*x)',[0 20]);
fprime = diff(f);
hold off, plot(f)
hold on, plot(fprime,'r')
%%
% If the derivative of a function with a jump is computed, then a delta
% function is introduced. Consider for example this function defined
% piecewise:
f = chebfun('x.^2',1,'4-x','4./x',0:4);
hold off, plot(f)
%%
% Here is the derivative:
fprime = diff(f);
plot(fprime,'r'), ylim([-2,3])
%%
% The first segment of f' is linear, since f is quadratic here. Then comes
% a segment with f' = 0, since f is constant. And the end of this second
% segment appears a delta function of amplitude 1, corresponding to the
% jump of f by 1. (Currently delta functions are not shown on Chebfun
% plots.) The third segment has constant value f' = -1. Finally another
% delta function, this time with amplitude 1/3, takes us to the final
% segment.
%%
% Thanks to the delta functions, cumsum and diff are essentially inverse
% operations. It is no surprise that differentiating an indefinite
% integral returns us to the original function:
norm(f-diff(cumsum(f)))
%%
% More surprising is that integrating a derivative does the same, so long
% as we add in the value at the left endpoint:
d = domain(f);
f2 = f(d(1)) + cumsum(diff(f));
norm(f-f2)
%%
% Multiple derivatives can be obtained by adding a second argument to
% "diff". Thus for example,
f = chebfun('1./(1+x.^2)');
g = diff(f,4); plot(g)
%%
% However, one should be cautious about the potential loss of information
% in repeated differentiation. For example, if we evaluate this fourth
% derivative at x=0 we get an answer that matches the correct value 24 only
% to 11 places:
g(0)
%%
% For a more extreme example, suppose we define a chebfun for exp(x) on
% [-1,1]:
f = chebfun('exp(x)');
length(f)
%%
% Since f is a polynomial of low degree, it cannot help but lose
% information rather fast as we differentiate, and 15 differentiations
% eliminate the function entirely.
for j = 0:length(f)
fprintf('%6d %19.12f\n', j,f(1))
f = diff(f);
end
%%
% Is such behavior "wrong"? Well, that is an interesting question. Chebfun
% is behaving correctly in the sense mentioned in the second paragraph of
% Section 1.1: the operations are individually stable in that each
% differentiation returns the exact derivative of a function very close to
% the right one. The trouble is that because of the intrinsically ill-posed
% nature of differentiation, the errors in these stable operations
% accumulate exponentially as successive derivatives are taken.
%%
% Section 10.3 describes an alternative method of differentiating functions
% via automatic differentiation. Here is an example:
x = chebfun('x');
f = sin(30*exp(x));
g = f.*exp(x);
gprime = diag(diff(g,x));
subplot(1,2,1), plot(g), title g
subplot(1,2,2), plot(gprime), title('g''')
%% 2.5 Integrals in two dimensions
% Chebfun can often do a pretty good job with integrals over rectangles.
% Here for example is a colorful function:
r = @(x,y) sqrt(x.^2+y.^2); theta = @(x,y) atan2(y,x);
f = @(x,y) sin(5*(theta(x,y)-r(x,y))).*sin(x);
x = -2:.02:2; y = 0.5:.02:2.5; [xx,yy] = meshgrid(x,y);
clf, contour(x,y,f(xx,yy),-1:.2:1),
axis([-2 2 0.5 2.5]), colorbar, grid on
%%
% We can compute the integral over the box like this. Notice the use of
% the flag 'vectorize' to construct a chebfun from a function only defined
% for scalar arguments.
Iy = @(y) sum(chebfun(@(x) f(x,y),[-2 2]));
tic; I = sum(chebfun(@(y) Iy(y),[0.5 2.5],'vectorize')); t = toc;
fprintf('CHEBFUN: I = %16.14f time = %5.3f secs\n',I,t)
%%
% Here for comparison is Matlab's
% dblquad/quadl with a tolerance of 1e-11:
tic; I = dblquad(f,-2,2,0.5,2.5,1e-11,@quadl); t = toc;
fprintf('DBLQUAD/QUADL: I = %16.14f time = %5.3f secs\n',I,t)
%%
% This example of a 2D integrand is smooth, so both Chebfun and dblquad can
% handle it to high accuracy. The results will be quite different for less
% smooth integrands, and typically one will need to lower the tolerance.
% In Chebfun, this can be done by the chebfunpref command, as described in
% Chapter 8. For this example, however, there is not much speedup:
tic; I = sum(chebfun(@(y) Iy(y),[0.5 2.5],'vectorize','eps',1e-6)); t = toc;
fprintf('CHEBFUN: I = %16.14f time = %5.3f secs\n',I,t)
%% 2.6 Gauss and Gauss-Jacobi quadrature
% For quadrature experts, Chebfun contains some powerful capabilities
% implemented by Nick Hale. To start with, suppose we wish to carry out
% 4-point Gauss quadrature over [-1,1]. The quadrature nodes are the zeros
% of the degree 4 Legendre polynomial LEGPOLY(4), which can be obtained
% from the Chebfun command LEGPTS, and if two output arguments are
% requested, LEGPTS provides weights also:
[s,w] = legpts(4)
%%
% To compute the 4-point Gauss quadrature approximation to the integral of
% exp(x) from -1 to 1, for example, we could now do this:
x = chebfun('x');
f = exp(x);
Igauss = w*f(s)
Iexact = exp(1) - exp(-1)
%%
% There is no need to stop at 4 points, however.
% Here we use 1000 Gauss quadrature points:
tic
[s,w] = legpts(1000); Igauss = w*f(s)
toc
%%
% Even 100,000 points doesn't take too long:
tic
[s,w] = legpts(100000); Igauss = w*f(s)
toc
%%
% Traditionally, numerical analysts have computed Gauss quadrature nodes
% and weights by the eigenvalue algorithm of Golub and Welsch [Golub &
% Welsch 1969]. However, Chebfun uses the different algorithm of Glaser,
% Liu and Rokhlin [Glaser, Liu & Rokhlin 2007] with enhancements due to
% Hale and Sheehan Olver (unpublished).
%%
% For Legendre polynomials, Legendre points, and Gauss quadrature, use
% LEGPOLY and LEGPTS. For Chebyshev polynomials, Chebyshev points, and
% Clenshaw-Curtis quadrature, use CHEBPOLY and CHEBPTS and the built-in
% Chebfun commands such as SUM. A third variant is also available: for
% Jacobi polynomials, Gauss-Jacobi points, and Gauss-Jacobi quadrature, see
% JACPOLY and JACPTS. These arise in integration of functions with
% singularities at one or both endpoints, and are used internally by
% Chebfun for integration of chebfuns with singularities (Chapter 9).
%%
% As explained in the help texts, all of these operators
% work on general intervals [a,b], not just on [-1,1].
%% 2.7 References
%
% [Assheton 2008] P. Assheton, Comparing Chebfun to Adaptive Quadrature
% Software, dissertation, MSc in Mathematical Modelling and Scientific
% Computing, Oxford University, 2008.
%
% [Espelid 2003] Terje O. Espelid, "Doubly adaptive quadrature routines
% based on Newton-Cotes rules," BIT Numerical Mathematics 43 (2003),
% 319-337.
%
% [Gentleman 1972] W. M. Gentleman, "Implementing Clenshaw-Curtis
% quadrature I and II", Journal of the ACM 15 (1972), 337-346 and 353.
%
% [Glaser, Liu & Rokhlin 2007] A. Glaser, X. Liu and V. Rokhlin, "A fast
% algorithm for the calculation of the roots of special functions", SIAM
% Journal on Scientific Computing 29 (2007), 1420-1438.
%
% [Golub & Welsch 1969] G. H. Golub and J. H. Welsch, "Calculation of Gauss
% quadrature rules," Mathematics of Computation 23 (1969), 221-230.
%
% [Gonnet 2009] P. Gonnet, Adaptive Quadrature Re-Revisited, ETH
% dissertation no. 18347, Swiss Federal Institute of Technology, 2009.
%
% [Hale & Trefethen 2012] N. Hale and L. N. Trefethen,
% Chebfun and numerical quadrature, Science in China, submitted, 2012.
%
% [Kahaner 1971] D. K. Kahaner, "Comparison of numerical quadrature
% formulas", in J. R. Rice, ed., Mathematical Software, Academic Press,
% 1971, 229-259.
%
% [Kotnik 2008] T. Kotnik, "The prime-counting function and its analytic
% approximations", Advances in Computational Mathematics 29 (2008), 55-70.
%
% [Wolfram 2003] S. Wolfram, The Mathematica Book, 5th ed., Wolfram Media,
% 2003.