|
1 | 1 | #############
|
2 |
| -# This demonstrates the Triangle harmonic transform and inverse transform, |
3 |
| -# explaining precisely the normalization and points |
| 2 | +# In this example, we sample a bivariate function: |
4 | 3 | #
|
5 |
| -# Note we use the duffy map |
6 |
| -# x == (s+1)/2 |
7 |
| -# y== (t+1)/2*(1-(s+1)/2) |
| 4 | +# f(x,y) = 1/(1+x^2+y^2), |
| 5 | +# |
| 6 | +# on the reference triangle with vertices (0,0), (0,1), and (1,0) and analyze it |
| 7 | +# in a Proriol series. Then, we find Proriol series for each component of its |
| 8 | +# gradient by term-by-term differentiation of our expansion, and we compare them |
| 9 | +# with the true Proriol series by sampling an exact expression for the gradient. |
| 10 | +# |
| 11 | +# We analyze f(x,y) on an N×M mapped tensor product grid defined by: |
| 12 | +# |
| 13 | +# x = (1+u)/2, and y = (1-u)*(1+v)/4, where: |
| 14 | +# |
| 15 | +# uₙ = cos[(n+1/2)π/N], for 0 ≤ n < N, and |
| 16 | +# |
| 17 | +# vₘ = cos[(m+1/2)π/M], for 0 ≤ m < M; |
| 18 | +# |
| 19 | +# we convert the function samples to mapped Chebyshev² coefficients using |
| 20 | +# `plan_tri_analysis`; and finally, we transform the mapped Chebyshev² |
| 21 | +# coefficients to Proriol coefficients using `plan_tri2cheb`. |
| 22 | +# |
| 23 | +# For the storage pattern of the arrays, please consult the documentation. |
8 | 24 | #############
|
9 | 25 |
|
| 26 | +using FastTransforms, LinearAlgebra |
10 | 27 |
|
11 |
| -using ApproxFun, FastTransforms |
| 28 | +f = (x,y) -> 1/(1+x^2+y^2) |
| 29 | +fx = (x,y) -> -2x/(1+x^2+y^2)^2 |
| 30 | +fy = (x,y) -> -2y/(1+x^2+y^2)^2 |
12 | 31 |
|
13 |
| -jacobinorm(n,a,b) = if n ≠ 0 |
14 |
| - sqrt((2n+a+b+1))*exp((lgamma(n+a+b+1)+lgamma(n+1)-log(2)*(a+b+1)-lgamma(n+a+1)-lgamma(n+b+1))/2) |
15 |
| - else |
16 |
| - sqrt(exp(lgamma(a+b+2)-log(2)*(a+b+1)-lgamma(a+1)-lgamma(b+1))) |
17 |
| - end |
18 |
| -njacobip(n,a,b,x) = jacobinorm(n,a,b) * jacobip(n,a,b,x) |
| 32 | +N = 10 |
| 33 | +M = N |
| 34 | + |
| 35 | +α, β, γ = 0, 0, 0 |
19 | 36 |
|
20 |
| -P = (ℓ,m,x,y) -> (2*(1-x))^m*njacobip(ℓ-m,2m,0,2x-1)*njacobip(m,-0.5,-0.5,2y/(1-x)-1) |
| 37 | +P = plan_tri2cheb(Float64, N, α, β, γ) |
| 38 | +Px = plan_tri2cheb(Float64, N, α+1, β, γ+1) |
| 39 | +Py = plan_tri2cheb(Float64, N, α, β+1, γ+1) |
| 40 | +PA = plan_tri_analysis(Float64, N, M) |
21 | 41 |
|
| 42 | +u = [sinpi((N-2n-1)/(2N)) for n in 0:N-1] |
| 43 | +v = [sinpi((M-2m-1)/(2M)) for m in 0:M-1] |
22 | 44 |
|
| 45 | +# Instead of using the u, v grid, we use one with more accuracy near the origin. |
| 46 | +x = [sinpi((2N-2n-1)/(4N))^2 for n in 0:N-1] |
| 47 | +w = [sinpi((2M-2m-1)/(4M))^2 for m in 0:M-1] |
23 | 48 |
|
24 |
| -p_T = chebyshevpoints(40) |
25 |
| -f = (x,y) -> exp(x + cos(y)) |
26 |
| - f̃ = (s,t) -> f((s+1)/2, (t+1)/2*(1-(s+1)/2)) |
| 49 | +(1 .+ u)./2 ≈ x |
| 50 | +(1 .- u).*(1 .+ v')/4 ≈ reverse(x).*w' |
27 | 51 |
|
28 |
| - F = f̃.(p_T, p_T') |
29 |
| - for j = 1:size(F,2) |
30 |
| - F[:,j] = chebyshevtransform(F[:,j]) |
31 |
| - end |
32 |
| - for k = 1:size(F,1) |
33 |
| - F[k,:] = chebyshevtransform(F[k,:]) |
34 |
| - end |
| 52 | +# On the mapped tensor product grid, our function samples are: |
| 53 | +F = [f(x[n+1], x[N-n]*w[m+1]) for n in 0:N-1, m in 0:M-1] |
35 | 54 |
|
36 |
| - F̌ = cheb2tri(F, 0.0, -0.5, -0.5) |
| 55 | +# Its Proriol-(α,β,γ) coefficients are: |
| 56 | +U = P\(PA*F) |
37 | 57 |
|
| 58 | +# Similarly, our function's gradient samples are: |
| 59 | +Fx = [fx(x[n+1], x[N-n]*w[m+1]) for n in 0:N-1, m in 0:M-1] |
| 60 | +Fy = [fy(x[n+1], x[N-n]*w[m+1]) for n in 0:N-1, m in 0:M-1] |
38 | 61 |
|
39 |
| -f̃ = function(x,y) |
40 |
| - ret = 0.0 |
41 |
| - for j=1:size(F,2), k=1:size(F,1)-j+1 |
42 |
| - ret += F̌[k,j] * P(k+j-2,j-1,x,y) |
43 |
| - end |
44 |
| - ret |
| 62 | +# For the partial derivative with respect to x, Olver et al. |
| 63 | +# derive simple expressions for the representation of this component |
| 64 | +# using a Proriol-(α+1,β,γ+1) series. For the partial derivative with respect |
| 65 | +# to y, the analogous formulae result in a Proriol-(α,β+1,γ+1) series. |
| 66 | +# These expressions are adapted from https://arxiv.org/abs/1902.04863. |
| 67 | +Gx = zeros(Float64, N, M) |
| 68 | +for m = 0:M-2 |
| 69 | + for n = 0:N-2 |
| 70 | + cf1 = m == 0 ? sqrt((n+1)*(n+2m+α+β+γ+3)/(2m+β+γ+2)*(m+γ+1)*8) : sqrt((n+1)*(n+2m+α+β+γ+3)/(2m+β+γ+1)*(m+β+γ+1)/(2m+β+γ+2)*(m+γ+1)*8) |
| 71 | + cf2 = sqrt((n+α+1)*(m+1)/(2m+β+γ+2)*(m+β+1)/(2m+β+γ+3)*(n+2m+β+γ+3)*8) |
| 72 | + Gx[n+1, m+1] = cf1*U[n+2, m+1] + cf2*U[n+1, m+2] |
45 | 73 | end
|
| 74 | +end |
| 75 | +Ux = Px\(PA*Fx) |
| 76 | + |
| 77 | +Gy = zeros(Float64, N, M) |
| 78 | +for m = 0:M-2 |
| 79 | + for n = 0:N-2 |
| 80 | + Gy[n+1, m+1] = 4*sqrt((m+1)*(m+β+γ+2))*U[n+1, m+2] |
| 81 | + end |
| 82 | +end |
| 83 | +Uy = Py\(PA*Fy) |
| 84 | + |
| 85 | +# The 2-norm relative error in differentiating the Proriol series |
| 86 | +# for f(x,y) term-by-term and its sampled gradient is: |
| 87 | +hypot(norm(Ux-Gx), norm(Uy-Gy))/hypot(norm(Ux), norm(Uy)) |
46 | 88 |
|
47 |
| -f̃(0.1,0.2) ≈ f(0.1,0.2) |
| 89 | +# This error can be improved upon by increasing N and M. |
0 commit comments