{ "cells": [ { "cell_type": "code", "execution_count": 12, "metadata": { "scrolled": false }, "outputs": [], "source": [ "using Plots, ComplexPhasePortrait, ApproxFun, SingularIntegralEquations, DifferentialEquations\n", "gr();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# M3M6: Methods of Mathematical Physics\n", "\n", "$$\n", "\\def\\dashint{{\\int\\!\\!\\!\\!\\!\\!-\\,}}\n", "\\def\\infdashint{\\dashint_{\\!\\!\\!-\\infty}^{\\,\\infty}}\n", "\\def\\D{\\,{\\rm d}}\n", "\\def\\E{{\\rm e}}\n", "\\def\\dx{\\D x}\n", "\\def\\dt{\\D t}\n", "\\def\\dz{\\D z}\n", "\\def\\C{{\\mathbb C}}\n", "\\def\\R{{\\mathbb R}}\n", "\\def\\CC{{\\cal C}}\n", "\\def\\HH{{\\cal H}}\n", "\\def\\I{{\\rm i}}\n", "\\def\\qqqquad{\\qquad\\qquad}\n", "\\def\\qqfor{\\qquad\\hbox{for}\\qquad}\n", "\\def\\qqwhere{\\qquad\\hbox{where}\\qquad}\n", "\\def\\Res_#1{\\underset{#1}{\\rm Res}}\\,\n", "\\def\\sech{{\\rm sech}\\,}\n", "\\def\\acos{\\,{\\rm acos}\\,}\n", "\\def\\vc#1{{\\mathbf #1}}\n", "\\def\\ip<#1,#2>{\\left\\langle#1,#2\\right\\rangle}\n", "\\def\\norm#1{\\left\\|#1\\right\\|}\n", "\\def\\half{{1 \\over 2}}\n", "$$\n", "\n", "Dr. Sheehan Olver\n", "
\n", "s.olver@imperial.ac.uk\n", "\n", "
\n", "Website: https://github.com/dlfivefifty/M3M6LectureNotes\n", "\n", "\n", "# Lecture 16: Solving differential equations with orthogonal polynomials\n", "\n", "\n", "This lecture we do the following:\n", "\n", "1. Recurrence relationships for Chebyshev and ultrashperical polynomials\n", " - Conversion\n", " - Three-term recurrence and Jacobi operators\n", "2. Application: solving differential equations\n", " - First order constant coefficients differential equations\n", " - Second order constant coefficient differential equations with boundary conditions\n", " - Non-constant coefficients\n", " \n", "\n", "\n", "That is, we introduce recurrences related to ultraspherical polynomials. This allows us to represent general linear differential equations as almost-banded systems." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Recurrence relationships for Chebyshev and ultraspherical polynomials\n", "\n", "\n", "We have discussed general properties, but now we want to discuss some classical orthogonal polynomials, beginning with Chebyshev (first kind) $T_n(x)$, which is orthogonal w.r.t.\n", "$$1\\over \\sqrt{1-x^2}$$\n", "and ultraspherical $C_n^{(\\lambda)}(x)$, which is orthogonal w.r.t.\n", "$$(1-x^2)^{\\lambda - \\half}$$\n", "for $\\lambda > 0$. Note that Chebyshev (second kind) satisfies $U_n(x) = C_n^{(1)}(x)$.\n", "\n", "For Chebyshev, recall we have the normalization constant (here we use a superscript $T_n(x) = k_n^{\\rm T} x^n + O(x^{n-1})$)\n", "$$\n", "k_0^{\\rm T} = 1, k_n^{\\rm T} = 2^{n-1}\n", "$$\n", "For Ultraspherical $C_n^{(\\lambda)}$, this is\n", "$$\n", "k_n^{(\\lambda)} = {2^n (\\lambda)_n \\over n!} = {2^n \\lambda (\\lambda+1) (\\lambda+2) \\cdots (\\lambda+n-1) \\over n!}\n", "$$\n", "where $(\\lambda)_n$ is the Pochhammer symbol. Note for $U_n(x) = C_n^{(1)}(x)$ this simplifies to $k_n^{\\rm U} = k_n^{(1)} = 2^n$.\n", "\n", "We have already found the recurrence for Chebyshev:\n", "$$\n", "x T_n(x) = {T_{n-1}(x) \\over 2} + {T_{n+1}(x) \\over 2}\n", "$$\n", "We will show that we can use this to find the recurrence for _all_ ultraspherical polynomials. But first we need some special recurrences.\n", "\n", "**Remark** Jacobi, Laguerre, and Hermite all have similar relationships, which will be discussed further in the problem sheet.\n", "\n", "### Derivatives\n", "\n", "It turns out that the derivative of $T_n(x)$ is precisely a multiple of $U_{n-1}(x)$, and similarly the derivative of $C_n^{(\\lambda)}$ is a multiple of $C_{n-1}^{(\\lambda+1)}$.\n", "\n", "**Proposition (Chebyshev derivative)** $$T_n'(x) = n U_{n-1}(x)$$\n", "\n", "**Proof** \n", "We first show that $T_n'(x)$ is othogonal w.r.t. $\\sqrt{1-x^2}$ to all polynomials of degree $m < n-1$, denoted $f_m$, using integration by parts:\n", "$$\n", "\\ip_{\\rm U} = \\int_{-1}^1 T_n'(x) f_m(x) \\sqrt{1-x^2} \\dx = -\\int_{-1}^1 T_n(x) (f_m'(x)(1-x^2) + xf_m) {1 \\over \\sqrt{1-x^2}} \\dx = - \\ip_{\\rm T} = 0\n", "$$\n", "since $f_m'(1-x^2) + f_m $ is degree $m-1 +2 = m+1 < n$.\n", "\n", "The constant works out since\n", "$$\n", "T_n'(x) = {\\D \\over \\dx} (2^{n-1} x^n) + O(x^{n-2}) = n 2^{n-1} x^{n-1} + O(x^{n-2})\n", "$$\n", "⬛️\n", "\n", "The exact same proof shows the following:\n", "\n", "**Proposition (Ultraspherical derivative)** \n", "$${\\D \\over \\dx} C_n^{(\\lambda)}(x) = 2 \\lambda C_{n-1}^{(\\lambda+1)}(x)$$\n", "\n", "Like the three-term recurrence and Jacobi operators, it is useful to express this in matrix form. That is, for the derivatives of $T_n(x)$ we get\n", "$$\n", "{\\D \\over \\dx} \\begin{pmatrix} T_0(x) \\\\ T_1(x) \\\\ T_2(x) \\\\ \\vdots \\end{pmatrix}= \\begin{pmatrix}\n", "0 \\cr\n", "1 \\cr \n", "& 2 \\cr\n", "&& 3 \\cr\n", "&&&\\ddots \n", "\\end{pmatrix} \\begin{pmatrix} U_0(x) \\\\ U_1(x) \\\\ U_2(x) \\\\ \\vdots \\end{pmatrix} \n", "$$\n", "which let's us know that, for \n", "$$\n", "f(x) = (T_0(x),T_1(x),\\ldots) \\begin{pmatrix} f_0\\\\f_1\\\\\\vdots \\end{pmatrix}\n", "$$\n", "we have a derivative operator in coefficient space as\n", "$$\n", "f'(x) = (U_0(x),U_1(x),\\ldots)\\begin{pmatrix}\n", "0 & 1 \\cr \n", "&& 2 \\cr\n", "&&& 3 \\cr\n", "&&&&\\ddots \n", "\\end{pmatrix} \\begin{pmatrix} f_0\\\\f_1\\\\\\vdots \\end{pmatrix}\n", "$$\n", "\n", "_Demonstration_ Here we see that applying a matrix to a vector of coefficients successfully calculates the derivative:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "31×32 Array{Float64,2}:\n", " 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\n", " 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\n", " 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\n", " 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\n", " 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\n", " 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\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " ⋮ ⋮ ⋱ ⋮ \n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " 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\n", " 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\n", " 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\n", " 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\n", " 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\n", " 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" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = Fun(x -> cos(x^2), Chebyshev()) # f is expanded in Chebyshev coefficients\n", "n = ncoefficients(f) # This is the number of coefficients\n", "D = zeros(n-1,n)\n", "for k=1:n-1\n", " D[k,k+1] = k\n", "end\n", "D" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here `D*f.coefficients` gives the vector of coefficients corresponding to the derivative, but now the coefficients are in the $U_n(x)$ basis, that is, `Ultraspherical(1)`:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-0.001999966666833569" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fp = Fun(Ultraspherical(1), D*f.coefficients)\n", "\n", "fp(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Indeed, it matches the \"true\" derivative:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-0.0019999666668335634" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f'(0.1)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-0.0019999666668333335" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "-2*0.1*sin(0.1^2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that in ApproxFun.jl we can construct these operators rather nicely:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-0.001999966666833569" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "D = Derivative()\n", "(D*f)(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we see that we can write produce the ∞-dimensional version as follows:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ConcreteDerivative : Chebyshev() → Ultraspherical(1)\n", " ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ 2.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 3.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 4.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 5.0 ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 6.0 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 7.0 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 8.0 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 9.0 ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "D : Chebyshev() → Ultraspherical(1) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conversion\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "**Proposition (Chebyshev T-to-U conversion)** \n", "\\begin{align*}\n", " T_0(x) &= U_0(x) \\\\\n", " T_1(x) &= {U_1(x) \\over 2} \\\\\n", " T_n(x) &= {U_n(x) \\over 2} - {U_{n-2}(x) \\over 2}\n", "\\end{align*}\n", "\n", "**Proof** \n", "\n", "Before we do the proof, note that the fact that there are limited non-zero entries follows immediately: if $m < n-2$ we have\n", "$$\n", "\\ip_{\\rm U} = \\ip_{\\rm T} = 0\n", "$$\n", "\n", "To actually determine the entries, we use the trigonometric formulae. Recall for $x = (z + z^{-1})/2$, $z = \\E^{\\I \\theta}$, we have\n", "\\begin{align*}\n", "T_n(x) &= \\cos n \\theta = {z^{-n} + z^n \\over 2}\\\\\n", "U_n(x) &= {\\sin (n+1) \\theta \\over \\sin \\theta} = {z^{n+1} - z^{-n-1} \\over z - z^{-1}} = z^{-n} + z^{2-n} + \\cdots + \\cdots + z^{n-2} + z^n \n", "\\end{align*}\n", "The result follows immediately.\n", "\n", "⬛️\n", "\n", "**Corollary (Ultrapherical λ-to-(λ+1) conversion)**\n", "$$\n", "C_n^{(\\lambda)}(x) = {\\lambda \\over n+ \\lambda} (C_n^{(\\lambda+1)}(x) - C_{n-2}^{(\\lambda+1)}(x))\n", "$$\n", "\n", "**Proof** This follows from differentiating the previous result. For example:\n", "\\begin{align*}\n", " {\\D\\over \\dx} T_0(x) &= {\\D\\over \\dx} U_0(x) \\\\\n", " {\\D\\over \\dx} T_1(x) &= {\\D\\over \\dx} {U_1(x) \\over 2} \\\\\n", "{\\D\\over \\dx} T_n(x) &= {\\D\\over \\dx} \\left({U_n(x) \\over 2} - {U_{n-2} \\over 2} \\right)\n", "\\end{align*}\n", "becomes\n", "\\begin{align*}\n", " 0 &= 0\\\\\n", " U_0(x) &= C_1^{(2)}(x) \\\\\n", " n U_{n-1}(x) &= C_{n-1}^{(2)}(x) - C_{n-3}^{(2)}(x)\n", "\\end{align*}\n", "\n", "Differentiating this repeatedly completes the proof.\n", "\n", "⬛️\n", "\n", "\n", "Note we can write this in matrix form, for example, we have\n", "$$\n", "\\underbrace{\\begin{pmatrix}1 \\cr\n", " 0 & \\half\\cr\n", " -\\half & 0 & \\half \\cr\n", " &\\ddots &\\ddots & \\ddots\\end{pmatrix} }_{S_0^\\top} \\begin{pmatrix} \n", " U_0(x) \\\\ U_1(x) \\\\ U_2(x) \\\\ \\vdots \\end{pmatrix} = \\begin{pmatrix} T_0(x) \\\\ T_1(x) \\\\ T_2(x) \\\\ \\vdots \\end{pmatrix}\n", "$$\n", "\n", "therefore,\n", "$$\n", "f(x) = (T_0(x),T_1(x),\\ldots) \\begin{pmatrix} f_0\\\\f_1\\\\\\vdots \\end{pmatrix} = (U_0(x),U_1(x),\\ldots) S_0 \\begin{pmatrix} f_0\\\\f_1\\\\\\vdots \\end{pmatrix}\n", "$$\n", "\n", "Again, we can construct this nicely in ApproxFun:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ConcreteConversion : Chebyshev() → Ultraspherical(1)\n", " 1.0 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 -0.5 ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S₀ = I : Chebyshev() → Ultraspherical(1)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "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])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = Fun(exp, Chebyshev())\n", "g = S₀*f" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(0.1) - exp(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ultraspherical Three-term recurrence \n", "\n", "**Theorem (three-term recurrence for Chebyshev U)** \n", "\\begin{align*}\n", "x U_0(x) &= {U_1(x) \\over 2} \\\\\n", "x U_n(x) &= {U_{n-1}(x) \\over 2} + {U_{n+1}(x) \\over 2}\n", "\\end{align*}\n", "\n", "**Proof**\n", "Differentiating\n", "\\begin{align*}\n", " x T_0(x) &= T_1(x) \\\\\n", "x T_n(x) &= {T_{n-1}(x) \\over 2} + {T_{n+1}(x) \\over 2}\n", "\\end{align*}\n", "we get\n", "\\begin{align*}\n", " T_0(x) &= U_0(x) \\\\\n", " T_n(x) + n x U_{n-1}(x) &= {(n-1) U_{n-2}(x) \\over 2} + {(n+1) U_n(x) \\over 2}\n", "\\end{align*}\n", "substituting in the conversion $T_n(x) = (U_n(x) - U_{n-2}(x))/2$ we get\n", "\\begin{align*}\n", " T_0(x) &= U_0(x) \\\\\n", " n x U_{n-1}(x) &= {(n-1) U_{n-2}(x) \\over 2} + {(n+1) U_n(x) \\over 2} - (U_n(x) - U_{n-2}(x))/2 = {n U_{n-2}(x) \\over 2} + {n U_n(x) \\over 2}\n", "\\end{align*}\n", "\n", "⬛️\n", "\n", "Differentiating this theorem again and applying the conversion we get the following\n", "\n", "**Corollary (three-term recurrence for ultrashperical)** \n", "\\begin{align*}\n", "x C_0^{(\\lambda)}(x) &= {1 \\over 2\\lambda } C_1^{(\\lambda)}(x) \\\\\n", " x C_n^{(\\lambda)}(x) &= {n+2\\lambda-1 \\over 2(n+\\lambda)} C_{n-1}^{(\\lambda)}(x) + {n+1 \\over 2(n+\\lambda)} C_{n+1}^{(\\lambda)}(x) \n", "\\end{align*}\n", "\n", "\n", "Here's an example of the Jacobi operator (which is the transpose of the multiplciation by $x$ operator):" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "TransposeOperator : Ultraspherical(2) → Ultraspherical(2)\n", " 0.0 0.25 … ⋅ ⋅ ⋅ ⋅\n", " 0.6666666666666666 0.0 ⋅ ⋅ ⋅ ⋅\n", " ⋅ 0.625 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ … ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ 0.4375 ⋅ ⋅ ⋅\n", " ⋅ ⋅ 0.0 0.4444444444444444 ⋅ ⋅\n", " ⋅ ⋅ 0.55 0.0 0.45 ⋅\n", " ⋅ ⋅ ⋅ 0.5454545454545454 0.0 ⋱\n", " ⋅ ⋅ … ⋅ ⋅ ⋱ ⋱" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Multiplication(Fun(), Ultraspherical(2))'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Application: solving differential equations\n", "\n", "The preceding results allowed us to represent \n", "\n", "1. Differentiation\n", "2. Conversion\n", "3. Multiplication\n", "\n", "as banded operators. We will see that we can combine these, along with \n", "\n", "4\\. Evaluation\n", "\n", "to solve ordinary differential equations.\n", "\n", "### First order, constant coefficient differential equations\n", "\n", "Consider the simplest ODE:\n", "\\begin{align*}\n", "u(0) &= 0 \\\\\n", "u'(x) - u(x) &= 0 \n", "\\end{align*}\n", "and suppose represent $u(x)$ in its Chebyshev expansion, with to be determined coefficents. In other words, we want to calculate coefficients $u_k$ such that\n", "$$\n", "u(x) = \\sum_{k=0}^\\infty u_k T_k(x) = (T_0(x), T_1(x), \\ldots) \\begin{pmatrix} u_0 \\\\ u_1 \\\\ \\vdots \\end{pmatrix}\n", "$$\n", "In this case we know that $u(x) = \\E^x$, but we would still need other means to calculate $u_k$ (They are definitely not as simple as Taylor series coefficients).\n", "\n", "We can express the constraints as acting on the coefficients. For example, we have\n", "$$\n", "u(0) = (T_0(0), T_1(0), \\ldots) \\begin{pmatrix} u_0\\\\u_1\\\\\\vdots \\end{pmatrix} = (1,0,-1,0,1,\\ldots) \\begin{pmatrix} u_0\\\\u_1\\\\\\vdots \\end{pmatrix} \n", "$$\n", "We also have \n", "$$u'(x) = (U_0(x),U_1(x),\\ldots) \\begin{pmatrix}\n", "0 & 1 \\cr \n", "&& 2 \\cr\n", "&&& 3 \\cr\n", "&&&&\\ddots \n", "\\end{pmatrix}\\begin{pmatrix} u_0\\\\u_1\\\\\\vdots \\end{pmatrix} \n", "$$\n", "To represent $u'(x) - u(x)$, we need to make sure the bases are compatible. In other words, we want to express $u(x)$ in it's $U_k(x)$ basis using the conversion operator $S_0$:\n", "$$u(x) = (U_0(x),U_1(x),\\ldots) \\begin{pmatrix}\n", " 1 &0 & -\\half \\cr \n", "& \\half & 0 & -\\half \\cr\n", "&&\\ddots & \\ddots & \\ddots\n", "\\end{pmatrix}\\begin{pmatrix} u_0\\\\u_1\\\\\\vdots \\end{pmatrix} \n", "$$\n", "\n", "Which gives us, \n", "$$\n", "u'(x) - u(x) = (U_0(x),U_1(x),\\ldots) \\begin{pmatrix}\n", " -1 &1 & \\half \\cr \n", "& -\\half & 2 & \\half \\cr\n", "&& -\\half & 3 & \\half \\cr\n", "&&&\\ddots & \\ddots & \\ddots\n", "\\end{pmatrix} \\begin{pmatrix} u_0\\\\u_1\\\\\\vdots \\end{pmatrix} \n", "$$\n", "\n", "\n", "Combing the differential part and the evaluation part, we arrive at an (infinite) system of equations for the coefficients $u_0,u_1,\\dots$:\n", "$$\n", "\\begin{pmatrix}\n", " 1 & 0 & -1 & 0 & 1 & \\cdots \\\\\n", " -1 &1 & \\half \\cr \n", "& -\\half & 2 & \\half \\cr\n", "&& -\\half & 3 & \\half \\cr\n", "&&&\\ddots & \\ddots & \\ddots\n", "\\end{pmatrix} \\begin{pmatrix} u_0\\\\u_1\\\\\\vdots \\end{pmatrix} = \\begin{pmatrix} 1 \\\\ 0 \\\\ 0 \\\\ \\vdots \\end{pmatrix}\n", "$$\n", "\n", "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:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "InterlaceOperator : Chebyshev() → 2-element ArraySpace:\n", "Space{D,Float64} where D[ConstantSpace(Point(0.0)), Ultraspherical(1)]\n", " 1.0 0.0 -1.0 0.0 1.0 0.0 -1.0 0.0 1.0 0.0 ⋯\n", " -1.0 1.0 0.5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ⋱\n", " 0.0 -0.5 2.0 0.5 0.0 0.0 0.0 0.0 0.0 0.0 ⋱\n", " 0.0 0.0 -0.5 3.0 0.5 0.0 0.0 0.0 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 -0.5 4.0 0.5 0.0 0.0 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 -0.5 5.0 0.5 0.0 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 0.0 -0.5 6.0 0.5 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 0.0 0.0 -0.5 7.0 0.5 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.5 8.0 0.5 ⋱\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.5 9.0 ⋱\n", " ⋮ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱ ⋱" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = Evaluation(0.0) : Chebyshev()\n", "D = Derivative() : Chebyshev() → Ultraspherical(1)\n", "S₀ = I : Chebyshev() → Ultraspherical(1)\n", "L = [B; \n", " D - S₀]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can solve this system as follows:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "1.5\n", "\n", "\n", "2.0\n", "\n", "\n", "2.5\n", "\n", "\n", "\n", "\n", "\n", "\n", "y1\n", "\n", "\n" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "u = L \\ [1; 0]\n", "plot(u)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It matches the \"true\" result:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-4.440892098500626e-16" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "u(0.1) - exp(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "### Second-order constanst coefficient equations\n", "\n", "This approach extends to second-order constant-coefficient equations by using ultraspherical polynomials. Consider\n", "\\begin{align*}\n", "u(-1) &= 1\\\\\n", "u(1) &= 0\\\\\n", "u''(x) + u'(x) + u(x) &= 0\n", "\\end{align*}\n", "Evaluation works as in the first-order case. To handle second-derivatives, we need $C^{(2)}$ polynomials:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ConcreteDerivative : Chebyshev() → Ultraspherical(2)\n", " ⋅ ⋅ 4.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 6.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 8.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 10.0 ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 12.0 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 14.0 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 16.0 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 18.0 ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "D₀ = Derivative() : Chebyshev() → Ultraspherical(1)\n", "D₁ = Derivative() : Ultraspherical(1) → Ultraspherical(2)\n", "D₁*D₀ # 2 zeros not printed in (1,1) and (1,2) entry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the identity operator, we use two conversion operators:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ConversionWrapper : Chebyshev() → Ultraspherical(2)\n", " 1.0 0.0 -0.6666666666666666 0.0 … ⋅ ⋅ ⋅\n", " ⋅ 0.25 0.0 -0.375 ⋅ ⋅ ⋅\n", " ⋅ ⋅ 0.16666666666666666 0.0 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 0.125 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 0.07142857142857142 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ … 0.0 0.0625 ⋅\n", " ⋅ ⋅ ⋅ ⋅ -0.12698412698412698 0.0 ⋱\n", " ⋅ ⋅ ⋅ ⋅ 0.0 -0.1125 ⋱\n", " ⋅ ⋅ ⋅ ⋅ 0.05555555555555555 0.0 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 0.05 ⋱\n", " ⋅ ⋅ ⋅ ⋅ … ⋅ ⋅ ⋱" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S₀ = I : Chebyshev() → Ultraspherical(1)\n", "S₁ = I : Ultraspherical(1) → Ultraspherical(2)\n", "S₁*S₀" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And for the first derivative, we use a derivative and then a conversion:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "TimesOperator : Chebyshev() → Ultraspherical(2)\n", " ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 -1.0 ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 0.0 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S₁*D₀ # or could have been D₁*S₀" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Putting everything together we get:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "InterlaceOperator : Chebyshev() → 3-element ArraySpace:\n", "Space{D,Float64} where D[ConstantSpace(Point(-1)), ConstantSpace(Point(1)), Ultraspherical(2)]\n", " 1.0 -1.0 1.0 -1.0 … 1.0 -1.0 ⋯\n", " 1.0 1.0 1.0 1.0 1.0 1.0 ⋱\n", " 1.0 1.0 3.3333333333333335 -1.0 0.0 0.0 ⋱\n", " 0.0 0.25 1.0 5.625 0.0 0.0 ⋱\n", " 0.0 0.0 0.16666666666666666 1.0 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 0.125 … 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 0.07142857142857142 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 -1.0 0.0625 ⋱\n", " 0.0 0.0 0.0 0.0 15.873015873015873 -1.0 ⋱\n", " 0.0 0.0 0.0 0.0 1.0 17.8875 ⋱\n", " ⋮ ⋱ ⋱ ⋱ … ⋱ ⋱ ⋱" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B₋₁ = Evaluation(-1) : Chebyshev()\n", "B₁ = Evaluation(1) : Chebyshev()\n", "# u(-1) \n", "# u(1)\n", "# u'' + u' +u\n", "\n", "L = [B₋₁; \n", " B₁; \n", " D₁*D₀ + S₁*D₀ + S₁*S₀]" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "0.00\n", "\n", "\n", "0.25\n", "\n", "\n", "0.50\n", "\n", "\n", "0.75\n", "\n", "\n", "1.00\n", "\n", "\n", "\n", "\n", "\n", "\n", "y1\n", "\n", "\n" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "u = L \\ [1.0,0.0,0.0]\n", "plot(u)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Variable coefficients\n", "\n", "Consider the Airy ODE \n", "\\begin{align*}\n", "u(-1) &= 1\\\\\n", "u(1) &= 0\\\\\n", "u''(x) - xu(x) &= 0\n", "\\end{align*}\n", "\n", "to handle, this, we need only use the Jacobi operator to represent multiplication by $x$:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ConcreteMultiplication : Chebyshev() → Chebyshev()\n", " 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " 1.0 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 0.5 ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.5 0.0 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋱" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = Fun()\n", "Jᵗ = Multiplication(x) : Chebyshev() → Chebyshev() # transpose of the Jacobi operator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We set op ther system as follows:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "InterlaceOperator : Chebyshev() → 3-element ArraySpace:\n", "Space{D,Float64} where D[ConstantSpace(Point(-1)), ConstantSpace(Point(1)), Ultraspherical(2)]\n", " 1.0 -1.0 1.0 -1.0 … -1.0 ⋯\n", " 1.0 1.0 1.0 1.0 1.0 ⋱\n", " 0.0 -0.16666666666666669 4.0 0.25 0.0 ⋱\n", " -0.25 0.0 0.0625 6.0 0.0 ⋱\n", " 0.0 -0.08333333333333333 0.0 0.05 0.0 ⋱\n", " 0.0 0.0 -0.0625 0.0 … 0.0 ⋱\n", " 0.0 0.0 0.0 -0.05 -0.03571428571428571 ⋱\n", " 0.0 0.0 0.0 0.0 0.0 ⋱\n", " 0.0 0.0 0.0 0.0 0.03571428571428571 ⋱\n", " 0.0 0.0 0.0 0.0 18.0 ⋱\n", " ⋮ ⋱ ⋱ ⋱ … ⋱ ⋱" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "L = [B₋₁; # u(-1)\n", " B₁ ; # u(1)\n", " D₁*D₀ - S₁*S₀*Jᵗ] # u'' - x*u" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "0.00\n", "\n", "\n", "0.25\n", "\n", "\n", "0.50\n", "\n", "\n", "0.75\n", "\n", "\n", "1.00\n", "\n", "\n", "\n" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "u = L \\ [1.0;0.0;0.0]\n", "plot(u; legend=false)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we introduce a small parameter, that is, solve\n", "\\begin{align*}\n", "u(-1) &= 1\\\\\n", "u(1) &= 0\\\\\n", "\\epsilon u''(x) - xu(x) &= 0\n", "\\end{align*}\n", "we can see pretty hard to compute solutions:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "-2\n", "\n", "\n", "-1\n", "\n", "\n", "0\n", "\n", "\n", "1\n", "\n", "\n", "2\n", "\n", "\n", "3\n", "\n", "\n", "\n" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ε = 1E-6\n", "L = [B₋₁; \n", " B₁ ; \n", " ε*D₁*D₀ - S₁*S₀*Jᵗ]\n", "\n", "u = L \\ [1.0;0.0;0.0]\n", "plot(u; legend=false)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because of the banded structure, this can be solved fast:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.466365 seconds (13.06 M allocations: 296.400 MiB, 19.59% gc time)\n", "ncoefficients(u) = 62496\n" ] } ], "source": [ "ε = 1E-10\n", "L = [B₋₁; \n", " B₁ ; \n", " ε*D₁*D₀ - S₁*S₀*Jᵗ]\n", "\n", "\n", "@time u = L \\ [1.0;0.0;0.0]\n", "@show ncoefficients(u);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To handle other variable coefficients, first consider a polynomial $p(x)$. If Multiplication by $x$ is represented by multiplying the coefficients by $J^\\top$, then multiplication by $p$ is represented by $p(J^\\top)$:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "PlusOperator : Chebyshev() → Chebyshev()\n", " -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " 1.0 -0.25 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " 0.5 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅ ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 0.25 ⋅\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 0.5 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.25 0.5 -0.5 ⋱\n", " ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋱ ⋱ ⋱" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = -I + Jᵗ + (Jᵗ)^2 # represents -1+x+x^2" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.023297 seconds (295.98 k allocations: 7.463 MiB)\n", "ε * ((u')')(0.1) - (-1 + 0.1 + 0.1 ^ 2) * u(0.1) = -1.4863110742169283e-14\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "-2\n", "\n", "\n", "-1\n", "\n", "\n", "0\n", "\n", "\n", "1\n", "\n", "\n", "2\n", "\n", "\n", "\n", "\n", "\n", "\n", "y1\n", "\n", "\n" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ε = 1E-6\n", "L = [B₋₁; \n", " B₁ ; \n", " ε*D₁*D₀ - S₁*S₀*M]\n", "\n", "@time u = L \\ [1.0;0.0;0.0]\n", "\n", "@show ε*u''(0.1) - (-1+0.1+0.1^2)*u(0.1)\n", "plot(u)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For other smooth functions, we first approximate in a polynomial basis, and without loss of generality we use Chebyshev T basis. For example, consider \n", "\\begin{align*}\n", "u(-1) &= 1\\\\\n", "u(1) &= 0\\\\\n", "\\epsilon u''(x) - \\E^x u(x) &= 0\n", "\\end{align*}\n", "where\n", "$$\n", "\\E^x \\approx p(x) = \\sum_{k=0}^{m-1} p_k T_k(x)\n", "$$\n", "Evaluating at a point $x$, recall Clenshaw's algorithm:\n", "\\begin{align*}\n", "\\gamma_{n-1} &= 2p_{n-1} \\\\\n", "\\gamma_{n-2} &= 2p_{n-2} + 2x \\gamma_{n-1} \\\\\n", "\\gamma_{n-3} &= 2 p_{n-3} + 2x \\gamma_{n-2} - \\gamma_{n-1} \\\\\n", "& \\vdots \\\\\n", "\\gamma_1 &= p_1 + x \\gamma_2 - \\half \\gamma_3 \\\\\n", "p(x) = \\gamma_0 &= p_0 + x \\gamma_1 - \\half \\gamma_2\n", "\\end{align*}\n", "If multiplication by $x$ becomes $J^\\top$, then multiplication by $p(x)$ becomes $p(J^\\top)$, and hence we calculate:\\\n", "\\begin{align*}\n", "\\Gamma_{n-1} &= 2p_{n-1}I \\\\\n", "\\Gamma_{n-2} &= 2p_{n-2}I + 2J^\\top \\Gamma_{n-1} \\\\\n", "\\Gamma_{n-3} &= 2 p_{n-3}I + 2J^\\top \\Gamma_{n-2} - \\Gamma_{n-1} \\\\\n", "& \\vdots \\\\\n", "\\Gamma_1 &= p_1I + J^\\top \\Gamma_2 - \\half \\Gamma_3 \\\\\n", "p(J^\\top) = \\Gamma_0 &= p_0 + x \\Gamma_1 - \\half \\Gamma_2\n", "\\end{align*}\n", "\n", "Here is an example:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ConcreteMultiplication : Chebyshev() → Chebyshev()\n", " 1.2660658777520084 0.5651591039924851 … 5.518385951076946e-9 ⋱\n", " 1.1303182079849703 1.4018135475190467 9.988153506976674e-8 ⋱\n", " 0.27149533953407656 0.587327528916817 1.59923072107804e-6 ⋱\n", " 0.044336849848663804 0.1384847899880851 2.248866199669348e-5 ⋱\n", " 0.0054742404420936785 0.022439888080288854 0.00027146315597690023 ⋱\n", " 0.0005429263119139036 0.0027596088825239777 … 0.0027371202210468393 ⋱\n", " 4.497732295427654e-5 0.00027306237418820746 0.022168424924331902 ⋱\n", " 3.198436462511398e-6 2.2588267717379123e-5 0.13574766976703828 ⋱\n", " 1.992124804817033e-7 1.6047366172067758e-6 0.5651591039924851 ⋱\n", " 1.1036771902153892e-8 9.988153506976674e-8 1.2660658777520084 ⋱\n", " ⋱ ⋱ … ⋱ ⋱" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = Fun(exp, Chebyshev()) # polynomial approximation to exp(x)\n", "M = Multiplication(p) : Chebyshev() # constructed using Clenshaw:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(13, 13)" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ApproxFun.bandwidths(M) # still banded" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.039351 seconds (1.04 M allocations: 21.628 MiB, 28.97% gc time)\n", "ε * ((u')')(0.1) + exp(0.1) * u(0.1) = 4.884981308350689e-15\n" ] }, { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "-1.0\n", "\n", "\n", "-0.5\n", "\n", "\n", "0.0\n", "\n", "\n", "0.5\n", "\n", "\n", "1.0\n", "\n", "\n", "\n", "\n", "\n", "\n", "y1\n", "\n", "\n" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ε = 1E-6\n", "L = [B₋₁; \n", " B₁ ; \n", " ε*D₁*D₀ + S₁*S₀*M]\n", "\n", "@time u = L \\ [1.0;0.0;0.0]\n", "\n", "@show ε*u''(0.1) + exp(0.1)*u(0.1)\n", "plot(u)" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.0.2", "language": "julia", "name": "julia-1.0" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.0.2" } }, "nbformat": 4, "nbformat_minor": 2 }