--- title: 9-1. Chebyshev approximations - derivative tags: Spectral method --- **Reference** 1. [Spectral method in Matlab](https://people.maths.ox.ac.uk/trefethen/spectral.html) by *Lloyd N. Trefethen* 2. Spectral methods by *J. Shen*, *T. Tang* and *L.-L. Wang* --- ```Matlab= % CHEB compute D = differentiation matrix, x = Chebyshev grid function [D,x] = cheb(N) if N==0, D=0; x=1; return, end x = cos(pi*(0:N)/N)'; c = [2; ones(N-1,1); 2].*(-1).^(0:N)'; X = repmat(x,1,N+1); dX = X-X'; D = (c*(1./c)')./(dX+(eye(N+1))); % off-diagonal entries D = D - diag(sum(D')); % diagonal entries ``` --- ```Matlab= % CHEBFFT Chebyshev differentiation via FFT. Simple, not optimal. % If v is complex, delete "real" commands. function w = chebfft(v) N = length(v)-1; if N==0, w=0; return, end x = cos((0:N)'*pi/N); ii = 0:N-1; v = v(:); V = [v; flipud(v(2:N))]; % transform x -> theta U = real(fft(V)); W = real(ifft(1i*[ii 0 1-N:-1]'.*U)); w = zeros(N+1,1); w(2:N) = -W(2:N)./sqrt(1-x(2:N).^2); % transform theta -> x w(1) = sum(ii'.^2.*U(ii+1))/N + .5*N*U(N+1); w(N+1) = sum((-1).^(ii+1)'.*ii'.^2.*U(ii+1))/N + ... .5*(-1)^(N+1)*N*U(N+1); ``` --- ```Matlab= % p11.m - Chebyshev differentation of a smooth function xx = -1:.01:1; uu = exp(xx).*sin(5*xx); clf for N = [10 20] [D,x] = cheb(N); u = exp(x).*sin(5*x); subplot('position',[.15 .66-.4*(N==20) .31 .28]) plot(x,u,'.','markersize',14), grid on line(xx,uu) title(['u(x), N=' int2str(N)]) error = D*u - exp(x).*(sin(5*x)+5*cos(5*x)); subplot('position',[.55 .66-.4*(N==20) .31 .28]) plot(x,error,'.','markersize',14), grid on line(x,error) title([' error in u''(x), N=' int2str(N)]) end ``` --- ### C++ Code(亞衛) #### Differentiation Matrix ``` C++= #include<iostream> #include<stdio.h> #include<stdlib.h> #include<math.h> #include<cmath> #include<vector> #include<conio.h> #include<iomanip> using namespace std; #define N 10 #define pi 3.14159265358979323846 int main(void) { cout.precision(6); vector<double> x(N+1,0); for (int i=0;i<=N;i++){ x[i] = cos((pi*i) / N); } vector<double> c(N+1,0); c[0] = 2; c[N] = 2; for (int i=1;i<N;i++){ c[i] = pow((-1.0),i); } double X[N+1][N+1]; double Y[N+1][N+1]; for(int i=0;i<=N;i++){ for(int j=0;j<=N;j++){ X[j][i] = x[j]; Y[i][j] = x[j]; //Y = X transpose } } for(int i=0;i<=N;i++){ for(int j=0;j<=N;j++){ X[i][j] = X[i][j] - Y[i][j]; //dX = X-X' } } double C[N+1][N+1]; double D[N+1][N+1]; for(int i=0;i<=N;i++){ for(int j=0;j<=N;j++){ C[i][j] = c[i]*(1.0/c[j]); } } for(int i=0;i<=N;i++) X[i][i] = X[i][i] + 1.0; for(int i=0;i<=N;i++){ for(int j=0;j<=N;j++){ D[i][j] = C[i][j]/X[i][j]; } } for(int i=0;i<=N;i++){ for(int j=0;j<=N;j++){ Y[i][j] = D[j][i]; //Y = D transpose } } vector<double> sum(N+1,0); for(int i=0;i<=N;i++){ for(int j=0;j<=N;j++){ sum[i] = sum[i] + Y[j][i]; //Y = D transpose } } for(int i=0;i<=N;i++){ D[i][i] = D[i][i] - sum[i]; } //Output Differentiation Matrix cout.setf(ios::fixed); for(int i=0;i<=N;i++){ cout<<"[ "; for(int j=0;j<=N;j++){ cout<<fixed<< setprecision(6) <<D[i][j]<<", "; } cout<<" ]"<<endl; } } ``` Result:(with N=10) ![](https://i.imgur.com/53HSLxj.png) #### Chebyshev differentiation via FFT ```C++= #include<iostream> #include<stdio.h> #include<stdlib.h> #include<math.h> #include<cmath> #include<vector> #include<conio.h> #include<complex> #include<valarray> using namespace std; #define N 16 #define pi 3.14159265358979323846 typedef complex<double> Complex; typedef valarray<Complex> CArray; void Peek(vector<double> vector) { //Output the vector for (int i = 0; i < vector.size(); i++) { cout <<"vec["<< i << "] = " << vector[i] << endl; } } void Peekcomplex(CArray& x){ for(int i=0;i<=2*N-1;i++){ cout <<"arr["<< i << "] = " << x[i] <<endl; } } void fft(CArray& x) { const size_t K = x.size(); if (K <= 1) return; // divide CArray even = x[std::slice(0, K/2, 2)]; CArray odd = x[std::slice(1, K/2, 2)]; // conquer fft(even); fft(odd); // combine for (size_t k = 0; k < K/2; ++k) { Complex t = std::polar(1.0, -2 * pi * k / K) * odd[k]; x[k ] = even[k] + t; x[k+K/2] = even[k] - t; } } void ifft(CArray& x) { // conjugate the complex numbers x = x.apply(std::conj); // forward fft fft( x ); // conjugate the complex numbers again x = x.apply(std::conj); // scale the numbers x /= x.size(); } int main(void) { vector<double> x(N+1,0); vector<double> v(N+1,0); for(int i=0;i<=N;i++) { //create vector v x[i] = (-1) + (2.0/N)*i; v[i] = exp(x[i]) * sin(5*x[i]); } for(int i=0;i<=N;i++) x[i] = cos((pi*i) / N); Complex V[2*N] = {0.0}; double U[2*N] = {0.0}; for(int i=0;i<(2*N);i++) { //create vector v if(i<=N) V[i] = v[i]; else { V[i] = v[2*N-i]; } } CArray data(V, 2*N); fft(data); complex<double> temp; for(int i=0;i<(2*N);i++){ temp = data[i]; U[i] = temp.real(); } vector<double> ii(2*N,0.0); for(int i=0;i<(2*N);i++) { if(i<N) ii[i] = i; else{ ii[i] = -ii[(2*N)-i]; } } for(int i=0;i<(2*N);i++){ temp.real() = 0.0; temp.imag() = ii[i] * U[i]; data[i] = temp; } ifft(data); vector<double> W(2*N,0.0); for(int i=0;i<(2*N);i++){ temp = data[i]; W[i] = temp.real(); } vector<double> w(N+1,0.0); for(int i=1;i<N;i++) w[i] = -W[i] / sqrt(1-(pow(x[i],2))) ; double sum = 0.0; for(int i=0;i<N;i++) sum = sum + (pow(i,2)*U[i]); w[0] = sum/N + 0.5*N*U[N]; sum = 0.0; for(int i=0;i<N;i++) sum = sum + (pow(i,2)*pow(-1,i+1)*U[i]); w[N] = sum/N + 0.5*pow(-1,N+1)*N*U[N]; Peek(w); } ``` Result: (Using $N=16$ with $v_j = v(x_j) = \exp(x_j)\times\sin(5x_j)$ where $x_j=\cos(j\pi/N)$.) ![](https://i.imgur.com/yDG6HGJ.png) #### FFT C++ Code References [Fast Fourier transform](https://rosettacode.org/wiki/Fast_Fourier_transform#C.2B.2B) --- ### Python (尚文) Some auxiliary codes: ```Python= import numpy as np import matlotlib.pyplot as plt def alt(n): alt = [] for i in range(n): alt.append((-1)**i) return np.array(alt) def _alt(n): alt = [] for i in range(n): alt.append((-1)**(i+1)) return np.array(alt) def consec(n): return list(range(n)) def cesnoc(n): cesnoc = [] for i in range(n): cesnoc.append(-n+i) return cesnoc def arrconsec(n): return np.array(list(range(n))) ``` #### Differentiation Matrix ```Python= def cheb(N): if N==0: return 0, 1 x = np.cos(np.pi*np.linspace(0,1,N+1)) c = np.array([2] + [1]*(N-1) + [2]) * alt(N+1) X = np.outer(x, np.ones(N+1)) dX = X-X.T D = np.outer(c, np.array([1]*(N+1))/c) / (dX + np.identity(N+1)) D = D - np.diag(np.sum(D,axis=1)) return D, x ``` #### FFT differentiation ```Python= def chebfft(v): N, = np.shape(v) N = N-1 if N==0: return 0 vl = v.tolist() x = np.cos(np.pi*np.linspace(0,1,N+1)) # len = N+1 = len(v) ii = arrconsec(N) # len = N V = np.array(vl + vl[1:N][::-1]) # len = N+1+N-1 = 2N U = np.real(np.fft.fft(V)) # len = 2N W = np.real(np.fft.ifft( 1j*np.array(consec(N) + [0] + cesnoc(N-1)) * U)) # len = 2N w = np.zeros(N+1) # len = N+1 = len(v) # note - the last entry of (N+1,)-array is [N] in python w[0] = sum( np.square(ii)* U[:N] )/N + 0.5*N*U[N] # sum of matrix w[1:N] = -W[1:N]/np.sqrt(np.ones(N-1)-np.square(x[1:N])) # use np 1-array w[N] = sum( _alt(N)*np.square(ii)*U[:N] )/N + 0.5*(-1)**(N+1)*N*U[N] return w ``` #### Chebyshev recursion ```Python= def list2cheb(f): #given discrete evaluations on cosine grids f_i, return the Chebyshev coefficients N, = np.shape(f) N = N-1 f_l = f.tolist() f_ext = np.array(f_l + f_l[1:N][::-1]) f_hat = np.fft.fft(f_ext)/(2*N) a = np.zeros(N+1, dtype=complex) a[1:N] = 2*np.real(f_hat[1:N]) a[0] = np.real(f_hat[0]) a[N] = np.real(f_hat[N]) return a def chebdiff_3(f): N, = np.shape(f) N = N-1 a = list2cheb(f) b = np.zeros(N+1) b[N-1] = 2*N*a[N] for run in range(2,N): i = N+1-run b[i-1] = 2*i*a[i] + b[i+1] print("i=",i,"b[{}]=".format(i+1), b[i+1]) b[0] = a[1]+.5*b[2] ddx_f_hat = np.zeros(2*N) ddx_f_hat[0] = b[0] ddx_f_hat[N] = b[N] ddx_f_hat[1:N] = 0.5*b[1:N] ddx_f_hat[N+1:] = 0.5*np.flip(b[1:N]) ddx_f = np.real(2*N*np.fft.ifft(ddx_f_hat))[0:N+1] return ddx_f ``` #### Results ```Python= import matplotlib.pyplot as plt xx = np.linspace(-1,1,201) uu = np.exp(xx)*np.sin(5*xx) plt.figure(figsize=(20,8)) plt.subplots_adjust(hspace=0.5) plot_count = 1 for N in [10,20]: D, x = cheb(N) u = np.exp(x)*np.sin(5*x) error1 = np.dot(D,u) - np.exp(x)*(np.sin(5*x)+5*np.cos(5*x)) # differentiation matrix error2 = chebfft(u) - np.exp(x)*(np.sin(5*x)+5*np.cos(5*x)) # FFT error3 = chebdiff_3(u) - np.exp(x)*(np.sin(5*x)+5*np.cos(5*x)) # recusion plt.subplot(3,2,plot_count) plot_count += 1 plt.plot(xx,uu,'-',x,u,'o') plt.axis([-1,1,-4,2]) plt.title('u(x), N={}'.format(N)) plt.subplot(3,2,plot_count) plot_count += 1 plt.plot(x,error1,'o',label="Diffmat") plt.plot(x,error2,'*',label="FFT") plt.plot(x,error3,'+',label="Recursion") plt.legend() plt.grid(True) plt.title('error in first derivative, N={}'.format(N)) plt.plot(x,error1) plt.plot(x,error2) plt.plot(x,error3) ``` ![](https://i.imgur.com/JGitMwe.png) --- ### Julia Code(浩恩) #### Differentiation Matrix ```julia= using LinearAlgebra using Plots using FFTW # CHEB compute D = differentiation matrix, x = Chebyshev grid function cheb(N) if N == 0 D = 0; x = 1; return end x = cos.(pi*(0:N)/N); # generate grid x_i = cos(i*pi/N) i=0~N c = [2; ones(N-1,1); 2].*(-1).^(0:N); # c_i*(-1)^i X = repeat(x,1,N+1); dX = X-X'; # compute x_i - x_j D = (c*(1 ./c)')./(dX+(I(N+1))); # off-diagonal entries D = D - diagm(vec(sum(D,dims=2))); # diagonal entries return D, x end ``` #### Chebyshev differentiation via FFT ```julia= # CHEBFFT Chebyshev differentiation via FFT. Simple, not optimal. # If f is complex, delete "real" commands. # f = f(x_i) is a column vector function chebfft(f) N = length(f)-1; if N==0 f_prime = 0; return end x = cos.(pi*(0:N)/N); ii = 0:N-1; f_extend = [f; f[N:-1:2]]; # transform f -> theta f_hat = real(fft(f_extend)); w = real(ifft(1im*[0:N-1;0;1-N:-1;].*f_hat)); # transform theta -> w = df/(d theta) # transform w -> f_prime f_prime = zeros(N+1,1); f_prime[2:N] = -w[2:N]./sqrt.(ones(N-1,1)-x[2:N].^2); f_prime[1] = sum(ii.^2 .*f_hat[1:N])/N + .5*N*f_hat[N+1]; f_prime[N+1] = sum((-1).^(1:N).*ii.^2 .*f_hat[1:N])/N + .5*(-1)^(N+1)*N*f_hat[N+1]; return f_prime end ``` #### Chebyshev differentiation via FFT in another way ```julia= # CHEBFFT2 Chebyshev differentiation via FFT in another way # If v is complex, delete "real" commands. # f = f(x_i) is a column vector function chebfft2(f) N = length(f)-1; if N==0 f_prime = 0; return end x = -sin.((-N:2:N)*pi/(2*N)); # extend the function and take scaled FFT f_extend = [f; f[N:-1:2]]; f_hat = fft(f_extend)/(2*N); # obtain the Chebyshev coefficients of f a = zeros(N+1,1); a[1] = real(f_hat[1]); a[2:N] = 2*real(f_hat[2:N]); a[N+1] = real(f_hat[N+1]); # compute the Chebyshev coefficients of f' b = zeros(N+1,1); b[N] = 2*N*a[N+1]; for i = N:-1:3 b[i-1] = 2*(i-1)*a[i] + b[i+1]; end b[1] = .5*(2*a[2]+b[3]); # compute the Fourier coefficient of f' f_prime_hat = zeros(2*N,1); f_prime_hat[1] = b[1]; f_prime_hat[2:N] = 0.5*b[2:N]; f_prime_hat[N+1] = b[N+1]; f_prime_hat[N+2:end] = 0.5*b[N:-1:2]; # take rescaled ifft to get extend f' f_prime_extend = real(ifft(f_prime_hat)*2*N); f_prime = f_prime_extend[1:N+1]; # get the unextended f' return f_prime end ``` #### Example $u(x) = e^x\sin5x$ and $u'(x) = e^x(\sin5x+5\cos5x)$ (1) $N=10$ ```julia= xx=-1:1/100:1; uu=(exp.(xx).*sin.(5*xx)); (D,x)=cheb(10); u = (exp.(x).*sin.(5*x)); exact_u_prime = exp.(x).*(sin.(5*x)+5*cos.(5*x)); error1 = D*u-exact_u_prime; u_prime = chebfft(u); error2 = u_prime - exact_u_prime; u_prime2 = chebfft2(u); error3 = u_prime2 - exact_u_prime; p1 = plot(xx,uu,label="",title="u(x), N=10",ylim=(-4,2),yticks=-4:2:2); p1 = plot!(x,u,seriestype = :scatter,label=""); p1 = plot!(size=(800,210)); p2 = plot(x,error1,label="method 1",title="error in u'(x)",marker=:circle,markersize=6); p2 = plot!(x,error2,label="method 2",seriestype = :scatter,marker=:utriangle,color=:red,markersize=5); p2 = plot!(x,error3,label="method 3",seriestype = :scatter,marker=:hexagon,color=:yellow); p2 = plot!(size=(800,210)); plot(p1,p2,layout = 2,legend=(0.8,0.3)) ``` Result : ![](https://i.imgur.com/NNyrJpT.png) (2) $N=20$ ```julia= xx=-1:1/100:1; uu=(exp.(xx).*sin.(5*xx)); (D,x)=cheb(20); u = (exp.(x).*sin.(5*x)); exact_u_prime = exp.(x).*(sin.(5*x)+5*cos.(5*x)); error1 = D*u-exact_u_prime; u_prime = chebfft(u); error2 = u_prime - exact_u_prime; u_prime2 = chebfft2(u); error3 = u_prime2 - exact_u_prime; p3 = plot(xx,uu,label="",title="u(x), N=20",ylim=(-4,2),yticks=-4:2:2); p3 = plot!(x,u,seriestype = :scatter,label=""); p3 = plot!(size=(900,240)); p4 = plot(x,error1,label="method 1",title="error in u'(x)",marker=:circle,markersize=6); p4 = plot!(x,error2,label="method 2",seriestype = :scatter,marker=:utriangle,color=:red,markersize=5) p4 = plot!(x,error3,label="method 3",seriestype = :scatter,marker=:hexagon,color=:yellow); p4 = plot!(size=(900,240)); plot(p3,p4,layout = 2,legend=(0.4,0.88)) ``` Result : ![](https://i.imgur.com/k10qA2S.png)