LW = 'linewidth'; lw = 1.6; MS = 'MarkerSize'; ms = 10;
The Fourier series of a function $u \in L^{2}[-\pi,\pi]$ is given as
$$ \mathcal{F}[u] = \sum_{k=-\infty}^{\infty} c_k e^{ikx} $$
where
$$ c_k = \frac{1}{2\pi} \int_{-\pi}^{\pi} f(x)e^{-ikx} dx. $$
Alternatively, we can express the series in terms of sines and cosines:
$$ \mathcal{F}[u] = \sum_{k=0}^{\infty} a_k \cos(k x) + \sum_{k=1}^{\infty} b_k \sin(k x) $$
where
$$ a_k = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x)\cos(kx) dx, \quad b_k = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x)\sin(kx) dx, $$
for $k>0$ and
$$ a_0 = \frac{1}{2\pi} \int_{-\pi}^{\pi} f(x) dx. $$
Similar expressions hold for more general intervals $[a,b]$ by shifting and scaling appropriately.
The Fourier coefficients for many functions $u$ can be computed in Chebfun using the trigcoeffs
command. The smoothness of $u$ over $[-\pi,\pi]$ dictates the technique for determining the coefficients.
Smooth periodic functions
Typically, if $u$ and its periodic extension are twice continuously differentiable over $[-\pi,\pi]$ and $u'''(x)$ is piecewise continuous on $[-\pi,\pi]$ (or more specifically of bounded variation) then the Fourier coefficients of $u$ can be quickly computed by first constructing $u$ with the 'trig'
flag in the Chebfun, then calling trigcoeffs
. Here is an example for a simple Fourier polynomial:
u = chebfun(@(x) 1 - 4*cos(x) + 6*sin(2*x),[-pi,pi],'trig'); c = trigcoeffs(u); disp('Fourier coeffs of 1 + cos(x) + sin(2*x):') c
Fourier coeffs of 1 + cos(x) + sin(2*x): c = 0.000000000000000 + 3.000000000000001i -2.000000000000000 - 0.000000000000000i 1.000000000000000 + 0.000000000000000i -2.000000000000000 + 0.000000000000000i 0.000000000000000 - 3.000000000000001i
Note that trigcoeffs
follows the MATLAB convention of having the coefficients appear in order from highest degree to lowest degree. In the code above we have reversed this order to match the way the coefficients appear in the definitions above.
Also note that trigcoeffs
by default returns the coefficients in complex exponential form, i.e., the value of $c_k$ above. The equivalent coefficients in terms of cosines and sines can be obtained as:
[a,b] = trigcoeffs(u); disp('Fourier cosine coeffs of 1 + cos(x) + sin(2*x)') a disp('Fourier sine coeffs of 1 + cos(x) + sin(2*x)') b
Fourier cosine coeffs of 1 + cos(x) + sin(2*x) a = 1.000000000000000 -4.000000000000000 0.000000000000000 Fourier sine coeffs of 1 + cos(x) + sin(2*x) b = -0.000000000000000 6.000000000000002
Note that a
contains the constant term in the series as its first coefficient followed by the coefficients for $\cos(x)$ and $\cos(2x)$, while b
starts with the coefficient for $\sin(x)$ followed by the coefficient for $\sin(2x)$.
The default behavior of trigcoeffs
is to return all the Fourier coefficients necessary to resolve the function to machine precision (assuming this number is less than 65537). However, a specific number can be obtained with an additional input argument. We illustrate this feature on the function $f(x) = 3/(5 - 4\cos(x))$, which is analytic in a strip in the complex plane and has exact Fourier coefficients given by $c_k = 2^{-|k|}$:
numCoeffs = 11; u = chebfun(@(x) 3./(5 - 4*cos(x)),[-pi,pi],'trig'); c = trigcoeffs(u,numCoeffs); disp('Fourier coeffs of 3/(5-4cos(x)):') c
Fourier coeffs of 3/(5-4cos(x)): c = 0.031250000000000 - 0.000000000000000i 0.062500000000000 + 0.000000000000000i 0.125000000000000 - 0.000000000000000i 0.250000000000000 + 0.000000000000000i 0.500000000000000 - 0.000000000000000i 1.000000000000000 + 0.000000000000000i 0.500000000000000 + 0.000000000000000i 0.250000000000000 - 0.000000000000000i 0.125000000000000 + 0.000000000000000i 0.062500000000000 - 0.000000000000000i 0.031250000000000 + 0.000000000000000i
We see that the computed results match the exact results to machine precision.
Here is an example for a less smooth function:
numCoeffs = 17; u = chebfun(@(x) abs(sin(x)).^3,[-pi,pi],'trig'); c = trigcoeffs(u,numCoeffs); c = c(end:-1:1); disp('Fourier coeffs of |sin(x)|^3') c
Fourier coeffs of |sin(x)|^3 c = 0.001102371900205 - 0.000000000000000i 0.000000000000000 + 0.000000000000000i 0.004042030300748 - 0.000000000000000i -0.000000000000000 - 0.000000000000000i 0.036378272706721 - 0.000000000000000i 0.000000000000000 - 0.000000000000000i -0.254647908947031 - 0.000000000000000i -0.000000000000000 + 0.000000000000000i 0.424413181578389 + 0.000000000000000i -0.000000000000000 - 0.000000000000000i -0.254647908947031 + 0.000000000000000i 0.000000000000000 + 0.000000000000000i 0.036378272706721 + 0.000000000000000i -0.000000000000000 + 0.000000000000000i 0.004042030300748 + 0.000000000000000i 0.000000000000000 - 0.000000000000000i 0.001102371900205 + 0.000000000000000i
We see that the coefficients decay much more slowly in this case in fact the number of terms required to resolve this function to machine precision is:
length(u)
ans = 2513
The reason is that this function has only two continuous derivatives in $L^{2}[-\pi,\pi]$ and a piecewise continuous third derivative, so its Fourier coefficients decay as $O(|k|^{-4})$ [1]. This decay rate can be seen by plotting the Fourier coefficients on a log-log scale, which can be easily done for the positive mode coefficients (i.e. $k>0$) using the plotcoeffs
command:
plotcoeffs(u,'loglog'), ylim([1e-15 1]), hold on k = [100 length(u)/2]; plot(k,10*k.^(-4),'k-',LW,lw) text(500,50*(500)^(-4),'O(k^{-4})','FontSize',12) hold off
Non-smooth functions
The Fourier coefficients of functions (and their periodic extensions) with fewer than two continuous derivatives can also be computed. However, the functions must first be constructed using the default, 'non-periodic', option. In this case the Fourier coefficients are computed using the integral formulas (via Chebfun's sum
method) instead of the Fast Fourier Transform.
The quintessential example of a non-smooth function is that of the square wave (or periodic extension of the step function), which can be defined by the sign
function as
$$ u(x) = \mbox{sign}(\sin(x)) $$
This can be constructed in Chebfun with 'splitting' turned on,
sq_wave = @(x) sign(sin((x))); u = chebfun(sq_wave,[-pi,pi],'splitting','on');
We can obtain the Fourier coefficients of this function using again the trigcoeffs
method. Since the square wave is odd, it makes sense to look at the Fourier sine coefficients in this case:
numCoeffs = 15; [a,b] = trigcoeffs(u,numCoeffs); disp('Fourier sine coeffs of unit step function:') b
Fourier sine coeffs of unit step function: b = 1.273239544735163 - 0.000000000000000i -0.000000000000000 - 0.000000000000000i 0.424413181578388 - 0.000000000000000i 0.000000000000000 - 0.000000000000000i 0.254647908947033 + 0.000000000000000i 0.000000000000000 - 0.000000000000000i 0.181891363533594 - 0.000000000000000i
The exact values of the coefficients are
$$ b_k = \frac{4}{k\pi} ~~(k \mbox{ odd}) $$
with $b_k = 0$ for $k$ even, $k\ge 1$. These values can be easily seen in the computed results:
disp(' k pi/4*b_k') disp([(1:7)' pi/4*real(b)])
k pi/4*b_k 1.000000000000000 1.000000000000000 2.000000000000000 -0.000000000000000 3.000000000000000 0.333333333333333 4.000000000000000 0.000000000000000 5.000000000000000 0.200000000000000 6.000000000000000 0.000000000000000 7.000000000000000 0.142857142857142
The computed cosine coefficients are numerically zero, as expected:
norm(a,inf)
ans = 1.017844372940613e-16
Truncated Fourier approximations
The trigcoeffs
function can also be used in combination with the Chebfun constructor to generate truncated Fourier series representations of functions. Here's an example for the above square wave using 15 Fourier modes:
numModes = 15; c = trigcoeffs(u,2*numModes+1); u_trunc = chebfun(c,[-pi,pi],'trig','coeffs'); plot(u,'k-',u_trunc,'b-',LW,lw)
This represents the best 15-mode trigonometric approximation to the square wave over $[-\pi,\pi]$ in the $L^2$ sense. The oscillations in the approximation are called the Gibbs phenomenon.
To see the actual 'wave' it is useful to plot the approximation over a larger interval, which can be done for $-4\pi \leq x \leq 4\pi$ as follows:
u = chebfun(sq_wave,[-4*pi,4*pi],'splitting','on'); u_trunc = chebfun(u_trunc,[-4*pi,4*pi],'trig'); plot(u,'k-',u_trunc,'b-',LW,lw)
Note that in this case we don't recompute the Fourier coefficients over the larger domain, we simply construct u_trunc
over the larger domain.
Here is one last example for the sawtooth wave, again computed over $[-\pi,\pi]$ then expanded to a larger domain:
sawtooth = @(x) (mod(x+pi,2*pi))/(2*pi); u = chebfun(sawtooth,[-pi,pi],'splitting','on'); c = trigcoeffs(u,2*numModes+1); u_trunc = chebfun(c,[-pi,pi],'trig','coeffs'); u = chebfun(sawtooth,[-4*pi,4*pi],'splitting','on'); u_trunc = chebfun(u_trunc,[-4*pi,4*pi],'trig'); plot(u,'k-',u_trunc,'b-',LW,lw)
To hear this wave using try chebtune(u_trunc,6)
.
References
[1] L.N. Trefethen, Spectral Methods in MATLAB, SIAM, 2000.