---
# System prepended metadata

title: 11-2. Rectangular spectral collocation
tags: [Final reports]

---

---
title: 11-2. Rectangular spectral collocation
tags: Final reports
---

**Reference**
1. Rectangular spectral collocation, T. A. Driscoll and N. Hale, IMA Journal of Numerical Analysis **36** (2016) 108–132, . [10.1093/imanum/dru062](https://doi.org/10.1093/imanum/dru062)

---

## Questions to be answered

1. Sec3.2: Re-sampling and aliasing

2. Sec4: Rectangular collocation for boundary-value problems

---

### Resampling and aliasing

#### (i) Resampling
Recall the Barycentric interpolation formula : 
The polynomial interpolation through $(x_j,f_j)_{j=0}^N$ is given by $p(x) = \frac{\sum\limits_{j=0}^N \frac{\lambda_j f_j}{x-x_j}}{\sum\limits_{j=0}^N \frac{\lambda_j}{x-x_j}}$ with $p(x_j)=f_j,$ where $\lambda_j = (\prod\limits_{k \neq j} x_j-x_k )^{-1}$

In particular, when $\{ x_j \}_{j=0}^N$ are Chebyshev points of second kind, $\lambda_j = \begin{cases}
(-1)^j &,j=1,\cdots ,N-1 \\
\frac{(-1)^j}{2} &,j=0,N
\end{cases}$

From the formula, we can see that the interpolant depends linearly on the values $\{ f_j \}$. That is, the evaluation of $p(y)$ at a set of points $\mathbf{y} = \{ y_i \}_{i=0}^{N-m}$ can be expressed as the matrix-vector multiplication
$$
p(\mathbf{y}) =\begin{bmatrix}
 & & \\
 & & \\
 &P_{N,-m}&\\
 & & \\
 & & \\
\end{bmatrix} \begin{bmatrix}
f_0\\
f_1\\
\vdots\\
f_{N-1}\\
f_N
\end{bmatrix}
$$
where $P_{N,-m} \in \mathbb{R}^{(N-m+1) \times (N+1)}$ is the **barycentric resampling matrix**
$$[ P_{N,-m} ]_{j,k}=
\begin{cases}
\frac{\lambda_k}{y_j-x_k} (\sum\limits_{l=1}^{N} \frac{\lambda_l}{y_j-x_l})^{-1} &, y_j \neq x_k\\
1 &, y_j = x_k
\end{cases}
$$

**Example :**
$u(x) = e^x \sin 5x$
```julia=
using LinearAlgebra
using Plots
using FFTW

# resampling f from original second kind chebyshev points to n_new+1 new first/second kind chebyshev points
function Chebresample(n_new, f, kind)
    
    # generate old grid and new grid
    n_old = length(f)-1;
    x_old = -sin.((-n_old:2:n_old)*pi/(2*n_old));
    if kind == 1
        x_new = cos.((1:2:2*n_new+1)*pi/(2*n_new+2));
    else
        x_new = -sin.((-n_new:2:n_new)*pi/(2*n_new));
    end
    
    # construct Barycentric resampling matrix
    P = zeros(n_new+1,n_old+1);
    w = (-1.0).^(0:n_old);  # weight
    w[1] = w[1]/2; w[end] = w[end]/2;
    
    for i = 1:n_new+1
        if x_new[i] in x_old
            P[i,findfirst(x->x==x_new[i],x_old)] = 1;
        else
            temp = sum(w ./(x_new[i]*ones(n_old+1)-x_old));
            for j = 1:n_old+1
                P[i,j] = w[j]/(x_new[i]-x_old[j])/temp;
            end
        end
    end
    
    return x_new, P*f
end
```
```julia=
N = 26;
x = -sin.((-N:2:N)*pi/(2*N));
u = exp.(x).*sin.(5*x);

xx=-1:1/100:1;
uu=(exp.(xx).*sin.(5*xx));
NN=13;
(x_new, u_new) = Chebresample(NN,u,1);

plot(xx,uu,label="u(x)")
plot!(x,u,marker=:circle,seriestype = :scatter,label="original sample points",legend=:bottomleft)
plot!(x_new,u_new,marker=:utriangle,seriestype = :scatter,label="resampling points")
```
![](https://i.imgur.com/GFYUvZ1.png)

```julia=
u_new_exact = exp.(x_new).*sin.(5*x_new);
print(string("max error = ",norm(u_new_exact-u_new,Inf)));
```
![](https://i.imgur.com/5phBRUJ.png)

coefficients : 
```julia=
u_extend = [u; u[N:-1:2]];
u_hat = fft(u_extend)/(2*N);

a = zeros(N+1,1);
a[1] = real(u_hat[1]);
a[2:N] = 2*real(u_hat[2:N]);
a[N+1] = real(u_hat[N+1]);

plot(1:N+1,log10.(abs.(a)),seriestype=:scatter,label="a_k")
```
![](https://i.imgur.com/VNwMmu4.png)


#### (ii) Resamlping with differentiation matrix
Recall that $[f_j^{(k)}]=D^k[f_j]$ where $D$ is the differentiation matrix. We can have 
$$
p^{(k)}(\mathbf{y})=[P_{N,-m}][f_j^{(k)}]=[P_{N,-m}]D^k[f_j]
$$

**Example :**
$u'(x) = e^x(\sin 5x + 5\cos 5x)$
```julia=
function cheb(N)
    if N == 0
        D = 0; x = 1;
        return
    end
    x = -sin.((-N:2:N)*pi/(2*N));          # generate grid x_i = cos(i*pi/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
end
```
```julia=
DD = cheb(N);
(x_prime, u_prime) = Chebresample(N-1,D*u,2);

uu_prime = exp.(xx).*(sin.(5*xx)+5*cos.(5*xx));
exact_u_prime = exp.(x_prime).*(sin.(5*x_prime)+5*cos.(5*x_prime));

plot(xx,uu_prime,label="u'(x)")
plot!(x,D*u,marker=:circle,markersize=6,seriestype=:scatter,label="D*u")
plot!(x_prime,u_prime,marker=:utriangle,seriestype=:scatter,color=:yellow,label="resampling points (P*D*u)",legend=:bottomleft)
```
![](https://i.imgur.com/UnwK9Xs.png)

```julia=
print(string("max error = ",norm(exact_u_prime-u_prime,Inf)));
```
![](https://i.imgur.com/pGmmQAC.png)


#### (iii) Aliasing
Theorem 1 : 
For any $N \geq 1$ and $0 \leq k \leq N$, the following Chebyshev polynomials take the same value on the $(N+1)-$point second-kind Chebyshev grid : 
$$
T_k,\; T_{2N \pm k},\; T_{4N \pm k},\; T_{6N \pm k},\; \cdots
$$

Theorem 2 : 
For any $N \geq 1$ and $0 \leq k \leq N$, the following Chebyshev polynomials take the same value on the $N-$point first-kind Chebyshev grid : 
$$
T_k,\; -T_{2N \pm k},\; T_{4N \pm k},\; -T_{6N \pm k},\; \cdots
$$
Now consider downsampling a degree $N$ polynomial $p_{N-1} = \sum\limits_{k=0}^{N-1} a_k T_k(x)$, quoting
a result for second-kind discretizations, to the degree $N-m-1$ polynomial $p_{N-m-1} = \sum\limits_{k=0}^{N-m-1} c_k T_k(x)$ by interpolating at $N−m$ points (where, for simplicity, we restrict $m<\frac{N}{2}$).

It follows from Theorem 1 that
$$
c_k = \begin{cases}
a_k &,\; k=0,1,\cdots,N-2m-2 \text{, and } N-m-1\\
a_k + a_{2(N-m)-(k+1)} &,\; k=N-2m-1,\cdots,N-m-2
\end{cases}
$$ if the $N-m$ points are second-kind Chebyshev points.

And it also follows from Theorem 2 that
$$
c_k = \begin{cases}
a_k &,\; k=0,1,\cdots,N-2m\\
a_k - a_{2(N-m)-(k-1)} &,\; k=N-2m+1,\cdots,N-m-1
\end{cases}
$$ if the $N-m$ points are first-kind Chebyshev points.

![](https://i.imgur.com/KIOIRcu.png)
Illustration of aliased coefficients : (a) is second to second and (b) is second to first.

**Example :**
$u(x)=e^x\sin5x$

(1) Second-kind to Second-kind
```julia=
u_extend = [u; u[N:-1:2]];
u_hat = fft(u_extend)/(2*N);

a = zeros(N+1,1);
a[1] = real(u_hat[1]);
a[2:N] = 2*real(u_hat[2:N]);
a[N+1] = real(u_hat[N+1]);

NN=N-1;
(x_new, u_new) = Chebresample(NN,u,2);
u_new_extend = [u_new; u_new[NN:-1:2]];
u_new_hat = fft(u_new_extend)/(2*NN);

c = zeros(NN+1,1);
c[1] = real(u_new_hat[1]);
c[2:NN] = 2*real(u_new_hat[2:NN]);
c[NN+1] = real(u_new_hat[NN+1]);

plot(1:N+1,log10.(abs.(a)),seriestype=:scatter,label="a_k")
plot!(1:NN+1,log10.(abs.(c)),seriestype=:scatter,marker=:utriangle,label="c_k")
plot!(1:NN+1,log10.(abs.(c-a[1:NN+1])),seriestype=:scatter,marker=:+,label="error")
```
![](https://i.imgur.com/XGHgqwL.png)



(2) Second-kind to First-kind
```julia=
u_extend = [u; u[N:-1:2]];
u_hat = fft(u_extend)/(2*N);

a = zeros(N+1,1);
a[1] = real(u_hat[1]);
a[2:N] = 2*real(u_hat[2:N]);
a[N+1] = real(u_hat[N+1]);

NN=N-1;
(x_new, u_new) = Chebresample(NN,u,1);
c = dct(u_new);
c[1] = c[1]/sqrt(NN+1);
c[2:end] = c[2:end]/sqrt((NN+1)/2);

plot(1:N+1,log10.(abs.(a)),seriestype=:scatter,label="a_k")
plot!(1:NN+1,log10.(abs.(c)),seriestype=:scatter,marker=:utriangle,label="c_k")
plot!(1:NN+1,log10.(abs.(c-a[1:NN+1])),seriestype=:scatter,marker=:+,label="error")
```
![](https://i.imgur.com/sLRBgZp.png)
![](https://i.imgur.com/7CDwr3m.png)



From the above illustration, the downsampling of $p_{N−1} =\sum\limits^{N−1}_{
k=0} a_kT_k(x)$ to a grid of $N − 1$ Chebyshev points of the first kind is equivalent to truncating the highest-order Chebyshev  coefficient $a_{N−1}$.