using Plots, ComplexPhasePortrait, ApproxFun, SingularIntegralEquations, DifferentialEquations
gr();
Dr. Sheehan Olver
This lecture we do the following:
That is, we introduce recurrences related to ultraspherical polynomials. This allows us to represent general linear differential equations as almost-banded systems.
We have discussed general properties, but now we want to discuss some classical orthogonal polynomials, beginning with Chebyshev (first kind) Tn(x), which is orthogonal w.r.t. 1√1−x2
For Chebyshev, recall we have the normalization constant (here we use a superscript Tn(x)=kTnxn+O(xn−1)) kT0=1,kTn=2n−1
We have already found the recurrence for Chebyshev: xTn(x)=Tn−1(x)2+Tn+1(x)2
Remark Jacobi, Laguerre, and Hermite all have similar relationships, which will be discussed further in the problem sheet.
It turns out that the derivative of Tn(x) is precisely a multiple of Un−1(x), and similarly the derivative of C(λ)n is a multiple of C(λ+1)n−1.
Proposition (Chebyshev derivative) T′n(x)=nUn−1(x)
Proof We first show that T′n(x) is othogonal w.r.t. √1−x2 to all polynomials of degree m<n−1, denoted fm, using integration by parts: ⟨T′n,fm⟩U=∫1−1T′n(x)fm(x)√1−x2dx=−∫1−1Tn(x)(f′m(x)(1−x2)+xfm)1√1−x2dx=−⟨Tn,f′m(1−x2)+xfm⟩T=0
The constant works out since T′n(x)=ddx(2n−1xn)+O(xn−2)=n2n−1xn−1+O(xn−2)
The exact same proof shows the following:
Proposition (Ultraspherical derivative) ddxC(λ)n(x)=2λC(λ+1)n−1(x)
Like the three-term recurrence and Jacobi operators, it is useful to express this in matrix form. That is, for the derivatives of Tn(x) we get ddx(T0(x)T1(x)T2(x)⋮)=(0123⋱)(U0(x)U1(x)U2(x)⋮)
Demonstration Here we see that applying a matrix to a vector of coefficients successfully calculates the derivative:
f = Fun(x -> cos(x^2), Chebyshev()) # f is expanded in Chebyshev coefficients
n = ncoefficients(f) # This is the number of coefficients
D = zeros(n-1,n)
for k=1:n-1
D[k,k+1] = k
end
D
31×32 Array{Float64,2}: 0.0 1.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 4.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ⋮ ⋮ ⋱ ⋮ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 26.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 27.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 28.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 29.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 30.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 31.0
Here D*f.coefficients
gives the vector of coefficients corresponding to the derivative, but now the coefficients are in the Un(x) basis, that is, Ultraspherical(1)
:
fp = Fun(Ultraspherical(1), D*f.coefficients)
fp(0.1)
-0.001999966666833569
Indeed, it matches the "true" derivative:
f'(0.1)
-0.0019999666668335634
-2*0.1*sin(0.1^2)
-0.0019999666668333335
Note that in ApproxFun.jl we can construct these operators rather nicely:
D = Derivative()
(D*f)(0.1)
-0.001999966666833569
Here we see that we can write produce the ∞-dimensional version as follows:
D : Chebyshev() → Ultraspherical(1)
ConcreteDerivative : Chebyshev() → Ultraspherical(1) ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 2.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 3.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 4.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 5.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 6.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 7.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 8.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 9.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱
We can convert between any two polynomial bases using a lower triangular operator, because their span's are equivalent. In the case of Chebyshev and ultraspherical polynomials, they have the added property that they are banded.
Proposition (Chebyshev T-to-U conversion) T0(x)=U0(x)T1(x)=U1(x)2Tn(x)=Un(x)2−Un−2(x)2
Proof
Before we do the proof, note that the fact that there are limited non-zero entries follows immediately: if m<n−2 we have ⟨Tn,Um⟩U=⟨Tn,(1−x2)Um⟩T=0
To actually determine the entries, we use the trigonometric formulae. Recall for x=(z+z−1)/2, z=eiθ, we have Tn(x)=cosnθ=z−n+zn2Un(x)=sin(n+1)θsinθ=zn+1−z−n−1z−z−1=z−n+z2−n+⋯+⋯+zn−2+zn
⬛️
Corollary (Ultrapherical λ-to-(λ+1) conversion) C(λ)n(x)=λn+λ(C(λ+1)n(x)−C(λ+1)n−2(x))
Proof This follows from differentiating the previous result. For example: ddxT0(x)=ddxU0(x)ddxT1(x)=ddxU1(x)2ddxTn(x)=ddx(Un(x)2−Un−22)
Differentiating this repeatedly completes the proof.
⬛️
Note we can write this in matrix form, for example, we have (1012−12012⋱⋱⋱)⏟S⊤0(U0(x)U1(x)U2(x)⋮)=(T0(x)T1(x)T2(x)⋮)
therefore, f(x)=(T0(x),T1(x),…)(f0f1⋮)=(U0(x),U1(x),…)S0(f0f1⋮)
Again, we can construct this nicely in ApproxFun:
S₀ = I : Chebyshev() → Ultraspherical(1)
ConcreteConversion : Chebyshev() → Ultraspherical(1) 1.0 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱
f = Fun(exp, Chebyshev())
g = S₀*f
Fun(Ultraspherical(1),[1.13032, 0.542991, 0.133011, 0.021897, 0.00271463, 0.000269864, 2.23891e-5, 1.5937e-6, 9.93309e-8, 5.5059e-9, 2.74775e-10, 1.24699e-11, 5.19555e-13, 1.99485e-14])
g(0.1) - exp(0.1)
0.0
Theorem (three-term recurrence for Chebyshev U) xU0(x)=U1(x)2xUn(x)=Un−1(x)2+Un+1(x)2
Proof Differentiating xT0(x)=T1(x)xTn(x)=Tn−1(x)2+Tn+1(x)2
⬛️
Differentiating this theorem again and applying the conversion we get the following
Corollary (three-term recurrence for ultrashperical) xC(λ)0(x)=12λC(λ)1(x)xC(λ)n(x)=n+2λ−12(n+λ)C(λ)n−1(x)+n+12(n+λ)C(λ)n+1(x)
Here's an example of the Jacobi operator (which is the transpose of the multiplciation by x operator):
Multiplication(Fun(), Ultraspherical(2))'
TransposeOperator : Ultraspherical(2) → Ultraspherical(2) 0.0 0.25 … ⋅ ⋅ ⋅ ⋅ 0.6666666666666666 0.0 ⋅ ⋅ ⋅ ⋅ ⋅ 0.625 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.4375 ⋅ ⋅ ⋅ ⋅ ⋅ 0.0 0.4444444444444444 ⋅ ⋅ ⋅ ⋅ 0.55 0.0 0.45 ⋅ ⋅ ⋅ ⋅ 0.5454545454545454 0.0 ⋱ ⋅ ⋅ … ⋅ ⋅ ⋱ ⋱
The preceding results allowed us to represent
as banded operators. We will see that we can combine these, along with
4. Evaluation
to solve ordinary differential equations.
Consider the simplest ODE: u(0)=0u′(x)−u(x)=0
We can express the constraints as acting on the coefficients. For example, we have u(0)=(T0(0),T1(0),…)(u0u1⋮)=(1,0,−1,0,1,…)(u0u1⋮)
Which gives us, u′(x)−u(x)=(U0(x),U1(x),…)(−1112−12212−12312⋱⋱⋱)(u0u1⋮)
Combing the differential part and the evaluation part, we arrive at an (infinite) system of equations for the coefficients u0,u1,…: (10−101⋯−1112−12212−12312⋱⋱⋱)(u0u1⋮)=(100⋮)
How to solve this system is outside the scope of this course (though a simple approach is to truncate the infinite system to finite systems). We can however do this in ApproxFun:
B = Evaluation(0.0) : Chebyshev()
D = Derivative() : Chebyshev() → Ultraspherical(1)
S₀ = I : Chebyshev() → Ultraspherical(1)
L = [B;
D - S₀]
InterlaceOperator : Chebyshev() → 2-element ArraySpace: Space{D,Float64} where D[ConstantSpace(Point(0.0)), Ultraspherical(1)] 1.0 0.0 -1.0 0.0 1.0 0.0 -1.0 0.0 1.0 0.0 ⋯ -1.0 1.0 0.5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ⋱ 0.0 -0.5 2.0 0.5 0.0 0.0 0.0 0.0 0.0 0.0 ⋱ 0.0 0.0 -0.5 3.0 0.5 0.0 0.0 0.0 0.0 0.0 ⋱ 0.0 0.0 0.0 -0.5 4.0 0.5 0.0 0.0 0.0 0.0 ⋱ 0.0 0.0 0.0 0.0 -0.5 5.0 0.5 0.0 0.0 0.0 ⋱ 0.0 0.0 0.0 0.0 0.0 -0.5 6.0 0.5 0.0 0.0 ⋱ 0.0 0.0 0.0 0.0 0.0 0.0 -0.5 7.0 0.5 0.0 ⋱ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.5 8.0 0.5 ⋱ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.5 9.0 ⋱ ⋮ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱
We can solve this system as follows:
u = L \ [1; 0]
plot(u)
It matches the "true" result:
u(0.1) - exp(0.1)
-4.440892098500626e-16
Note we can incorporate right-hand sides as well, for example, to solve u′(x)−u(x)=f(x), by expanding f in its Chebyshev U series.
This approach extends to second-order constant-coefficient equations by using ultraspherical polynomials. Consider u(−1)=1u(1)=0u″(x)+u′(x)+u(x)=0
D₀ = Derivative() : Chebyshev() → Ultraspherical(1)
D₁ = Derivative() : Ultraspherical(1) → Ultraspherical(2)
D₁*D₀ # 2 zeros not printed in (1,1) and (1,2) entry
ConcreteDerivative : Chebyshev() → Ultraspherical(2) ⋅ ⋅ 4.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 6.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 8.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 10.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 12.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 14.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 16.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 18.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱
For the identity operator, we use two conversion operators:
S₀ = I : Chebyshev() → Ultraspherical(1)
S₁ = I : Ultraspherical(1) → Ultraspherical(2)
S₁*S₀
ConversionWrapper : Chebyshev() → Ultraspherical(2) 1.0 0.0 -0.6666666666666666 0.0 … ⋅ ⋅ ⋅ ⋅ 0.25 0.0 -0.375 ⋅ ⋅ ⋅ ⋅ ⋅ 0.16666666666666666 0.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.125 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.07142857142857142 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … 0.0 0.0625 ⋅ ⋅ ⋅ ⋅ ⋅ -0.12698412698412698 0.0 ⋱ ⋅ ⋅ ⋅ ⋅ 0.0 -0.1125 ⋱ ⋅ ⋅ ⋅ ⋅ 0.05555555555555555 0.0 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ 0.05 ⋱ ⋅ ⋅ ⋅ ⋅ … ⋅ ⋅ ⋱
And for the first derivative, we use a derivative and then a conversion:
S₁*D₀ # or could have been D₁*S₀
TimesOperator : Chebyshev() → Ultraspherical(2) ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱
Putting everything together we get:
B₋₁ = Evaluation(-1) : Chebyshev()
B₁ = Evaluation(1) : Chebyshev()
# u(-1)
# u(1)
# u'' + u' +u
L = [B₋₁;
B₁;
D₁*D₀ + S₁*D₀ + S₁*S₀]
InterlaceOperator : Chebyshev() → 3-element ArraySpace: Space{D,Float64} where D[ConstantSpace(Point(-1)), ConstantSpace(Point(1)), Ultraspherical(2)] 1.0 -1.0 1.0 -1.0 … 1.0 -1.0 ⋯ 1.0 1.0 1.0 1.0 1.0 1.0 ⋱ 1.0 1.0 3.3333333333333335 -1.0 0.0 0.0 ⋱ 0.0 0.25 1.0 5.625 0.0 0.0 ⋱ 0.0 0.0 0.16666666666666666 1.0 0.0 0.0 ⋱ 0.0 0.0 0.0 0.125 … 0.0 0.0 ⋱ 0.0 0.0 0.0 0.0 0.07142857142857142 0.0 ⋱ 0.0 0.0 0.0 0.0 -1.0 0.0625 ⋱ 0.0 0.0 0.0 0.0 15.873015873015873 -1.0 ⋱ 0.0 0.0 0.0 0.0 1.0 17.8875 ⋱ ⋮ ⋱ ⋱ ⋱ … ⋱ ⋱ ⋱
u = L \ [1.0,0.0,0.0]
plot(u)
Consider the Airy ODE u(−1)=1u(1)=0u″(x)−xu(x)=0
to handle, this, we need only use the Jacobi operator to represent multiplication by x:
x = Fun()
Jᵗ = Multiplication(x) : Chebyshev() → Chebyshev() # transpose of the Jacobi operator
ConcreteMultiplication : Chebyshev() → Chebyshev() 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋱
We set op ther system as follows:
L = [B₋₁; # u(-1)
B₁ ; # u(1)
D₁*D₀ - S₁*S₀*Jᵗ] # u'' - x*u
InterlaceOperator : Chebyshev() → 3-element ArraySpace: Space{D,Float64} where D[ConstantSpace(Point(-1)), ConstantSpace(Point(1)), Ultraspherical(2)] 1.0 -1.0 1.0 -1.0 … -1.0 ⋯ 1.0 1.0 1.0 1.0 1.0 ⋱ 0.0 -0.16666666666666669 4.0 0.25 0.0 ⋱ -0.25 0.0 0.0625 6.0 0.0 ⋱ 0.0 -0.08333333333333333 0.0 0.05 0.0 ⋱ 0.0 0.0 -0.0625 0.0 … 0.0 ⋱ 0.0 0.0 0.0 -0.05 -0.03571428571428571 ⋱ 0.0 0.0 0.0 0.0 0.0 ⋱ 0.0 0.0 0.0 0.0 0.03571428571428571 ⋱ 0.0 0.0 0.0 0.0 18.0 ⋱ ⋮ ⋱ ⋱ ⋱ … ⋱ ⋱
u = L \ [1.0;0.0;0.0]
plot(u; legend=false)
If we introduce a small parameter, that is, solve u(−1)=1u(1)=0ϵu″(x)−xu(x)=0
ε = 1E-6
L = [B₋₁;
B₁ ;
ε*D₁*D₀ - S₁*S₀*Jᵗ]
u = L \ [1.0;0.0;0.0]
plot(u; legend=false)
Because of the banded structure, this can be solved fast:
ε = 1E-10
L = [B₋₁;
B₁ ;
ε*D₁*D₀ - S₁*S₀*Jᵗ]
@time u = L \ [1.0;0.0;0.0]
@show ncoefficients(u);
0.466365 seconds (13.06 M allocations: 296.400 MiB, 19.59% gc time) ncoefficients(u) = 62496
To handle other variable coefficients, first consider a polynomial p(x). If Multiplication by x is represented by multiplying the coefficients by J⊤, then multiplication by p is represented by p(J⊤):
M = -I + Jᵗ + (Jᵗ)^2 # represents -1+x+x^2
PlusOperator : Chebyshev() → Chebyshev() -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 -0.25 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 ⋱ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋱ ⋱
ε = 1E-6
L = [B₋₁;
B₁ ;
ε*D₁*D₀ - S₁*S₀*M]
@time u = L \ [1.0;0.0;0.0]
@show ε*u''(0.1) - (-1+0.1+0.1^2)*u(0.1)
plot(u)
0.023297 seconds (295.98 k allocations: 7.463 MiB) ε * ((u')')(0.1) - (-1 + 0.1 + 0.1 ^ 2) * u(0.1) = -1.4863110742169283e-14
For other smooth functions, we first approximate in a polynomial basis, and without loss of generality we use Chebyshev T basis. For example, consider u(−1)=1u(1)=0ϵu″(x)−exu(x)=0
Here is an example:
p = Fun(exp, Chebyshev()) # polynomial approximation to exp(x)
M = Multiplication(p) : Chebyshev() # constructed using Clenshaw:
ConcreteMultiplication : Chebyshev() → Chebyshev() 1.2660658777520084 0.5651591039924851 … 5.518385951076946e-9 ⋱ 1.1303182079849703 1.4018135475190467 9.988153506976674e-8 ⋱ 0.27149533953407656 0.587327528916817 1.59923072107804e-6 ⋱ 0.044336849848663804 0.1384847899880851 2.248866199669348e-5 ⋱ 0.0054742404420936785 0.022439888080288854 0.00027146315597690023 ⋱ 0.0005429263119139036 0.0027596088825239777 … 0.0027371202210468393 ⋱ 4.497732295427654e-5 0.00027306237418820746 0.022168424924331902 ⋱ 3.198436462511398e-6 2.2588267717379123e-5 0.13574766976703828 ⋱ 1.992124804817033e-7 1.6047366172067758e-6 0.5651591039924851 ⋱ 1.1036771902153892e-8 9.988153506976674e-8 1.2660658777520084 ⋱ ⋱ ⋱ … ⋱ ⋱
ApproxFun.bandwidths(M) # still banded
(13, 13)
ε = 1E-6
L = [B₋₁;
B₁ ;
ε*D₁*D₀ + S₁*S₀*M]
@time u = L \ [1.0;0.0;0.0]
@show ε*u''(0.1) + exp(0.1)*u(0.1)
plot(u)
0.039351 seconds (1.04 M allocations: 21.628 MiB, 28.97% gc time) ε * ((u')')(0.1) + exp(0.1) * u(0.1) = 4.884981308350689e-15